オブジェクトスプレッドとObject.assign


396

のは、私が持っていると言うoptions変数を、私はいくつかのデフォルト値を設定したいです。

これらの2つの選択肢の利点/欠点は何ですか?

オブジェクトスプレッドの使用

options = {...optionsDefault, ...options};

またはObject.assignを使用する

options = Object.assign({}, optionsDefault, options);

これは私が不思議に思ったコミットです。


4
1つ目は、提案された新しい構文であり、ES6の一部ではないため、準拠する標準に依存します。
loganfsmyth

5
ベスト」を定義します(慎重に、意見に基づく質問に終わらないでください:-)
Amit

1
また、ネイティブサポートのない環境で実行する場合に、サポートする方法に依存する可能性もあります。コンパイルするだけでできる構文。ポリフィルする必要があるオブジェクトまたはメソッド。
JMM、2015年

6
互換性の問題は別として、Object.assignは元のオブジェクトを変更できるので便利です。広がりません。
スタントン2018年

2
@pstantonのコメントを明確にするために-object.assignは既存のターゲットオブジェクトを変更できます(ソースからプロパティを上書きし、他のプロパティはそのままにします)。ソースオブジェクトには触れません。私はまず彼の「元のオブジェクト」を「ソースオブジェクト」として読んだので、同様に読み間違えた人のためにこのメモを書きました。:)
ToolmakerSteve

回答:


328

これは必ずしも完全なものではありません。

スプレッド構文

options = {...optionsDefault, ...options};

利点:

  • ネイティブサポートのない環境で実行するコードを作成する場合、(ポリフィルを使用するのではなく)この構文をコンパイルするだけで済む場合があります。(例えば、Babelで。)

  • あまり冗長ではありません。

短所:

  • この回答が最初に書かれたとき、これは提案であり、標準化されていませんでした。プロポーザルを使用するときは、それを使用してコードを記述し、標準化に向けて移動しても標準化または変更されない場合にどうするかを検討してください。これは、ES2018で標準化されました。

  • 動的ではなくリテラル。


Object.assign()

options = Object.assign({}, optionsDefault, options);

利点:

  • 標準化されています。

  • 動的。例:

    var sources = [{a: "A"}, {b: "B"}, {c: "C"}];
    options = Object.assign.apply(Object, [{}].concat(sources));
    // or
    options = Object.assign({}, ...sources);

短所:

  • もっと冗長。
  • ネイティブサポートのない環境で実行するコードを作成する場合は、ポリフィルする必要があります。

これは私が不思議に思ったコミットです。

それはあなたが求めていることとは直接関係ありません。そのコードはを使用していませんでした。同じことObject.assign()を行うユーザーコード(object-assign)を使用していました。彼らはそのコードをBabelでコンパイルしている(そしてそれをWebpackにバンドルしている)ようです。これは、私が話していたものです。コンパイルできる構文です。彼らは明らかobject-assignに彼らのビルドに入る依存関係として含める必要があることを好んだ。


15
それは価値が広がりがとてもステージ3に移動したオブジェクトの残りはおそらく将来に標準化されることに注目かもしれないtwitter.com/sebmarkbage/status/781564713750573056
williaster

11
@JMM「もっと冗長」が表示されるかどうかわかりません。欠点として。
DiverseAndRemote.com 2017

6
@オマール、私はあなたがそれが意見であることを証明しただけだと思います:)誰かがその利点を認識していない場合は、そうです、他のすべての人は等しく使用できますObject.assign()。それとも、手動で手動でターゲットに割り当てるオブジェクトと、自分の小道具の配列を反復処理し、それがさらに冗長作ることができます:P
JMM

7
@JMMが言及したように、現在はES2018の仕様 ノードにあります。green/#ES2018
Sebastien H.

2
「すべてのバイト数」これは、@ yzorgのminimizers / uglifyの意味です
DiverseAndRemote.com

171

参照オブジェクトのレスト/スプレッドは、ステージ4としてECMAScript 2018で最終決定されます。提案はこちらにあります

ほとんどの場合、オブジェクトのリセットとスプレッドは同じように機能しますが、大きな違いは、spreadはプロパティを定義するのに対して、Object.assign()はそれらを設定することです。つまり、Object.assign()はセッターをトリガーします。

これ以外に、オブジェクトレスト/スプレッド1:1はObject.assign()にマップされ、配列(反復可能)スプレッドとは異なる動作をすることを覚えておく価値があります。たとえば、配列を拡散すると、ヌル値が拡散されます。ただし、オブジェクトスプレッドのnull値を使用すると、何も表示されずに暗黙的に広がります。

