ES6中Object.assign() 方法


ES6中Object.assign() 方法

1. 對象合並
Object.assign 方法用於對象的合並,將源對象(source)的所有可枚舉屬性,復制到目標對象上。
如下代碼演示:

var target = {a: 0};
var source1 = {b: 1};
var source2 = {c: 2};

Object.assign(target, source1, source2);
console.log(target);  // 輸出 {a: 0, b: 1, c: 2}

1-1 如果目標對象與源對象有同名屬性,或多個源對象有同名屬性,則后面的屬性會覆蓋前面的屬性。
如下代碼:

var target = {a: 0, b: 1};
var source1 = {a:1, b: 2, c:3};
var source2 = {c:5};

Object.assign(target, source1, source2);

console.log(target); // 輸出 {a: 1, b: 2, c: 5}

1-2 如果只有一個target(目標對象),Object.assign會直接返回該對象,如下代碼:

var o = {a: 0};
Object.assign(o);
console.log(o);  // {a: 0}

1-3 如果該參數不是對象,則會先轉成對象,然后返回。
先是這樣的,正常的返回是number類型。

var a = 1;
Object.assign(a);
console.log(a); // 1
console.log(typeof a); // number

然后直接判斷類型,返回的是object類型

console.log(typeof Object.assign(2)) // object

1-4 對於null, undefined 來說 無法轉換成Object,就會在控制台下報錯,如下代碼:

Object.assign(null);  // 報錯
Object.assign(undefined);  // 報錯

1-5 對象合並,如果源對象是null的話或者是undefined的話,那么對象合並的時候不會報錯,直接會跳過該對象的合並,直接返回目標對象。
如下代碼:

var obj = {a: 1};
console.log(Object.assign(obj, null) === obj); // true
console.log(obj); // {a: 1}

var obj = {a: 1};
console.log(Object.assign(obj, undefined) === obj); // true
console.log(obj); // {a: 1}

1-6 如果是數值,布爾型,和字符串合並對象的話,都不會報錯,但是字符串會以數組的形式表現。
先看數值合並對象如下代碼:

var obj = {a: 1};
console.log(Object.assign(obj, 12) === obj); // true
console.log(obj); // {a: 1}

布爾型合並對象如下代碼:

var obj = {a: 1};
console.log(Object.assign(obj, true) === obj); // true
console.log(obj); // {a: 1}

字符串合並對象如下代碼:

var obj = {a: 1};
console.log(Object.assign(obj, "bcd") === obj); // true
console.log(obj); // {0: 'b', 1: 'c', 2: 'd', a: 1}

如上代碼,只有字符串和對象合並,這是因為只有字符串有包裝對象,會產生可枚舉類型屬性。比如如下代碼:

console.log(Object('bcd')); // {0: "b", 1: "c", 2: "d", length: 3, [[PrimitiveValue]]: "bcd"}
console.log(Object(1111)); // {[[PrimitiveValue]]: 1111}
console.log(Object(true)); // {[[PrimitiveValue]]: true}

上面代碼可以看到原始值都在包裝對象的內部屬性[[PrimitiveValue]]上,這個屬性沒有被Object.assign合並,只有字符串的包裝對象會產生可枚舉的屬性,屬性則會被合並。
但是Object.assign合並的屬性是有限的,只合並對象的自身的屬性(不合並繼承屬性),也不合並不可枚舉的屬性。

2. Object.assign方法是淺拷貝

因此Object.assign方法是淺復制,不是深復制,也就是說,如果源對象某個屬性的值是對象,那么目標對象拷貝的是這個對象的引用。
比如如下代碼:

var o1 = {a: {b: 1} };
var o2 = Object.assign({}, o1);
o1.a.b = 2;
console.log(o2.a.b); // 2

如上代碼,o1是一個對象,該對象的屬性也是一個對象,使用Object.assign拷貝o1對象到o2上來,然后手動改變o1對象的屬性值,那么o2對象的屬性值也會被改變。

但是如果對象的屬性值不是一個對象的話,那么就不會影響該值,如下代碼:

var o1 = {a: 1};
var o2 = Object.assign({}, o1);
o1.a = 2;
console.log(o1); // {a: 2}
console.log(o2.a); // 1

