javascript - spread operatörü vs array.concat()


spread operator ile array.concat() arasındaki fark nedir

let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log([...numbers, ...parts]);

Array.concat() fonksiyonu

let parts = ['four', 'five'];
let numbers = ['one', 'two', 'three'];
console.log(numbers.concat(parts));

Her iki sonuç da aynı. Peki, onları hangi tür senaryolarda kullanmak istiyoruz? Ve hangisi performans açısından en iyisi?

Cevap #1

concat ve spread operatörleri, argüman bir dizi olmadığında çok farklıdır.

Argüman bir dizi olmadığında, concat onu tamamını ekler, oysa ... üzerinde yinelemeye çalışır ve yapamazsa başarısız olur. Şuna bakın:

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' ]

Burada, concat stringi atomik olarak işlerken, ... varsayılan yineleyicisini, karakter karakter kullanır.

Başka bir örnek:

x = 99;

console.log(a.concat(x));   // [1, 2, 3, 99]
console.log([...a, ...x]);  // TypeError: x is not iterable

Yine, concat için sayı bir atomdur, ... onu yinelemeye çalışır ve başarısız olur.

Sonunda :

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, üreticiyi yinelemeye çalışmaz ve onu bütün olarak ekler, oysa ... ondan tüm değerleri güzelce alır.

Özetlemek gerekirse, argümanlarınız olası olarak dizi değilse, concat ile ... arasındaki seçim, bunların üzerinde yineleme yapmak isteyip istemediğinize bağlıdır.

Yukarıda concat'in varsayılan davranışı açıklanmıştır, ancak ES6 bunu Symbol.isConcatSpreadable ile geçersiz kılmanın bir yolunu sunar. Varsayılan olarak, bu sembol diziler için true, diğer her şey için ise false'tur. Bunu true olarak ayarlamak, concat'e argümanı tıpkı ... gibi yinelemesini söyler:

str = 'hello'
console.log([1,2,3].concat(str)) // [1,2,3, 'hello']

str = new String('hello');
str[Symbol.isConcatSpreadable] = true;
console.log([1,2,3].concat(str)) // [ 1, 2, 3, 'h', 'e', 'l', 'l', 'o' ]

Performans açısından concat daha hızlıdır, muhtemelen diziye özgü optimizasyonlardan yararlanabilmesi nedeniyle, oysa ... ortak yineleme protokolüne uymak zorundadır. Zamanlamalar:

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');

Cevap #2

.concat() daha hızlıdır, aksi takdirde her iki argüman da liste olduğu varsayıldığında aynı şekilde çalışırlar. İşte her biri 10 milyon eleman içeren iki diziyi birleştirmenin süresi (daha düşük olan daha iyidir):

Tarayıcı[...a, ...b]a.concat(b)
Chrome 113350ms30ms
Firefox 113400ms63ms
Safari 16.492ms71ms

Bu kodu 8GB RAM'e sahip bir M1 MacBook Air'de çalıştırdım:

const arraySize = 10000000;
const trials = 50;

const array1 = [];
const array2 = [];
for (let i = 0; i < arraySize; ++i) {
  array1.push(i);
  array2.push(i);
}

let spreadTime = 0;
for (let i = 0; i < trials; ++i) {
  const start = performance.now();
  const array3 = [...array1, ...array2];
  const end = performance.now();

  spreadTime += end - start;
}

let concatTime = 0;
for (let i = 0; i < trials; ++i) {
  const start = performance.now();
  const array3 = array1.concat(array2);
  const end = performance.now();

  concatTime += end - start;
}

// performance.now() returns milliseconds with a
// 5 microsecond resolution in isolated contexts and a
// 100 microsecond resolution in non-isolated contexts.
spreadTime = Math.round(spreadTime / trials * 1000) / 1000;
concatTime = Math.round(concatTime / trials * 1000) / 1000;
console.log(`${arraySize} items - spread: ${spreadTime}ms concat: ${concatTime}ms`);

Cevap #3

Şey, console.log(['one', 'two', 'three', 'four', 'five']) de aynı sonuca sahip, o zaman burada neden herhangi birini kullanalım ki? :P

Genel olarak, farklı kaynaklardan gelen iki (veya daha fazla) diziye sahip olduğunuzda concat'i kullanırsınız ve dizide her zaman bulunacak ek elemanlar önceden biliniyorsa, dizi literalinde yayılma (spread) sözdizimini kullanırsınız. Yani kodunuzda concat ile bir dizi literaliniz olacaksa, yayılma sözdizimini kullanın, aksi halde sadece concat'i kullanın:

[...a, ...b] // bad :-(
a.concat(b) // good :-)

[x, y].concat(a) // bad :-(
[x, y, ...a]    // good :-)

Ayrıca, iki alternatif de dizi olmayan değerlerle uğraştıklarında oldukça farklı davranır.

Cevap #4

Bence geçerli olan tek fark, büyük dizi boyutu için spread operatörü kullanmanın size Maksimum çağrı yığını boyutu aşıldı hatası verecek olmasıdır; bunu concat operatörünü kullanarak önleyebilirsiniz.

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)

Cevap #5

Güncelleme:

Concat şimdi her zaman spread'den daha hızlıdır. Aşağıdaki kıyaslama, hem küçük hem de büyük boyutlu dizilerin birleştirildiğini gösteriyor: https://jsbench.me/nyla6xchf4/1

// preparation
const a = Array.from({length: 1000}).map((_, i)=>`${i}`);
const b = Array.from({length: 2000}).map((_, i)=>`${i}`);
const aSmall = ['a', 'b', 'c', 'd'];
const bSmall = ['e', 'f', 'g', 'h', 'i'];

const c = [...a, ...b];
// vs
const c = a.concat(b);

const c = [...aSmall, ...bSmall];
// vs
const c = aSmall.concat(bSmall)

Önceki:

Büyük dizilerde performans söz konusu olduğunda bazı yanıtlar doğru olsa da, küçük dizilerle uğraşırken performans oldukça farklıdır.

Sonuçları kendiniz https://jsperf.com/spread-vs-concat-size-agnostic adresinde kontrol edebilirsiniz.

Gördüğünüz gibi, spread daha küçük diziler için %50 daha hızlıdır, oysa concat büyük dizilerde birkaç kat daha hızlıdır.

İlgili makaleler