也來探討一下Object.assign

Object.assign 是什麼?

此處直接複製mdn文檔的內容以下:git

The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.

翻譯一下也就是:github

Object.assign()方法用於將全部可枚舉屬性的值從一個或多個源對象複製到目標對象。它會返回目標對象。

爲了便於理解,此處貼出mdn的對Object.assign的polyfillredux

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
  });
}

其中的Object構造函數爲給定的值建立一個對象包裹器。若是值爲null或undefined,它將建立並返回一個空對象,不然,它將返回一個Type對應於給定值的對象。若是該值已是一個對象,它將返回該值。
舉個栗子?數組

Object(1)
// Number {1}
Object('')
// String {"", length: 0}
Object(false)
// Boolean {false}

從polyfill的代碼不難看出,Object.assign 就是將所傳參數當中的對象的可枚舉屬性的值覆蓋到第一個對象上,那麼因爲js當中的object,array是引用類型,因此對與對象,數組的覆蓋其實只是覆蓋了對數組,對象的引用,也即 淺copy安全

mdn栗子來一枚函數

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 }

如何實現深copy?

來個redux 當中reducer 嵌套數據更新的栗子?prototype

add(state, { payload: todo }) {
      const todos = state.a.b.todos.concat(todo);
      const b = Object.assign({},state.a.b,{todos})
      const a = Object.assign({},state.a,{b});
      return Object({},state,{a});
    },

上面的栗子當中concat()方法用於合併兩個或多個數組。此方法不會更改現有的數組,而是返回一個新的數組。能夠理解爲利用concat方法建立了一個新的todos數組,這樣就能夠避免對數據的修改影響到了舊的todos數組,而後將新的todos數組使用Object.assign 給新的b,以此,僅僅實現了sate對象中將深層次的todos的一個‘深copy’。
可是若是state還有其餘的屬性的值爲對象或者數組,簡單的使用Object.assign 只是複製了一個引用。因此在寫reducer的時候須要儘可能避免state嵌套的太深,爲了安全,咱們可使用 updeep來更新數據,或者直接使用不可變數據,此處再也不多說,繼續探討Object.assign.翻譯

實現一個deepCopy?

前面探討了對象當中單個屬性值的深copy,可是若是有多個值,怎麼辦呢?一個一個手動找出來?固然不行啊,這樣一點兒也很差玩兒
爲了實現一個deepCopy,咱們先簡單瞭解一下js的數據類型:

值類型:數值、布爾值、null、undefined。

基本類型值是指在棧內存保存的簡單數據段,在複製基本類型值的時候,會開闢出一個新的內存空間,將值複製到新的內存空間,舉個栗子:code

var a = 1;
var b = a;
a = 2;
console.log(a);//輸出2;
console.log(b);//輸出1;

引用類型:對象、數組、函數等。

用類型值是保存在堆內存中的對象,變量保存的只是指向該內存的地址,在複製引用類型值的時候,其實只複製了指向該內存的地址,舉個栗子:對象

var a={b:1}
var a2 = a;
a2.b = 2;
console.log(a)  // 輸出 {b: 2}

deepCopy

瞭解了js的數組類型以後,那麼實現一個深copy其實主要就是解決引用類型的copy,數組和對象無限往下拆,最終其屬性也是值類型組成的。因此咱們可使用遞歸實現一個deepCopy,下面就直接貼代碼了,略囉嗦,歡迎?拍磚

function detectType(source) {
    return Object.prototype.toString
        .call(source)
        .split(/[\[,\s,\]]/)[2]
        .toLowerCase();
}
function deepCopy(source, copyDeep) {
    var type = detectType(source);
    if (!(type === 'object' || type === 'array')) {
        return source;
    }
    var newObject = type === 'array' ? source.slice() :Object.assign({}, source);
    if (!copyDeep) {
        return newObject;
    }
    Object.keys(newObject).forEach(function (key) {
        newObject[key] = deepCopy(newObject[key], true);
    });
    return newObject;
}
相關文章
相關標籤/搜索