deepClone, extend, 深克隆物件和Object.assign(ES6)

十方魔發表於2017-10-27

在看 you might not need jQuery的時候(外網打不開,這是個部落格園轉過來的),看到了$.extend函式來拷貝某個物件。測試了一下原文程式碼,居然複製不到深層的。原始碼如下:

var deepExtend = function(out) {
  out = out || {};//問題就出在這裡!

  for (var i = 1; i < arguments.length; i++) {
    var obj = arguments[i];

    if (!obj)
      continue;

    for (var key in obj) {
      if (obj.hasOwnProperty(key)) {
        if (typeof obj[key] === 'object')
          deepExtend(out[key], obj[key]);
        else
          out[key] = obj[key];
      }
    }
  }

  return out;
};

deepExtend({}, objA, objB);

這裡雖然直接對out賦值了,但可能出現的情況是,原始的target物件容器中(第一個引數,即Out),out[key]為undefined,那麼如果確定out[key]的型別為物件的情況下,才是指向同一個物件,否則並沒有改變out[key]的值。

修改程式碼如下,將這一步提到前面,同時潤色程式碼增加報錯資訊:

function extend(out){

    if(!out){

        console.error('where is your container ?')
    }

    var objs = [].slice.call(arguments,1)

    if(objs.length > 0){

        objs.forEach(function(item,index){

            if(typeof item !== 'object'){

                console.error('item' + index + ' is no valid arguments, expected to be object')

            }

            else {

                for(var key in item){

                    if(item.hasOwnProperty(key)){

                        if(typeof item[key] === 'object'){

                            out[key]= out[key] || {}  // 這步是最重要的!

                            extend(out[key],item[key])

                        }

                        else{

                            out[key] = item[key]
                        }
                    }

                }

            }

        })

    }

    else{

        console.error('no objs to be copy')
    }

    return out;
}

var output = extend({a:{c:2},f:'fuck'},{a:1},{
    b:{
        lala:'name'
    }
})

這樣就可以達到深克隆的目的了。當然除了JQuery, zepto、 Lodash、underScore、Prototype等庫都實現了這個工具。ES6中的Object.assign也原生提供了相應的功能。

Object.assign() 方法值能複製可列舉、本身的屬性(enumerable and own properties), 它用在引用源上使用get方法,在目標容器上使用set方法,引發了getters、setters。因此,如果在融合的源物件含有getter的話,會不適用。如果要複製屬性的定義,包括其可列舉性,應該使用Object.getOwnPropertyDescriptor()和Object.defineProperty()。

對深克隆,不可使用Object.assign() 方法,因為它只會複製物件的引用而不是新建一個物件。最簡單的深克隆方法某過於此,但只能複製可列舉型別的。

let target = JSON.parse(JSON.stringify(source))

因此,對深克隆,最好還是用extend方式層層遞迴,直到把源物件的屬性拆到不是物件而是plain value為止。

相關文章