【问题标题】:Why does Array.prototype.push return the new length instead of something more useful?为什么 Array.prototype.push 返回新的长度而不是更有用的东西?
【发布时间】:2016-03-19 11:12:30
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

标签: javascript specifications return-type array-push ecma


【解决方案1】:

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

  • 返回新附加的项目:

鉴于 JavaScript 使用 C 风格的赋值来发出赋值(与基本风格的赋值相反),你仍然可以有这种行为:

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

(虽然我知道这确实意味着您不能将它作为声明+赋值组合中 r 值的一部分)

  • 返回变异数组本身:

这将采用“流利”API 的风格,在 ECMAScript 3 完成后获得了极大的欢迎,并且不会保持 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() );

【讨论】:

  • Refluent APIs:没有一个改变数组的方法会返回该数组。如果您主要将它们用于副作用,那么它们实际上不应该是可链接的。
【解决方案2】:

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

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

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

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

没有线索 - 我不确定是否存在符合这些原则的记录。这将取决于实施者,并且可能会在任何实现 ECMA 脚本标准的给定代码库中进行注释。

【讨论】:

  • [1,2,3].map(x => x+=4).push(1) //=== 4, 如果返回 [5,6, 7,1],不是吗?但是... [1,2,3].map(x => x+=4).concat(1) //=== [5, 6, 7, 1]
【解决方案3】:

我理解 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 是一个具有idshelf 属性(等等)的对象。
setState() 接受一个对象,该对象包含要分配给 state 的新值

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

所以我们使用 books 数组原样。
filter 删除 selectedBook 的旧副本。
然后concat 重新添加selectedBook,在更新它的shelf 属性后。

想要链接push的绝佳用例。
然而,正确的做法实际上是使用concat

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

【讨论】:

  • 嗯,我不明白。 oldArray.concat[newItem]; 在任何正常的 JS 环境中为我返回 undefined。 2ality.com/2016/11/concat-array-literal.html 有一个有趣的相关讨论,但没有解释您的语法如何工作。我认为这是特定于 React 的事情?数组原型有什么魔力?
  • 我认为你有一个错字@Tao。应该是oldArray.concat(newItem)
  • 感谢@NickManning,这确实是重点;在我发表评论大约 5 个月后,@SherylHohman 编辑了答案,将明显错误的newArray = oldArray.concat[newItem]; 更改为更正后的newArray = oldArray.concat([newItem]);,从而回答了我的问题。他们确实在底部的摘要中留下了错误的语法,但是我现在也更正了。我的困惑完全解决了。
  • 是的 - 为了记录,我认为你也可以连接一个项目 [].concat(3) 和 [].concat([3]) 都做同样的事情。
【解决方案4】:

posted this in TC39's communication hub,并且能够更多地了解这背后的历史:

pushpopshiftunshift 最初于 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.
 */

【讨论】:

    【解决方案5】:

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

    但我也认为 push() 返回 数组长度 不清楚并且不直观如下:

    let arr = ["a", "b"];
    
    let test = arr.push("c");
    
    console.log(test); // 3

    那么,如果你想使用clear直观的方法代替push(),你可以使用concat() 返回数组及其值,如下所示:

    let arr = ["a", "b"];
    
    let test = arr.concat("c");
    
    console.log(test); // ["a", "b", "c"]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-20
      • 2020-07-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多