但是如果源對象和目標對象有同名屬性的話,那么Object.assign會直接替換該屬性。比如如下代碼:

var target = {a: {b: 1}};
var source1 = {a: {b: 'hello'}};
Object.assign(target, source1);
console.log(target); // {a: {b: 'hello'}}

注意:Object.assign可以用來處理數組,但是會把數組視為對象。
也就是說對象里面有鍵值對索引,如果把兩個數組合並的話,那么得到不是合並后新增的數組,而是會把對應相同的鍵替換掉,如下使用數組的demo代碼如下:

var targetArrs = [1, 2, 3];
var sourceArrs = [4, 5];
Object.assign(targetArrs, sourceArrs);
console.log(targetArrs);  // [4, 5, 3]

如上代碼,目標對象有1,2,3屬性,源對象有4,5值,如果使用Object.assign的話,那么源對象的鍵4,5 和目標對象的1,2鍵是相同的,因此值直接替換掉。

3. Object.assign 常見使用在哪些地方?
    3-1 為對象添加屬性。比如如下代碼:

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

如上方法通過Object.assign方法,將x屬性和y屬性添加到A類的對象實列中。

   3-2 為對象添加方法

Object.assign(A.prototype, {
     xMethod(x, y) {
       ...
     },
     yMethod() {

     }
   });
 // 相當於如下代碼:
 A.prototype.xMethod = function(x, y) {};
 A.prototype.yMethod = function() {}

   3-3 克隆對象

function clone(obj) {
   return Object.assign({}, obj);
}

3-4 合並多個對象
如下一開始的代碼:

var target = {a: 0};
var source1 = {b: 1};
var source2 = {c: 2};
Object.assign(target, source1, source2);
console.log(target);  // 輸出 {a: 0, b: 1, c: 2}

4. 對象深度克隆
淺度克隆和深度克隆的含義如下:
淺度克隆: 原始類型為值傳遞,對象類型為引用傳遞。
深度克隆: 所有元素或屬性都是完全復制的,與原對象完全脫離,也就是說所有對於源對象的修改都不會反映到新對象中。反之,所有對於新對象的修改也不會反映到源對象中。
注意:Object.assign 是淺度克隆的。

4-1 ES5中我們可以通過遞歸的方式去調用函數來實現深度克隆。
代碼如下:

function deepClone(obj) {
  var newObj = obj instanceof Array ? [] : {};
  for (var i in obj) {
    newObj[i] = Object.prototype.toString.call(obj[i]) === "[object Object]" ? deepClone(obj[i]) : obj[i];
  }
  return newObj;
}
var obj = {a: {b: 1} };
var newObj = deepClone(obj);
console.log(newObj);  // 打印輸出 {a: {b: 1}}

// 修改對象 obj
obj.a.b = 2;
console.log(obj);   // 打印輸出 {a: {b: 2}}
console.log(newObj);  // 打印輸出 {a: {b: 1}} 原對象的修改不會影響到新對象中

如上面的代碼,在JS中,我們使用遞歸的方式,循環遍歷對象的所有屬性和方法,實現深度克隆,因此當我們深度克隆到新對象中的時候,再去更改源對象的屬性值的時候,不會影響到新對象。

對象和數組一起的深度克隆

function typeOf(obj) {
  const toString = Object.prototype.toString;
  const map = {
    '[object Boolean]': 'boolean',
    '[object Number]': 'number',
    '[object String]': 'string',
    '[object Function]': 'function',
    '[object Array]': 'array',
    '[object Date]': 'date',
    '[object RegExp]': 'regExp',
    '[object Undefined]': 'undefined',
    '[object Null]': 'null',
    '[object Object]': 'object'
  };
  return map[toString.call(obj)];
}
function deepCopy(data) {
  const t = this.typeOf(data);
  let o,
    i;
  if (t === 'array') {
    o = [];
  } else if (t === 'object') {
    o = {};
  } else {
    return data;
  }
  if (t === 'array') {
    for (let i = 0; i < data.length; i++) {
      o.push(this.deepCopy(data[i]));
    }
  } else if (t === 'object') {
    for (i in data) {
      o[i] = this.deepCopy(data[i]);
    }
  }
  return o;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM