spread operator
とarray.concat()
の違いは何ですか
let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log([...numbers, ...parts]);
Array.concat()関数
let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log(numbers.concat(parts));
両方の結果は同じです。それでは、どのようなシナリオを使用したいのでしょうか?そして、パフォーマンスに最適なのはどれですか?
console.log(['one', 'two', 'three', 'four', 'five'])
も同じ結果になるので、ここでどちらを使用するのですか? :P
一般に、任意のソースから2つ(またはそれ以上)の配列がある場合はconcat
を使用し、常に配列の一部である追加要素が以前にわかっている場合は、配列リテラルでspread構文を使用します。したがって、コードにconcat
を含む配列リテラルがある場合は、スプレッド構文に進み、そうでない場合はconcat
を使用します。
[...a, ...b] // bad :-(
a.concat(b) // good :-)
[x, y].concat(a) // bad :-(
[x, y, ...a] // good :-)
また、配列以外の値を処理する場合、2つの選択肢の動作はまったく異なります。
@Bergiが言ったように、引数が配列でない場合、concat
とスプレッドは非常に異なります。
引数が配列でない場合、concat
はそれを「配列」(つまり、x
を[x]
に変換)し、この一時配列を続行しますが、...
は反復を試み、失敗した場合は失敗します。考慮してください:
a = [1, 2, 3]
x = 'hello';
console.log(a.concat(x)); // [ 1, 2, 3, 'hello' ]
console.log([...a, ...x]); // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]
ここで、concat
は文字列をアトミックに処理しますが、...
はデフォルトのイテレータchar-by-charを使用します。
もう一つの例:
x = 99;
console.log(a.concat(x)); // [1, 2, 3, 99]
console.log([...a, ...x]); // TypeError: x is not iterable
繰り返しますが、concat
の場合、数値はアトムであり、...
はそれを反復しようとして失敗します。
最後に:
function* gen() { yield *'abc' }
console.log(a.concat(gen())); // [ 1, 2, 3, Object [Generator] {} ]
console.log([...a, ...gen()]); // [ 1, 2, 3, 'a', 'b', 'c' ]
concat
はジェネレーターを反復しようとせず、全体として追加しますが、...
はすべての値を適切にフェッチします。
要約すると、引数が配列でない可能性がある場合、concat
と...
の選択は、それらを反復させるかどうかによって異なります。
パフォーマンス面では、concat
はより高速です。おそらく、...
は共通の反復プロトコルに準拠する必要がありますが、おそらくアレイ固有の最適化の恩恵を受けることができるからです。タイミング:
let big = (new Array(1e5)).fill(99);
let i, x;
console.time('concat-big');
for(i = 0; i < 1e2; i++) x = [].concat(big)
console.timeEnd('concat-big');
console.time('spread-big');
for(i = 0; i < 1e2; i++) x = [...big]
console.timeEnd('spread-big');
let a = (new Array(1e3)).fill(99);
let b = (new Array(1e3)).fill(99);
let c = (new Array(1e3)).fill(99);
let d = (new Array(1e3)).fill(99);
console.time('concat-many');
for(i = 0; i < 1e2; i++) x = [1,2,3].concat(a, b, c, d)
console.timeEnd('concat-many');
console.time('spread-many');
for(i = 0; i < 1e2; i++) x = [1,2,3, ...a, ...b, ...c, ...d]
console.timeEnd('spread-many');
私が有効だと思う1つの違いは、大きな配列サイズにスプレッド演算子を使用すると、Maximum call stack size exceeded
のエラーが発生し、concat
演算子の使用を回避できることです。
var someArray = new Array(600000);
var newArray = [];
var tempArray = [];
someArray.fill("foo");
try {
newArray.Push(...someArray);
} catch (e) {
console.log("Using spread operator:", e.message)
}
tempArray = newArray.concat(someArray);
console.log("Using concat function:", tempArray.length)
シナリオに関する良い回答が既にあるので、パフォーマンスの質問に答えています。テストを作成し、最新のブラウザーで実行しました。結果とコードの下。
/*
* Performance results.
* Browser Spread syntax concat method
* --------------------------------------------------
* Chrome 75 626.43ms 235.13ms
* Firefox 68 928.40ms 821.30ms
* Safari 12 165.44ms 152.04ms
* Edge 18 1784.72ms 703.41ms
* Opera 62 590.10ms 213.45ms
* --------------------------------------------------
*/
私が書いて使用したコードの下。
const array1 = [];
const array2 = [];
const mergeCount = 50;
let spreadTime = 0;
let concatTime = 0;
// Used to popolate the arrays to merge with 10.000.000 elements.
for (let i = 0; i < 10000000; ++i) {
array1.Push(i);
array2.Push(i);
}
// The spread syntax performance test.
for (let i = 0; i < mergeCount; ++i) {
const startTime = performance.now();
const array3 = [ ...array1, ...array2 ];
spreadTime += performance.now() - startTime;
}
// The concat performance test.
for (let i = 0; i < mergeCount; ++i) {
const startTime = performance.now();
const array3 = array1.concat(array2);
concatTime += performance.now() - startTime;
}
console.log(spreadTime / mergeCount);
console.log(concatTime / mergeCount);
また、ブログでこのトピックについて書いた: https://www.malgol.com/how-to-merge-two-arrays-in-javascript/ 。