25

自从它在ECMA-262, 3rd Edition中引入以来,该Array.prototype.push方法的返回值为 a Number

15.4.4.7 Array.prototype.push ([ item1 [ , item2 [ , ... ] ] ] )

参数按照它们出现的顺序附加到数组的末尾。数组的新长度作为调用的结果返回。

返回数组的新长度背后的设计决策是什么,而不是返回可能更有用的东西,例如:

  • 对新附加项目的引用
  • 变异数组本身

为什么会这样做,是否有历史记录说明这些决定是如何做出的?

4

5 回答 5

8

在 TC39 的通讯中心发布了这篇文章,并且能够更多地了解这背后的历史:

push, pop, shift,unshift最初是在 1997 年添加到 JS1.2 (Netscape 4) 中的。

有模仿 Perl 中类似命名的函数。

JS1.2 push 遵循 Perl 4 返回最后一个推送的项目的约定。在 JS1.3(Netscape 4.06 Summer 1998)中,将 push 更改为遵循 Perl 5 返回数组新长度的约定。

https://dxr.mozilla.org/classic/source/js/src/jsarray.c#804

/*
 * If JS1.2, follow Perl4 by returning the last thing pushed.  Otherwise,
 * return the new array length.
 */
于 2019-06-03T14:24:23.747 回答
8

我理解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)
  }
));

stateobject 有一个books属性,它包含一个book.
book是一个具有id, 和shelf属性(等等)的对象。
setState()接受一个对象,该对象包含要分配给的新值state

selectedBook已在books数组中,但shelf需要更改其属性。
然而,我们只能给出setState一个顶级对象。
我们不能告诉它去寻找这本书,并在那本书上寻找一个属性,并赋予它这个新的价值。

所以我们按books原样使用数组。
filter删除selectedBook.
然后在更新它的属性后concat重新添加。selectedBookshelf

想要链接的很好用例push
但是,执行此操作的正确方法实际上是使用concat.

摘要:
array.push()将返回一个数字(变异数组的新长度)。
array.concat([])将返回一个新的“变异数组”。
从技术上讲,它返回一个数组,其中添加了修改后的元素,并保持初始数组不变。返回一个新的数组实例,而不是回收现有的数组实例是一个重要的区别,这使得 React 应用程序中的状态对象非常有用,可以获取更改的数据以重新渲染。

于 2018-01-02T05:27:35.507 回答
2

既然你问了,我就很好奇。我制作了一个示例数组并在 Chrome 中对其进行了检查。

var arr = [];
arr.push(1);
arr.push(2);
arr.push(3);
console.log(arr);

在此处输入图像描述

由于我已经引用了数组以及我推入其中的每个对象,因此只有一个其他属性可能有用……长度。通过返回 Array 数据结构的这一附加值,我现在可以访问所有相关信息。这似乎是最好的设计选择。那,或者如果您想为节省 1 条单条机器指令而争论,则根本不返回任何内容。

为什么会这样做,是否有关于如何做出这些决定的历史记录?

没有线索-我不确定是否存在沿着这些思路的基本原理记录。这将取决于实施者,并且可能会在任何实现 ECMA 脚本标准的给定代码库中进行评论。

于 2015-12-14T03:31:18.960 回答
2

我无法解释他们为什么选择返回新长度,但响应您的建议:

  • 返回新附加的项目:

鉴于 JavaScript 使用发出分配值的 C 样式赋值(与不发出分配值的基本样式赋值相反),您仍然可以具有这种行为:

var addedItem;
myArray.push( addedItem = someExpression() );

(尽管我认识到这确实意味着您不能将其作为声明+赋值组合中的右值的一部分)

  • 返回变异数组本身:

那将是在 ECMAScript 3 完成后显着流行的“流利”API 的风格,它不会保持 ECMAScript 中其他库功能的风格,而且,启用通过创建自己的push方法来实现您所追求的场景:

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() );
于 2015-12-14T03:15:47.733 回答
-1

我不知道“为什么会这样做,是否有关于如何做出这些决定的历史记录?” .

但我也认为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"]

于 2021-11-19T11:08:44.920 回答