配列(反復可能)スプレッドの例

const x = [1, 2, null , 3];
const y = [...x, 4, 5];
const z = null;

console.log(y); // [1, 2, null, 3, 4, 5];
console.log([...z]); // TypeError

オブジェクトスプレッドの例

const x = null;
const y = {a: 1, b: 2};
const z = {...x, ...y};

console.log(z); //{a: 1, b: 2}

これはObject.assign()の動作と一致しており、どちらもエラーなしでnull値を暗黙的に除外します。

const x = null;
const y = {a: 1, b: 2};
const z = Object.assign({}, x, y);

console.log(z); //{a: 1, b: 2}

10
これが選択された答えです。
エヴァンプライス

4
これが答えになるはずです...それが今の未来です。
ザカリーアブレッシュ

1
これが正解です。主な違いはObject.assign、セッターを使用することです。Object.assign({set a(v){this.b=v}, b:2}, {a:4}); // {b: 4}{...{set a(v){this.b=v}, b:2}, ...{a:4}}; // {a: 4, b: 2}
デビッド・ボーホー

1
「未来は今です!」- ジョージアレン明日は遅すぎます。
ruffin

1
nullの処理方法が異なるのは「リンゴとオレンジ」であり、意味のある比較ではありません。配列の場合、nullは配列の要素です。スプレッドの場合、nullはオブジェクト全体です。正しい比較は、xがnullプロパティを持つことですconst x = {c: null};。その場合、AFAIKでは、配列と同じように動作します//{a: 1, b: 2, c: null}
ToolmakerSteve、

39

スプレッド演算子とObject.assign現在の回答で言及されていないように見える大きな違いの1つは、スプレッド演算子がソースオブジェクトのプロトタイプをターゲットオブジェクトにコピーしないことです。オブジェクトにプロパティを追加し、そのインスタンスを変更したくない場合は、を使用する必要がありますObject.assign。以下の例はこれを示しています:

const error = new Error();
error instanceof Error // true

const errorExtendedUsingSpread = {
  ...error,
  ...{
    someValue: true
  }
};
errorExtendedUsingSpread instanceof Error; // false

const errorExtendedUsingAssign = Object.assign(error, {
  someValue: true
});
errorExtendedUsingAssign instanceof Error; // true


「プロトタイプをそのまま保持しない」 -何も変更しないため、確実に保持されます。あなたの例ではerrorExtendedUsingAssign === error、しかし、それerrorExtendedUsingSpreadは新しいオブジェクトです(そしてプロトタイプはコピーされませんでした)。
maaartinus

2
@maaartinusその通りです、私はおそらくそれをひどく言いました。プロトタイプがコピーされたオブジェクト上にないことを意味しました。より明確になるように編集するかもしれません。
Sean Dawson、

次の方法は、そのクラスを持つオブジェクトを「浅いクローン」にする方法ですか?let target = Object.create(source); Object.assign(target, source);
ToolmakerSteve

@ToolmakerSteveはい、オブジェクトの「独自のプロパティ」をすべてコピーします。これにより、事実上、浅いクローンになります。参照:stackoverflow.com/questions/33692912/...
ショーン・ドーソン

12

他の人が述べたように、執筆の現時点でObject.assign()は、ポリフィルが必要であり、オブジェクトの拡散に...は、機能するためにいくつかのトランスパイル(およびおそらくポリフィル)が必要です。

このコードを考えてみましょう:

// Babel wont touch this really, it will simply fail if Object.assign() is not supported in browser.
const objAss = { message: 'Hello you!' };
const newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

// Babel will transpile with use to a helper function that first attempts to use Object.assign() and then falls back.
const objSpread = { message: 'Hello you!' };
const newObjSpread = {...objSpread, dev: true };
console.log(newObjSpread);

これらは両方とも同じ出力を生成します。

以下は、BabelからES5への出力です。

var objAss = { message: 'Hello you!' };
var newObjAss = Object.assign(objAss, { dev: true });
console.log(newObjAss);

var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

var objSpread = { message: 'Hello you!' };
var newObjSpread = _extends({}, objSpread, { dev: true });
console.log(newObjSpread);

これは、これまでのところ私の理解です。Object.assign()実際に標準化されていますが、オブジェクトの広がり...はまだです。唯一の問題は、前者および将来的には後者のブラウザサポートです。

ここでコードを試してください

お役に立てれば。


1
ありがとうございました!あなたのコードサンプルは私のコンテキストにとって決定を本当に簡単にします。トランスパイラー(babelまたはtypescript)は、インラインコードにpollyfillを含めることにより、spreadオペレーターとブラウザーの互換性を高めます。ただ、関心を活字体transpiledバージョンはバベルとほぼ同じである:typescriptlang.org/play/...
マーク・Whitfeld

2
うーん...あなたの2つのケースが同じ結果をもたらさないのではないですか?最初のケースでは、あるオブジェクトから別のオブジェクトにプロパティをコピーし、もう1つのケースでは、新しいオブジェクトを作成します。Object.assignはターゲットを返すため、最初のケースではobjAssとnewObjAssは同じです。
ケビンB

の新しい最初のパラメータを追加{}すると、不整合が修正されます。
Kevin B

11

オブジェクト展開演算子(...)はブラウザでは機能しません。これは、ES仕様の一部ではなく、単なる提案にすぎないためです。唯一のオプションは、Babel(または類似のもの)でコンパイルすることです。

ご覧のとおり、これはObject.assign({})に対する単なる構文上の砂糖です。

私が見る限り、これらは重要な違いです。

  • Object.assignはほとんどのブラウザーで動作します(コンパイルなし)
  • ... オブジェクトは標準化されていません
  • ... オブジェクトを誤って変更しないように保護します
  • ... Object.assignがなければ、ブラウザでObject.assignをポリフィルします
  • ... 同じアイデアを表現するために必要なコードが少ない

34
スプレッド演算子は常に新しいオブジェクトを提供するため、これはの構文糖ではありませんObject.assign
MaxArt

4
実際、他の人が可変性の違いをこれ以上強調していないことに驚いています。Object.assign
deepelement

これは現在、ほとんどの最新のブラウザーでサポートされています(他のES6と同様)。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...を
akmalhakimi1991

11

ブラウザを介して、およびツールを介してエコシステムにおいて、「オブジェクトのマージの拡散」ES機能のステータスを要約したいと思います。

スペック

ブラウザー:Chrome、SF、Firefox(まもなく60、IIUC)

  • このシナリオを含め、Chrome 60同梱されている「スプレッドプロパティ」のブラウザサポート。
  • このシナリオのサポートは、現在のFirefox(59)では機能しませんが、Firefox Developer Editionでは機能します。したがって、Firefox 60で出荷されると思います。
  • Safari:テストされていませんが、KangaxはデスクトップSafari 11.1では機能するがSF 11では機能しないと述べています
  • iOS Safari:Tesetedではありませんが、KangaxはiOS 11.3では機能するがiOS 11では機能しないと述べています
  • Edgeにはまだありません

ツール:ノード8.7、TS 2.1

リンク集

コードサンプル(互換性テストを兼ねています)

var x = { a: 1, b: 2 };
var y = { c: 3, d: 4, a: 5 };
var z = {...x, ...y};
console.log(z); // { a: 5, b: 2, c: 3, d: 4 }

繰り返しますが、このサンプルの執筆時点では、Chrome(60以降)、Firefox Developer Edition(Firefox 60のプレビュー)、およびNode(8.7以降)では、このサンプルは翻訳なしで機能します。

なぜ答えるのですか?

私はこれを2.5 書いています元の質問から2.後にます。しかし、私は同じ質問をしました、そして、これはグーグルが私に送ったところです。私はロングテールを改善するSOの使命の奴隷です。

これは「配列分散」構文の拡張なので、グーグルで検索するのは非常に難しく、互換性の表で見つけるのは難しいと感じました。一番近いのはKangaxの「プロパティスプレッド」です。ですが、そのテストには同じ式の2つのスプレッドはありません(マージではありません)。また、プロポーザル/ドラフト/ブラウザのステータスページの名前はすべて「プロパティスプレッド」を使用していますが、「オブジェクトマージ」のスプレッド構文を使用するという提案の後にコミュニティが到達した「最初のプリンシパル」だったようです。(グーグルするのがなぜ難しいのかを説明しているかもしれません。)そこで、ここで私の発見を文書化して、他の人がこの特定の機能に関するリンクを表示、更新、およびコンパイルできるようにします。うまくいきますように。それが仕様とブラウザで着陸するというニュースを広めるのを手伝ってください。

最後に、この情報をコメントとして追加しましたが、著者の元の意図を壊さずに編集することはできませんでした。具体的には、@ RichardSchulteを修正する意図を失うことなく@ChillyPenguinのコメントを編集することはできません。しかし数年後、リチャードは正しいと判明しました(私の意見では)。だから私は代わりにこの答えを書いて、それが最終的に古い答えの牽引力を獲得することを願っています(結局、何年もかかるかもしれませんが、結局、それがロングテール効果のすべてです)。


