JavaScriptは対象を深く理解する方法——Object.assign()


Object.assign()Object.assign()方法は、エニュメレート・属性のすべての値を1つまたは複数のソースオブジェクトから対象オブジェクトにコピーするために使用される.ターゲットオブジェクトに戻ります.
構文Object.assign(target, ...sources)パラメータ
  • ターゲットオブジェクト.
  • sourcesソースオブジェクト.
  • 戻り値
    ターゲット
    説明
    ターゲットオブジェクトの属性が同じキーである場合、属性はソースの属性によって覆われます.後のソースの属性は、前の属性を同様にカバーします.Object.assign方法は、ソースオブジェクト自身の、列挙可能な属性のみを対象オブジェクトにコピーすることができる.この方法は、ソースオブジェクトの[[Get]]とターゲットオブジェクトの[[Set]]を使用するので、相関gettersetterとを起動する.したがって、新しい属性をコピーまたは定義するだけでなく、属性を割り当てます.マージソースがgetterを含む場合、これは新しい属性をプロトタイプに統合するのに適していない可能性がある.属性定義(エニュメレーションを含む)をプロトタイプにコピーするためには、Object.getOwnPropertyDescriptor()およびObject.defineProperty() が使用されるべきである.StringタイプとSymbolタイプの属性はいずれもコピーされる.
    エラーが発生した場合、例えば、属性が書き込み不可の場合、TypeErrorを誘発し、エラーが発生する前に任意の属性を追加した場合、targetオブジェクトを変更することができる.Object.assignは、それらの値がnullまたはundefinedのソースオブジェクトをスキップすることができることに留意されたい.

    オブジェクトをコピー
    var obj = { a: 1 };
    var copy = Object.assign({}, obj);
    console.log(copy); // { a: 1 }
    問題を深くコピーする
    深度コピーには他の方法が必要です.Object.assign()コピーは属性値です.ソースオブジェクトの属性値がオブジェクトへの参照であれば、その参照値だけをコピーします.
    function test() {
      'use strict';
    
      let obj1 = { a: 0 , b: { c: 0}};
      let obj2 = Object.assign({}, obj1);
      console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
      
      obj1.a = 1;
      console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
      console.log(JSON.stringify(obj2)); // { a: 0, b: { c: 0}}
      
      obj2.a = 2;
      console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 0}}
      console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 0}}
      
      obj2.b.c = 3;
      console.log(JSON.stringify(obj1)); // { a: 1, b: { c: 3}}
      console.log(JSON.stringify(obj2)); // { a: 2, b: { c: 3}}
      
      // Deep Clone
      obj1 = { a: 0 , b: { c: 0}};
      let obj3 = JSON.parse(JSON.stringify(obj1));
      obj1.a = 4;
      obj1.b.c = 4;
      console.log(JSON.stringify(obj3)); // { a: 0, b: { c: 0}}
    }
    
    test();
    オブジェクトを結合
    var o1 = { a: 1 };
    var o2 = { b: 2 };
    var o3 = { c: 3 };
    
    var obj = Object.assign(o1, o2, o3);
    console.log(obj); // { a: 1, b: 2, c: 3 }
    console.log(o1);  // { a: 1, b: 2, c: 3 },             。
    同じ属性を持つオブジェクトをマージします.
    var o1 = { a: 1, b: 1, c: 1 };
    var o2 = { b: 2, c: 2 };
    var o3 = { c: 3 };
    
    var obj = Object.assign({}, o1, o2, o3);
    console.log(obj); // { a: 1, b: 2, c: 3 }
    属性は、後続のパラメータで同じ属性を持つ他のオブジェクトによってカバーされます.
    var o1 = { a: 1 };
    var o2 = { [Symbol('foo')]: 2 };
    
    var obj = Object.assign({}, o1, o2);
    console.log(obj); // { a : 1, [Symbol("foo")]: 2 } (cf. bug 1207182 on Firefox)
    Object.getOwnPropertySymbols(obj); // [Symbol(foo)]
    属性の継承と非列挙属性はコピーできません.
    var obj = Object.create({foo: 1}, { // foo       。
        bar: {
            value: 2  // bar         。
        },
        baz: {
            value: 3,
            enumerable: true  // baz          。
        }
    });
    
    var copy = Object.assign({}, obj);
    console.log(copy); // { baz: 3 }
    オリジナルタイプは対象として包装されます.
    var v1 = "abc";
    var v2 = true;
    var v3 = 10;
    var v4 = Symbol("foo")
    
    var obj = Object.assign({}, v1, null, v2, undefined, v3, v4); 
    //         ,null   undefined     。
    //   ,                     。
    console.log(obj); // { "0": "a", "1": "b", "2": "c" }
    異常は後続のコピージョブを中断します.
    var target = Object.defineProperty({}, "foo", {
        value: 1,
        writable: false
    }); // target   foo         。
    
    Object.assign(target, {bar: 2}, {foo2: 3, foo: 3, foo3: 3}, {baz: 4});
    // TypeError: "foo" is read-only
    //                           。
    
    console.log(target.bar);  // 2,             。
    console.log(target.foo2); // 3,                    。
    console.log(target.foo);  // 1,         ,                   。
    console.log(target.foo3); // undefined,     assign       ,             。
    console.log(target.baz);  // undefined,               。
    コピーアクセス
    var obj = {
      foo: 1,
      get bar() {
        return 2;
      }
    };
    
    var copy = Object.assign({}, obj); 
    // { foo: 1, bar: 2 }
    // copy.bar    obj.bar getter       
    console.log(copy); 
    
    //                      
    function completeAssign(target, ...sources) {
      sources.forEach(source => {
        let descriptors = Object.keys(source).reduce((descriptors, key) => {
          descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
          return descriptors;
        }, {});
    
        // Object.assign           Symbols
        Object.getOwnPropertySymbols(source).forEach(sym => {
          let descriptor = Object.getOwnPropertyDescriptor(source, sym);
          if (descriptor.enumerable) {
            descriptors[sym] = descriptor;
          }
        });
        Object.defineProperties(target, descriptors);
      });
      return target;
    }
    
    var copy = completeAssign({}, obj);
    console.log(copy);
    // { foo:1, get bar() { return 2 } }
    Polyfill
    if (typeof Object.assign != 'function') {
      // Must be writable: true, enumerable: false, configurable: true
      Object.defineProperty(Object, "assign", {
        value: function assign(target, varArgs) { // .length of function is 2
          'use strict';
          if (target == null) { // TypeError if undefined or null
            throw new TypeError('Cannot convert undefined or null to object');
          }
    
          var to = Object(target);
    
          for (var index = 1; index < arguments.length; index++) {
            var nextSource = arguments[index];
    
            if (nextSource != null) { // Skip over if undefined or null
              for (var nextKey in nextSource) {
                // Avoid bugs when hasOwnProperty is shadowed
                if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                  to[nextKey] = nextSource[nextKey];
                }
              }
            }
          }
          return to;
        },
        writable: true,
        configurable: true
      });
    }