使用JavaScript中的’concat’方法与’push’方法相比,有什么优势?

huangapple go评论72阅读模式
英文:

What are the benefits of using the 'concat' method over 'push' for concatenating arrays in JavaScript?

问题

concat(array) vs push(...array)

我一直在使用.push(...)方法来连接两个数组,解构第二个数组,并将其作为参数传递(array1.push(...array2))。但我刚刚注意到.concat(...)做了同样的事情,至少据我所知。

我只想知道除了.concat(...)更符合惯用法之外,使用其中一个的好处是什么,如果有的话。

英文:

concat(array) vs push(...array)

I've been using .push(...) method to concatenate 2 arrays unpacking the second array, using the three dots notation, and passing that as the argument (array1.push(...array2)). But I just noticed that .concat(...) does the same thing, at least as far as I know.

I just want to know what are the benefits of using one over the other, if any, aside from .concat(...) being more idiomatic.

答案1

得分: 3

JavaScript引擎通常对您可以传递给方法的参数数量设置了上限,否则它们会引发错误。例如,在Chrome中,以下代码会引发错误:

const arr = Array(150000).fill(0);
const arr2 = [];
arr2.push(...arr);

而使用.concat()时,您只会将一个数组传递给该方法,因此不会出现错误:

const arr = Array(150000).fill(0);
const arr2 = [];
const res = arr2.concat(arr);
// res 是一个包含 150000 个 `0` 的数组

另外,使用.push() + ... 会有效地对您的可迭代/数组执行两次迭代,一次用于将其内容解包为.push() 方法的参数,然后另一次由.push() 方法内部执行,用于迭代每个参数并将其附加到目标数组中。

另一个显著的区别在于这两种方法返回的内容。.concat() 将返回一个新数组,不会修改目标数组,这在需要生成新数组而不改变原始数组的方法(如.map().reduce())中很有用。而.push() 将返回更新后数组的长度,并修改目标数组,因此这是另一个需要考虑的区别。

正如@T.J. Crowder所指出的,使用扩展语法 ... 时,数组的迭代器不会保留稀疏数组中的空白,而是将它们解包为 undefined 值。这意味着,如果您使用.push() + ... 时,数组是稀疏的,那么空白处将得到 undefined 值,而直接使用.concat() 时将保留空白:

const arr = Array(3); // 这是一个稀疏数组,[empty × 3],不要与 [undefined, undefined, undefined] 混淆
const arr2 = [];
arr2.push(...arr); // 这里的 `...` 变成了:arr2.push(undefined, undefined, undefined);
console.log(arr2); // [undefined, undefined, undefined]

const arr3 = [];
console.log(arr3.concat(arr)); // 保留空白:[empty × 3]
英文:

JavaScript engines typically put a cap on the number of arguments you can pass to a method before they throw an error, for example, the below throws an error in Chrome:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const arr = Array(150000).fill(0);
const arr2 = [];
arr2.push(...arr);

<!-- end snippet -->

Whereas when using .concat(), you'll only be passing the one array to the method, so you don't get the error:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const arr = Array(150000).fill(0);
const arr2 = [];
const res = arr2.concat(arr);
// res is an array with 150000 `0`s

<!-- end snippet -->

Additionally, with .push() + ..., you're effectively doing two iterations over your iterable/array, one for unpacking its contents as arguments to the .push() method, and then another for one done internally by the .push() method itself to iterate through each of the arguments and append it to the target array.

Another noticeable difference is in what both methods return, .concat() will return a new array and won't modify the target, which can be useful in methods such as .map() or .reduce() where you need to produce a new array without mutating the original. Whereas .push() will return the length of the updated array and will modify the target, so that is another difference to consider.

As pointed out by @T.J. Crowder, the iterator of arrays which is invoked when using the spread syntax ... does not preserve blanks in sparse arrays, instead it unpacks them as undefined values, meaning that if the array you're specifying is sparse when using .push() + ... you'll get undefined for the blanks, whereas the blanks will be preserved when using .concat() directly:

<!-- begin snippet: js hide: false console: false babel: false -->

<!-- language: lang-js -->

const arr = Array(3); // this is a sparse array, [empty &#215; 3], not to be confused with [undefined, undefined, undefined]
const arr2 = [];
arr2.push(...arr); // Here `...` becomes: arr2.push(undefined, undefined, undefined);
console.log(arr2); // [undefined, undefined, undefined]

const arr3 = [];
console.log(arr3.concat(arr)); // Retains empties: [empty &#215; 3]

<!-- language: lang-html -->

See browser console for results

<!-- end snippet -->

答案2

得分: 0

The big difference that push() adds elements to an array, but concat() returns a new array.
push()添加元素到数组,而concat()返回一个新数组。

Recently after been working with Vue I found that it's important to keep object variables as intact as possible and declare them with const.
最近在使用Vue后,我发现尽量保持对象变量不变并使用const声明它们非常重要。

This allows not losing references to the original objects and promotes reactive programming.
这样可以保留对原始对象的引用,促进响应式编程。

For me currently that's a very important concept. It allows me to be relaxed regarding whether any object reference could be broken if the code is very modular.
对我来说,这是一个非常重要的概念。它让我不用担心代码非常模块化时是否会破坏任何对象引用。

The only problem that spread arguments are slow and not safe with big arrays, so I wouldn't mind monkeypatching Array.prototype.append with my custom benchmarked implementation.
唯一的问题是展开参数在处理大数组时速度较慢且不安全,所以我不介意使用我的自定义基准实现来monkeypatch Array.prototype.append

An example how the appending could be implemented, it's faster than pushing (there's also some boost because of pre-allocating new elements).
下面是一个实现追加操作的示例,它比pushing更快(也因为预先分配新元素而有一些性能提升)。

英文:

The big difference that push() adds elements to an array, but concat() returns a new array.
Recently after been working with Vue I found that it's important keep object variables as intact as possible and declare them with const.

This allows not losing references to the original objects and promotes reactive programming.
For me currently that's a very important concept. It allows me to be relaxed regarding whether any object reference could be broken if code is very modular.

The only problem that spread arguments are slow and not save with big arrays so I wouldn't mind to monkeypatch Array.prototype.append with my custom benchmarked implementation.

For example:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

class Some{
  constructor(source){
    this.arr = source;
  }
  push(...elems){
    this.arr.push(...elems);
  }
}

let source = []; // that&#39;s bad for me, I use const now as much as possible

const some = new Some(source);

// step 1: using push
source.push(...[0, 0, 0]);
some.push(...[1, 1, 1]);

console.log(source.join());
console.log(some.arr.join());

// step 1: using concat, here the original array link is lost
source = source.concat([2, 2, 2]);

// hmm, one would expect them to be equal...
console.log(source.join());
console.log(some.arr.join());

<!-- end snippet -->

An example how the appending could be implemented, it's faster than pushing (there's also some boost because of pre-allocating new elements).

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

    const arr = Array(10000000).fill(0);
    
    Array.prototype.append = function(arr){
    
        this.length += arr.length;
        for(let i = 0, j = this.length - arr.length; i &lt; arr.length; i++, j++){
            this[j] = arr[i];
        }
        
        return this;
    
    };
    
    let start = performance.now();
    
    const dst = [];
        
    for(let i = 0; i &lt; arr.length; i++){
        dst.push(arr[i]);
    }
    
    console.log(`pushing: ${performance.now() - start}`);
    
    start = performance.now();
    
    const dst2 = [];
        
    dst2.append(arr);
    
    
    console.log(`appending: ${performance.now() - start}`); 

<!-- end snippet -->

答案3

得分: 0

I don't understand if you want to add an Array into another or just make one Array made of two Arrays.

In the last case this is a short way to concatenate Arrays too by using spread syntax:

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArray = [...arr1, ...arr2];
console.log(combinedArray);

Note:

If you need to preserve the original arrays, this code above is suitable, like the concat() method.

The push() method will modify your initial Array.

英文:

I don't understand if you want to add an Array into another or just make one Array made of two Arrays.

In the last case this is a short way to concatenate Arrays too by using spread syntax:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const arr1 = [1, 2, 3];
const arr2 = [4, 5, 6];
const combinedArray = [...arr1, ...arr2];
console.log(combinedArray);

<!-- end snippet -->

Note:

If you need to preserve the original arrays, this code above is suitable, like the concat() method.

The push() method will modify your initial Array.

huangapple
  • 本文由 发表于 2023年5月21日 21:16:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300088.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定