对象的兼并及拷贝

Object.assign()

Object.assign() 要领用于对象的兼并,将一切本身的(非继续的)可罗列属性的值从一个或多个源对象拷贝到目的对象。返回目的对象。目的对象本身也会转变。

Object.assign(target, ...sources)
  • target: 目的对象。
  • sources: 源对象。

Object.assign() 兼并拷贝属性的限定

只拷贝源对象的本身属性(不拷贝继续属性),也不拷贝不可罗列的属性(enumerable: false)。

Object.assign({b: 'c'},
  Object.defineProperty({}, 'invisible', {
    enumerable: false,
    value: 'hello'
  })
)
// { b: 'c' }

参数范例

1. 只要一个参数

如果只要一个参数,Object.assign() 会直接返回该参数。

let obj = {a: 1};
Object.assign(obj) === obj // true

如果该参数不是对象,则会先转成对象,然后返回。

typeof Object.assign(2) // "object"

由于 undefinednull 没法转成对象,所以如果它们作为参数,就会报错。

Object.assign(undefined) // 报错
Object.assign(null)      // 报错

2. 对象 + 非对象

非对象参数都邑转成对象,如果没法转成对象,就会跳过,不会报错。

如果非对象参数为 undefinednull ,就会跳过,不会报错,返回的依旧是目的对象参数。

let obj = {a: 1};
Object.assign(obj, undefined) === obj    // true
Object.assign(obj, null) === obj         // true

如果非对象参数为其他范例的值(即数值、字符串和布尔值),也不会报错。然则,除了字符串会以数组情势拷贝入目的对象,其他值都不会发生结果。这是由于只要字符串的包装对象,会发生可罗列属性。

let v1 = 'abc';
let v2 = true;
let v3 = 10;

let obj = Object.assign({}, v1, v2, v3);
console.log(obj)  // { "0": "a", "1": "b", "2": "c" }

3. 目的对象 + 源对象…

(1) 属性值为 nullundefined 的属性会一般兼并

Object.assign() 不会跳过那些属性值为 nullundefined 的源对象。

var o1 = { a: null, b: 1};
var o2 = { c: undefined };
    
var obj = Object.assign({}, o1, o2);
obj   // {a: null, b: 1, c: undefined}
(2) 同名属性的替代

如果目的对象与源对象中的属性具有雷同的键,则目的对象属性将被源中的属性掩盖。厥后的源的属性将类似地掩盖新近的属性。

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);
obj    // { a: 1, b: 2, c: 3 }
(3) 浅拷贝

Object.assign() 要领执行的是浅拷贝,而不是深拷贝。拷贝的是属性值。如果源对象的属性值是一个指向对象的援用,它也只拷贝谁人援用值。

var obj1 = { a: 0 , b: { c: 0 } };
var obj2 = Object.assign({}, obj1);
obj2   // { a: 0, b: { c: 0 } };

obj2.b.c = 3;
obj1   // { a: 0, b: { c: 3 } };
obj2   // { a: 0, b: { c: 3 } };
(4) 数组的处置惩罚

Object.assign() 能够用来处置惩罚数组,然则会把数组视为键值为数组下标的对象来兼并,但是终究的返回情势也是数组。

Object.assign([1, 2, 3], [4, 5])  // [4, 5, 3]

Object.assign({0:1,1:2,2:3},{0:4,1:5})  // {0: 4, 1: 5, 2: 3}
(5) 存取器属性的处置惩罚

Object.assign() 如果碰到存取器定义的属性,会只拷贝值。

var obj = {
  foo: 1,
  get bar() { return 2; }
};

var copy = Object.assign({}, obj); 
copy  // { foo: 1, bar: 2 }

因而必需运用 Object.getOwnPropertyDescriptors() 要领合营 Object.defineProperties() 要领,就能够完成准确拷贝。但仅限于可拷贝 gettersetter ,关于属性的援用范例照样属于浅拷贝。


var obj = {
  foo: { a : 0 },
  get bar() { return 2; }
};
var target = Object.defineProperties({},
  Object.getOwnPropertyDescriptors(obj)
);
Object.getOwnPropertyDescriptor(target, 'bar')
// { get : ƒ bar(),
   set : undefined,
   enumerable : true, 
   configurable : true }
   
obj.foo.a = 6
target.foo.a   // 6

罕见用处

1. 为对象增加属性

class Point {
  constructor(x, y) {
    Object.assign(this, {x, y});
  }
}

上面要领经由过程 Object.assign() 要领,将 x 属性和 y 属性增加到 Point 类的对象实例。

2. 为对象增加要领

Object.assign(SomeClass.prototype, {
  someMethod(arg1, arg2) { ··· },
  anotherMethod() { ··· }
});

// 等同于下面的写法
SomeClass.prototype.someMethod = function (arg1, arg2) { ··· };
SomeClass.prototype.anotherMethod = function () { ··· };

3. 浅克隆对象

let obj = {a:5};
function clone(origin) {
  return Object.assign({}, origin);
}
let aaa = clone(obj);  // {a:5}

不过,采纳这类要领克隆,只能克隆原始对象本身的值,不能克隆它继续的值。如果想要坚持继续链,能够采纳下面的代码。

function clone(origin) {
  let originProto = Object.getPrototypeOf(origin);
  return Object.assign(Object.create(originProto), origin);
}

4. 兼并多个对象

let merge = (target, ...sources) => Object.assign(target, ...sources);

如果愿望兼并后返回一个新对象,能够改写上面函数,对一个空对象兼并。

let merge = (...sources) => Object.assign({}, ...sources);

5. 为属性指定默认值

const DEFAULTS = {
  a: 0,
  b: 'ccc'
};

function copy(options) {
  options = Object.assign({}, DEFAULTS, options);
  // ...
}

注重,由于存在浅拷贝的题目,DEFAULTS对象和options对象的一切属性的值,最好都是简朴范例,不要指向另一个对象。不然,DEFAULTS对象的该属性极可能不起作用。

参考链接:Object.assign()

深拷贝

1. JSON.parse(JSON.stringify(obj))

var obj1 = { a: 0 , b: { c: 0}};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj1.b.c = 4;
obj2    // { a: 0, b: { c: 0}}

但由于 JSON 的局限性,该要领也不是全能的。比方,如果对象的属性是 undefined、函数、symbolXML 对象,该属性会被 JSON.stringify() 过滤掉,致使拷贝时会缺乏属性。

let obj = {
  name:'dora',
  sayHello:function(){ console.log('Hello World'); }
}

let cloneObj = JSON.parse(JSON.stringify(obj));
console.log(cloneObj); // {name: "dora"}

2. 应用递归对每一层都从新建立对象并赋值从而完成深拷贝

function deepClone(source){
  let targetObj = source.constructor === Array ? [] : {}; 
  for(let keys in source){
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}

let obj = { a: { b: 1, c: 2 }, sayHello: function(){ console.log('Hello World'); } }
let cloneObj = deepClone(obj);

obj.a.b = 4
obj       // {a:{b: 4, c: 2},sayHello:ƒ ()}
cloneObj  // {a:{b: 1, c: 2},sayHello:ƒ ()}
    原文作者:Dora36
    原文地址: https://segmentfault.com/a/1190000017928737
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