Ext 源碼筆記 Ext.apply Ext.Object.merge Ext.clone

 Ext.apply  &  Ext.Object.merge & Ext.clone

 前兩天寫Ext的時候,碰到對象引用的問題,本想Ext有本身的拷貝對象的方法,Ext.apply(),那就用唄~~ 然,問題依然存在啊。因而,猜測:Ext.apply不能拷貝深層對象,深層對象依然是引用。node

先看源碼:chrome

  /**
     * Copies all the properties of config to the specified object.
     * Note that if recursive merging and cloning without referencing the original objects / arrays is needed, use
     * {@link Ext.Object#merge} instead.
     * @param {Object} object The receiver of the properties
     * @param {Object} config The source of the properties
     * @param {Object} [defaults] A different object that will also be applied for default values
     * @return {Object} returns obj
     */
    Ext.apply = function(object, config, defaults) {
        if (defaults) {
            Ext.apply(object, defaults);
        }

        if (object && config && typeof config === 'object') {
            var i, j, k;

            for (i in config) {
                object[i] = config[i];   // 複製對象,這裏顯然是淺複製。
            }

            if (enumerables) {
                for (j = enumerables.length; j--;) {
                    k = enumerables[j];
                    if (config.hasOwnProperty(k)) {
                        object[k] = config[k];
                    }
                }
            }
        }

        return object;
    };

嗯,果真,人家已經寫的很清楚了:若是不想引用原對象或數組,可使用 Ext.Object.merge方法(仍是看源碼好啊!!),一下就找到問題的根源了。數組

源碼裏面還有個新鮮的玩意兒:enumerables  這是什麼呢? 看源碼:app

   enumerables = ['hasOwnProperty', 'valueOf', 'isPrototypeOf', 'propertyIsEnumerable',
                       'toLocaleString', 'toString', 'constructor'];

    /**
     * An array containing extra enumerables for old browsers
     * @property {String[]}
     */
    Ext.enumerables = enumerables;

'old browsers' 很明顯嘛,兼容ie6用的。可是若是還不明白爲何要兼容ie6,那就先用ie6測試一下吧,函數

var a = {
    years: [20013, 20012],
    toString: 'this year is 2013'
}

var b =  {};

Ext.apply(b, a);

if(b.hasOwnProperty('toString')){
     alert(1);  
}else{
     alert(2);
}

// 在ie6下,彈出 2
// ie7,ie8,ie9 及 chrome firefox彈出的都是 1
// 即 在ie6下,對象包含的像 toString 這樣的原生屬性不會被拷貝,也不能被 for in 遍歷到

 

在ie6下,複製對象後,不會將enumerables列舉的這些屬性複製到目標對象中,所以這裏須要手動添加!(萬惡的ie6啊)。測試

題外話,hasOwnProperty() 這個方法只會檢查該對象自己的屬性,不會檢查該對象的原型鏈中是否具備該屬性。若是須要檢查一個對象的原型鏈上是否包含了另外一個對象,可使用isPrototypeOf方法。this

 

下面咱們再看下Ext.Object.mergespa

   /** @param {Object} destination The object into which all subsequent objects are merged.
     * @param {Object...} object Any number of objects to merge into the destination.
     * @return {Object} merged The destination object with all passed objects merged in.
     */
   merge: function(destination) {
        var i = 1,
            ln = arguments.length,
            mergeFn = ExtObject.merge,  // ExtObject = Ext.Object 這裏指代 this
            cloneFn = Ext.clone,     // 真正幹活的函數
            object, key, value, sourceKey;

        for (; i < ln; i++) {
            object = arguments[i];

            for (key in object) {
                value = object[key];
                if (value && value.constructor === Object) {  // 判斷是不是深層對象
                    sourceKey = destination[key];
            // 判斷是複製仍是合併
if (sourceKey && sourceKey.constructor === Object) { mergeFn(sourceKey, value);  // 合併 遞歸 } else { destination[key] = cloneFn(value); // 複製 } } else { destination[key] = value; } } } return destination; }

其實,看到這裏,才發現原來真正幹活的是Ext.clone(),firefox

 /**
         * Clone simple variables including array, {}-like objects, DOM nodes and Date without keeping the old reference.
         * A reference for the object itself is returned if it's not a direct decendant of Object. For model cloning,
         * see {@link Ext.data.Model#copy Model.copy}.
         * 
         * @param {Object} item The variable to clone
         * @return {Object} clone
         */
        clone: function(item) {
            var type,
                i,
                j,
                k,
                clone,
                key;
            
            if (item === null || item === undefined) {
                return item;
            }

            // DOM nodes
            // TODO proxy this to Ext.Element.clone to handle automatic id attribute changing
            // recursively
            if (item.nodeType && item.cloneNode) {
                return item.cloneNode(true);
            }

            type = toString.call(item); // 這裏 toString = Object.prototype.toString 
// 這裏是一個細節的地方
       // String.prototype.toString 和 Array.prototype.toString 都重寫了 Object.prototype.toString 的方法
// So, 這兩個實際上是不同的方法.

// String.prototype.toString.call('str') -> 'str'
// Array.prototype.toString.call('str') -> '[object String]'
// Array.prototype.toString.call([1,2]) -> '1,2'
// Object.prototype.toString.call('str') -> '[object String]'  


// So 這個方法實際上是在變相的判斷數據類型 !!! 又是一個小技巧 MARK

// Date if (type === '[object Date]') { return new Date(item.getTime()); } // Array if (type === '[object Array]') { i = item.length; clone = []; while (i--) { clone[i] = Ext.clone(item[i]); } } // Object else if (type === '[object Object]' && item.constructor === Object) { clone = {}; for (key in item) { clone[key] = Ext.clone(item[key]); } if (enumerables) { for (j = enumerables.length; j--;) { k = enumerables[j]; clone[k] = item[k]; } } } return clone || item; }

Ext爲複製對象還提供了一個十分實用的函數 Ext.applyIf(),這個方法只拷貝源對象中存在,目標對象中不存在的屬性。prototype

       /**
         * Copies all the properties of config to object if they don't already exist.
         * @param {Object} object The receiver of the properties
         * @param {Object} config The source of the properties
         * @return {Object} returns obj
         */
        applyIf: function(object, config) {
            var property;

            if (object) {
                for (property in config) {
                    if (object[property] === undefined) {  //目標對象中不存在該屬性
                        object[property] = config[property];
                    }
                }
            }

            return object;
        }

 嗯,到這裏,整個Ext複製對象的過程已然明瞭。花花不再用擔憂對象引用的問題啦!

相關文章
相關標籤/搜索