web-dev-qa-db-ja.com

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

options変数があり、デフォルト値を設定したいとしましょう。

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

オブジェクトスプレッドを使用する

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

またはObject.assignを使用する

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

これが commit です。

295

これは必ずしも徹底的ではありません。

スプレッド構文

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

利点:

  • ネイティブサポートのない環境で実行するためのコードをオーサリングする場合は、(polyfillを使用するのではなく)この構文をコンパイルすることしかできないかもしれません。 (たとえば、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を含まなければならないことを好みました。

258
JMM

参照対象については、休息/スプレッドは段階4としてECMAScript 2018で最終決定される - 提案は ここで 見つけられることができる。

ほとんどの場合、オブジェクトのリセットと拡散は同じように機能しますが、主な違いは spreadはプロパティを定義し、Object.assign()はプロパティを設定します これは、Object.assign()が設定メソッドを起動することを意味します。

これ以外にも、object rest/spread 1:1はObject.assign()にマップされ、array(iterable)spreadとは異なる動作をすることを覚えておく価値があります。たとえば、配列を拡散するとき、null値が拡散されます。ただし、オブジェクトスプレッドを使用すると、null値は何もしないで静かに広げられます。

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

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

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

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

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}
120
tomhughes

スプレッド演算子とObject.assignの間の大きな違いの1つは、現在の答えでは言及されていないようですが、スプレッド演算子がプロトタイプを損なわないことです。オブジェクトにプロパティを追加したいが、それがインスタンスであるものを変更したくないのであれば、Object.assignを使う必要があります。以下の例はこれを実証するはずです。

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

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

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

オブジェクトスプレッド演算子(...)はブラウザでは機能しません。これはまだES仕様の一部ではないため、単なる提案です。唯一の選択肢は、それをBabel(または同様のもの)でコンパイルすることです。

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

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

  • Object.assignはほとんどのブラウザで動作します(コンパイルなし)。
  • オブジェクトの...は標準化されていません
  • ...は、誤ってオブジェクトを変更してしまうことからあなたを保護します
  • ...はそれなしでブラウザでObject.assignをpolyfillします
  • ...は同じ考えを表現するのに必要なコードが少なくてすみます
12
Karthick Kumar

他の人が言っているように、この文書を書いている時点では、Object.assign()はpolyfillを必要とし、オブジェクトスプレッド...は動作するためにある程度のtranspiling(そしておそらくpolyfillも)を必要とします。

このコードを考えてください:

// 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()は実際には標準化されていますが、オブジェクトスプレッドとしての...はまだ未定です。唯一の問題は前者と将来のブラウザサポートです。

こちらのコードで遊んでください

お役に立てれば。

11

私は、「スプレッドオブジェクトマージ」ES機能の状況をブラウザで、そしてツールを介してエコシステムで要約したいと思います。

Spec

ブラウザ:Chrome、SF、Firefoxの間もなく(ver 60、IIUC)

  • 「スプレッドプロパティ」のブラウザサポート Chrome 60で出荷 、このシナリオを含む。
  • このシナリオのサポートは現在のFirefox(59)では機能しませんが、DOESは私のFirefox Developer Editionで機能します。だから私はそれがFirefox 60で出荷されると信じています。
  • Safari:テストされていませんが、KangaxはDesktop Safari 11.1では動作しますが、SF 11では動作しません。
  • iOS Safari:テストされていないが、KangaxによるとiOS 11.3では動作するがiOS 11では動作しない
  • まだEdgeにはありません

ツール:ノード8.7、TS 2.1

  • NodeJS 8.7 以来サポートされています(Kangax経由)。私がローカルでテストしたときに9.8で確認されました。
  • TypeScript 2.1 からサポートされています。現在は2.8です。

リンク集

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

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 years を書いています。しかし、私は全く同じ質問をしていました、そしてこれはGoogleが私に送ったところです。私はロングテールを改善するというSOの任務の奴隷です。

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

最後に、この情報をコメントとして追加したはずですが、著者の当初の意図を損なわずにそれらを編集することはできませんでした。具体的には、@ ChicPenguinのコメントを編集して、@ RichardSchulteを修正するという意図を失うことはできません。しかし数年後、リチャードは(私の意見では)正しいことがわかりました。それで、私は代わりにこの答えを書きます、それが結局古い答えで牽引力を得ることを望んでいます(何年もかかるかもしれませんが、結局それがlong tail効果についてのすべてであるものです)。

9
yzorg

注:SpreadはObject.assignを囲む単なる構文上の糖ではありません。それらは舞台裏ではかなり異なった働きをします。

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

コピー 現時点でオブジェクトの値が必要で、その値にそのオブジェクトの他の所有者による変更が反映されないようにするには、これを使用します。

オブジェクトのシャローコピーを作成するためにこれを使用して、常にimmutableプロパティをcopyに設定します - 可変バージョンはimmutableプロパティに渡すことができるので、copyは常にimmutableオブジェクトを扱うようにします

割り当て 割り当てはコピーとは反対です。代入は、値をコピーまたは保持するのではなく、インスタンス変数に直接代入するセッターを生成します。割り当てプロパティの取得メソッドを呼び出すと、実際のデータへの参照が返されます。

7
Charles Owen

他の答えは古く、良い答えを得ることができませんでした。
以下の例はオブジェクトリテラルのためのもので、両者がどのようにして互いに補完することができ、またどのようにして互いを補完することができないか(したがって違い)を助けます。

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

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

それは使用するのが非常に便利で、オブジェクトの破壊と並んでとても意味があります。

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

そのため、正解は、標準化され、広く使用されているので(react、reduxなどを参照)、使いやすく、Object.assign()のすべての機能を備えているので、object spreadを使用することです。

0
Rikki Schulte