2つのアレイがあります。
const pets = ["dog", "cat", "hamster"]
const wishlist = ["bird", "snake"]
wishlist
をpets
に追加します。これは、2つの方法を使用して実行できます。
方法1:
pets.Push.apply(pets,wishlist)
その結果:[ 'dog', 'cat', 'hamster', 'bird', 'snake' ]
方法2:
pets.Push(...wishlist)
その結果も:[ 'dog', 'cat', 'hamster', 'bird', 'snake' ]
より大きなデータを処理する場合、パフォーマンスの点でこれら2つの方法に違いはありますか?
どちらも Function.prototype.apply
およびスプレッド構文は、大きな配列に適用されると、スタックオーバーフローを引き起こす可能性があります。
let xs = new Array(500000),
ys = [], zs;
xs.fill("foo");
try {
ys.Push.apply(ys, xs);
} catch (e) {
console.log("apply:", e.message)
}
try {
ys.Push(...xs);
} catch (e) {
console.log("spread:", e.message)
}
zs = ys.concat(xs);
console.log("concat:", zs.length)
使用する Array.prototype.concat
代わりに。スタックオーバーフローを回避することに加えて、concat
には変異を回避するという利点もあります。突然変異は微妙な副作用を引き起こす可能性があるため、有害と見なされます。
しかし、それは教義ではありません。関数のスコープを調べ、ミューテーションを実行してパフォーマンスを向上させ、ガベージコレクションを軽減する場合は、親スコープに表示されていない限り、ミューテーションを実行できます。
質問をwhich is more performant in general, using .Push() as an example
と解釈すると、applyは[ほんの少し]速くなります(MS Edgeを除く、以下を参照)。
ここにパフォーマンステストがあります 2つのメソッドに対して関数を動的に呼び出すオーバーヘッドのみ。
_function test() { console.log(arguments[arguments.length - 1]); }
var using = (new Array(200)).fill(null).map((e, i) => (i));
_
_test(...using);
_
_test.apply(null, using)
_
私はChrome 71.0.3578.80 (Official Build) (64-bit)
、FF 63.0.3 (64-bit)
、&_Edge 42.17134.1.0
_でテストしましたが、これらは自分で数回実行した後の私の結果です 最初の結果は常に何らかの方法で歪んでいました
ご覧のように、Edgeはapply
の実装がitよりも_...
_よりも優れているようです(ただし、結果を比較しないでください)ブラウザでは、Edgeのapply
が他のブラウザよりも優れているか、悪い_...
_であるか、またはこのデータから両方のビットが少しあるかがわかりません。
これを踏まえると、特にEdgeをターゲットにしているのでない限り、特にオブジェクトをapply
に戻す必要がある場合は、読みやすくなるように_...
_を使用します。 this
の場合。
配列のサイズにも依存する可能性があるため、_@Jaromanda X
_が言ったように、独自のテストを行って、本当に確認する必要がある場合は_200
_を変更します。
他の回答は質問をwhich would be better for .Push() specifically
として解釈し、解決されている「問題」に巻き込まれ、単にjust use .concat()
を推奨します。これは基本的に標準の_why are you doing it that way?
_であり、 .Push()
(たとえば、_Math.max
_、または独自のカスタム関数)を使用して解決策を探していない、グーグルから来た人々を怒らせます。
ftorが指摘したもの とは別に、Array.prototype.concat
は、平均して、配列拡散演算子よりも少なくとも1.4倍高速です。
ここで結果を参照してください: https://jsperf.com/es6-add-element-to-create-new-array-concat-vs-spread-op
ここで独自のブラウザーとマシンでテストを実行できます: https://www.measurethat.net/Benchmarks/Show/579/1/arrayprototypeconcat-vs-spread-operator
Pushでは既存の配列に追加し、spreadオペレーターではコピーを作成します。
a=[1,2,3]
b=a
a=[...a, 4]
alert(b);
=> 1、2、3
a=[1,2,3]
b=a
a.Push(4)
alert(b);
=> 1、2、3、4
Push.applyも:
a=[1,2,3]
c=[4]
b=a
Array.prototype.Push.apply(a,c)
alert(b);
=> 1、2、3、4
連結はコピーです
a=[1,2,3]
c=[4]
b=a
a=a.concat(c)
alert(b);
=> 1、2、3
特に大きなアレイの場合は、参照することをお勧めします。
スプレッドオペレーターは、従来は次のような方法で行われるコピーを行う高速な方法です。
a=[1,2,3]
b=[]
a.forEach(i=>b.Push(i))
a.Push(4)
alert(b);
=> 1、2、3
コピーが必要な場合は、spreadオペレーターを使用してください。これは高速です。または、@ ftorが指摘するconcatを使用します。そうでない場合は、プッシュを使用します。ただし、変更できないコンテキストがあることに注意してください。さらに、これらの関数のいずれかを使用すると、深いコピーではなく、浅いコピーが得られます。ディープコピーにはlodashが必要です。詳細はこちら: https://slemgrim.com/mutate-or-not-to-mutate/
答えはuser6445533の回答を受け付けましたが、テストケースはちょっと変だと思います。それは、通常、spread演算子をどのように使用すべきかとは思えません。
なぜこんなことができないのですか?
let newPets = [...pets, ...wishlist]
説明されているように、スタックオーバーフローの問題には直面しません。 Hashbrownが述べたように、それは同様にあなたにパフォーマンス上の利益を与えるかもしれません。
*私もES6の学習の最中です。私が間違っているとすみません。
大きな配列に追加する場合、 spread演算子はvastly高速です です。 _@ftor
_/_@Liau Jian Jie
_がどのようにして結論を導き出したのかわかりません。
Chrome 71.0.3578.80 (Official Build) (64-bit)
、FF 63.0.3 (64-bit)
、&_Edge 42.17134.1.0
_
concat()
は配列のコピーを作成し、同じメモリを使用することさえしないため、理にかなっています。
「突然変異」についてのことは何にも基づいていないようです。古い配列を上書きする場合、concat()
を使用してもメリットはありません。
_...
_を使用しない唯一の理由はスタックオーバーフローです。私はあなたの他の回答に同意します_...
_またはapply
は使用できません。
しかし、それでもfor {Push()}
を使用するだけで、多かれ少なかれconcat()
すべてのブラウザでオーバーフローしません。
あなたneedtoでない限り、concat()
を使用する理由はありません古い配列を保持。