3
「なぜ回答」セクションはおそらく必要ないでしょう
LocustHorde

@LocustHordeたぶん、2番目の段落(このトピックをグーグルで説明するのが難しい理由)を独自のセクションに移動できます。その後、残りはコメントに収まるかもしれません。
yzorg

8

注:Spreadは、Object.assignに関する単なる構文上の砂糖ではありません。彼らは舞台裏で大きく異なる動作をします。

Object.assignは新しいオブジェクトにセッターを適用しますが、Spreadは適用しません。さらに、オブジェクトは反復可能でなければなりません。

コピー オブジェクトの値が現時点で必要であり、その値にオブジェクトの他の所有者が行った変更を反映させたくない場合は、これを使用します。

オブジェクトの浅いコピーを作成するために使用して、常に不変のプロパティをコピーに設定します-変更可能なバージョンは不変のプロパティに渡すことができるため、コピーは常に不変オブジェクトを処理することを保証します

割り当て 割り当ては、コピーとは多少逆です。Assignは、値をコピーまたは保持するのではなく、インスタンス変数に直接値を割り当てるセッターを生成します。assignプロパティのゲッターを呼び出すと、実際のデータへの参照が返されます。


3
なぜ「コピー」と表示されているのですか?これらの大胆な見出しは何ですか。これを読んだとき、いくつかのコンテキストを見逃したような気がします...
ADJenks

2

他の回答は古く、良い回答を得ることができませんでした。
以下の例はオブジェクトリテラルの例であり、両方が互いに補完し合う方法と、互いに補完できない方法(したがって、違い)を示しています。

var obj1 = { a: 1,  b: { b1: 1, b2: 'b2value', b3: 'b3value' } };

// overwrite parts of b key
var obj2 = {
      b: {
        ...obj1.b,
        b1: 2
      }
};
var res2 = Object.assign({}, obj1, obj2); // b2,b3 keys still exist
document.write('res2: ', JSON.stringify (res2), '<br>');
// Output:
// res2: {"a":1,"b":{"b1":2,"b2":"b2value","b3":"b3value"}}  // NOTE: b2,b3 still exists

// overwrite whole of b key
var obj3 = {
      b: {
        b1: 2
      }
};
var res3 = Object.assign({}, obj1, obj3); // b2,b3 keys are lost
document.write('res3: ', JSON.stringify (res3), '<br>');
// Output:
  // res3: {"a":1,"b":{"b1":2}}  // NOTE: b2,b3 values are lost

ここにもいくつかの小さな例があり、配列とオブジェクトも含まれています:https :
//developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax


1

これは現在ES6の一部であり、標準化されており、MDNでも文書化されています。 ます。https //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

使用するのは非常に便利で、オブジェクトの構造化と並んで非常に理にかなっています。

上記の残りの利点の1つは、Object.assign()の動的な機能ですが、これは、リテラルオブジェクト内に配列を分散するのと同じくらい簡単です。コンパイルされたバベル出力では、Object.assign()で示されているものをそのまま使用します

したがって、オブジェクトスプレッドは標準化され、広く使用されており(react、reduxなどを参照)、使いやすく、Object.assign()のすべての機能を備えているため、正しい答えはオブジェクトスプレッドを使用することです。


2
いいえ、それはES6の一部ではありません。指定したリンクは、配列でのspreadオペレーターの使用のみを参照しています。JMMの回答で説明されているように、オブジェクトでのスプレッドオペレーターの使用は現在、ステージ2(つまり、ドラフト)の提案です。
チリーペンギン2016

1
完全に誤った情報に基づいた答えを見たことはないと思います。1年後でも、ES仕様の場合、それは一部ではなく、ほとんどの環境でサポートされていません。
2017

チリーとスリーは間違っていることが判明した、リチャードは正しい。ブラウザのサポートとツールのサポートはすべて着陸していますが、Richardの回答から1.5年かかりました。2018
。– yzorg

0

Object.assignを使用する必要がある場合は、この簡単な例を追加します。

class SomeClass {
  constructor() {
    this.someValue = 'some value';
  }

  someMethod() {
    console.log('some action');
  }
}


const objectAssign = Object.assign(new SomeClass(), {});
objectAssign.someValue; // ok
objectAssign.someMethod(); // ok

const spread = {...new SomeClass()};
spread.someValue; // ok
spread.someMethod(); // there is no methods of SomeClass!

JavaScriptを使用すると、はっきりしない場合があります。TypeScriptを使用すると、クラスのインスタンスを作成する方が簡単です。

const spread: SomeClass = {...new SomeClass()} // Error
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.