• Ext 源码笔记 Ext.apply Ext.Object.merge Ext.clone


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

     前两天写Ext的时候,碰到对象引用的问题,本想Ext有自己的拷贝对象的方法,Ext.apply(),那就用呗~~ 然,问题依然存在啊。于是,猜想:Ext.apply不能拷贝深层对象,深层对象依然是引用。

    先看源码:

      /**
         * 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  这是什么呢? 看源码:

       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方法。

    下面我们再看下Ext.Object.merge

       /** @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(),

     /**
             * 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(),这个方法只拷贝源对象中存在,目标对象中不存在的属性。

           /**
             * 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复制对象的过程已然明了。花花再也不用担心对象引用的问题啦!

    -----------------------------一花开五叶 结果自然成-------------------------------------------------
  • 相关阅读:
    python多线程
    python网络编程-udp/tcp通信
    通达OA未授权任意文件上传及文件包含导致远程代码执行漏洞
    星盟安全awd复盘20200314
    Axublog1.1.0代码审计
    回调函数绕过D盾小套路
    php7.1后webshell免杀的去路
    GO语言 特性概要
    Openstack 笔记概要
    大数据--基本理论知识(1)
  • 原文地址:https://www.cnblogs.com/zyc-undefined/p/3323705.html
Copyright © 2020-2023  润新知