Array.prototype.push
方法的返回值是一个Number
:
15.4.4.7 Array.prototype.push([item1 [,item2 [,…]]])
参数按照它们出现的顺序附加到数组的末尾。 调用的结果是将新长度作为返回值。
返回数组新长度而不是返回一些更有用的内容(例如新追加项的引用或已变异的数组本身)的设计决策是什么? 为什么要这样做,是否有关于这些决策是如何形成的历史记录?
Array.prototype.push
方法的返回值是一个Number
:
15.4.4.7 Array.prototype.push([item1 [,item2 [,…]]])
参数按照它们出现的顺序附加到数组的末尾。 调用的结果是将新长度作为返回值。
返回数组新长度而不是返回一些更有用的内容(例如新追加项的引用或已变异的数组本身)的设计决策是什么? 为什么要这样做,是否有关于这些决策是如何形成的历史记录?
我在TC39的交流中心发表了这篇文章,并且能够了解到更多关于它背后历史的知识:
push
,pop
,shift
,unshift
were originally added to JS1.2 (Netscape 4) in 1997.There were modeled after the similarly named functions in Perl.
JS1.2 push followed the Perl 4 convention of returning the last item pushed. In JS1.3 (Netscape 4.06 summer 1998) changed push to follow the Perl 5 conventions of returning the new length of the array.
/* * If JS1.2, follow Perl4 by returning the last thing pushed. Otherwise, * return the new array length. */
我理解使用array.push()
期望返回已改变的数组而不是其新长度,以及出于链式调用的原因希望使用这种语法。
然而,有一种内置的方法可以实现这一点:array.concat()
。
请注意,concat
期望被给予一个数组而不是一个项。因此,如果它们不在一个数组中,请记得用 []
包装您想要添加的项。
newArray = oldArray.concat([newItem]);
通过使用.concat()
来完成数组链接,因为它返回一个数组,
但不能使用.push()
来完成,因为它返回一个整数(数组的新长度)。
以下是在React
中基于其先前值更改state
变量的常见模式:
// the property value we are changing
selectedBook.shelf = newShelf;
this.setState((prevState) => (
{books: prevState.books
.filter((book) => (book.id !== selectedBook.id))
.concat(selectedBook)
}
));
state
对象有一个 books
属性,它保存着一个 book
数组。
book
是一个对象,拥有 id
和 shelf
属性(以及其他属性)。
setState()
接受一个对象,该对象保存了要分配给 state
的新值。
selectedBook
已经在 books
数组中,但是需要更改它的 shelf
属性。
然而,我们只能给 setState
一个顶级对象,无法告诉它去寻找这本书,并且查找该书的某个属性,将其赋予新值。
因此,我们先取出原来的 books
数组。
使用 filter
方法删除旧版本的 selectedBook
。
然后使用 concat
方法将更新后的 selectedBook
添加回数组中。
尽管使用 push
链式操作也能很好地完成目标,但用 concat
是正确的方法。
总结:
array.push()
返回一个数字(数组新长度)。array.concat([])
返回一个新数组。实际上,它返回一个新的数组,并将修改后的元素添加到末尾,而不改变初始数组。 返回新的数组实例,而不是重复使用现有数组实例,这是很重要的区别,它使其在 React 应用程序中的状态对象中非常有用,以获得更改的数据并重新渲染。
oldArray.concat[newItem];
对我来说都会返回未定义。在https://2ality.com/2016/11/concat-array-literal.html上有一个有趣的相关讨论,但没有解释你的语法应该如何工作。我猜这是React特有的东西?一些魔法干扰了数组原型吗? - TaooldArray.concat(newItem)
。 - Nick ManningnewArray = oldArray.concat[newItem];
更改为正确的newArray = oldArray.concat([newItem]);
,从而回答了我的问题。然而,他们在底部的摘要中仍然留下了错误的语法,但我现在也已经纠正了。我的困惑完全得到了解决。 - Tao我无法解释为什么他们选择返回新长度,但是针对您的建议:
鉴于JavaScript使用C风格赋值,它会发出分配的值(而不是Basic风格赋值不会),因此您仍然可以具有该行为:
var addedItem;
myArray.push( addedItem = someExpression() );
Array.prototype.push2 = function(x) {
this.push(x);
return this;
};
myArray.push2( foo ).push2( bar ).push2( baz );
或者:
Array.prototype.push3 = function(x) {
this.push(x);
return x;
};
var foo = myArray.push3( computeFoo() );
既然你问了,我做了一个样本数组并在Chrome中进行了检查。
var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
console.log(arr);
由于我已经有了对数组的引用以及我推入其中的每个对象,只有另一个属性可能有用……长度。通过返回这个数组数据结构的一个附加值,我现在可以访问所有相关信息。这似乎是最好的设计选择。或者如果您想为了节省1个机器指令而争论的话,就什么都不返回。
为什么要这样做,是否有关于这些决策如何被做出的历史记录?
不清楚-我不确定是否存在这样一种基于这些理由的记录。这取决于实现者,并且很可能在实现ECMA脚本标准的任何给定代码库中进行了注释。
const a = [1,2,3]
const b = (a.push(4), a).reverse()
console.log(a)
console.log(b)
const a = [1,2,3]
const b = [...a, 4].reverse()
console.log(a)
console.log(b)
reverse()
会就地反转数组。这就是为什么在第一个示例中,a
和b
变量都引用相同的反转数组。在第二个示例中,a
和b
引用不同的数组,只有其中一个被反转。你提到的文档(Ecma 262第3版)部分回答了这个问题,有一些方法会改变数组,而有一些则不会。那些改变数组的方法将返回改变后的数组长度。如果要添加元素,可以使用push、splice和unshift(取决于您想要新元素的位置)。
如果您想获取新的改变后的数组,可以使用concat。Concat将输入任意数量的数组,并将所有元素添加到一个新数组中。例如:
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3=['g','h'];
const array4 = array1.concat(array2,array3);
我不知道为什么会这样做,是否有这些决定形成的历史记录呢?
但我也觉得,push() 返回 数组的长度 是不清晰和不直观的,就像下面这样:
let arr = ["a", "b"];
let test = arr.push("c");
console.log(test); // 3
如果你想使用清晰直观的方法而不是push(),那么你可以使用concat(),它会返回包含数组值的数组,如下所示:
let arr = ["a", "b"];
let test = arr.concat("c");
console.log(test); // ["a", "b", "c"]