欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jQuery.extend()的實現(xiàn)方式詳解及實例

 更新時間:2013年06月29日 15:52:24   作者:  
extend()函數(shù)是jQuery的基礎(chǔ)函數(shù)之一,作用是擴(kuò)展現(xiàn)有的對象

復(fù)制代碼 代碼如下:

<script type="text/javascript" src="jquery-1.5.2.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };

$.extend(true, obj1, obj2);

alert(obj1.x.xxx);  // 得到"xxx"

obj2.x.xxx = 'zzz';
alert(obj2.x.xxx);  // 得到"zzz"
alert(obj1.x.xxx);  // 得帶"xxx"
</script>


$.extend(true, obj1, obj2)表示以obj2中的屬性擴(kuò)展對象obj1,第一個參數(shù)設(shè)為true表示深復(fù)制。
雖然obj1中原來沒有"x"屬性,但經(jīng)過擴(kuò)展后,obj1不但具有了"x"屬性,而且對obj2中的"x"屬性的修改也不會影響到obj1中"x"屬性的值,這就是所謂的“深復(fù)制”了。

淺復(fù)制的實現(xiàn)

如果僅僅需要實現(xiàn)淺復(fù)制,可以采用類似下面的寫法:

復(fù)制代碼 代碼如下:

$ = {
     extend : function(target, options) {
        for (name in options) {
            target[name] = options[name];
        }
        return target;
    }
};

也就是簡單地將options中的屬性復(fù)制到target中。我們?nèi)匀豢梢杂妙愃频拇a進(jìn)行測試,但得到的結(jié)果有所不同(假設(shè)我們的js命名為“jquery-extend.js”):
復(fù)制代碼 代碼如下:

<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };
$.extend(obj1, obj2);
alert(obj1.x.xxx);  // 得到"xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx);  // 得到"zzz"
alert(obj1.x.xxx);  // 得帶"zzz"
</script>

obj1中具有了"x"屬性,但這個屬性是一個對象,對obj2中的"x"的修改也會影響到obj1,這可能會帶來難以發(fā)現(xiàn)的錯誤。

深復(fù)制的實現(xiàn)

如果我們希望實現(xiàn)“深復(fù)制”,當(dāng)所復(fù)制的對象是數(shù)組或者對象時,就應(yīng)該遞歸調(diào)用extend。如下代碼是“深復(fù)制”的簡單實現(xiàn):

復(fù)制代碼 代碼如下:

$ = {
 extend : function(deep, target, options) {
  for (name in options) {
   copy = options[name];
   if (deep && copy instanceof Array) {
                target[name] = $.extend(deep, [], copy);
            } else if (deep && copy instanceof Object) {
                target[name] = $.extend(deep, {}, copy);
   } else {
    target[name] = options[name];
   }
  }
  return target;
 }
};

具體分為三種情況:
1. 屬性是數(shù)組時,則將target[name]初始化為空數(shù)組,然后遞歸調(diào)用extend;
2. 屬性是對象時,則將target[name]初始化為空對象,然后遞歸調(diào)用extend;
3. 否則,直接復(fù)制屬性。

測試代碼如下:

復(fù)制代碼 代碼如下:

<script type="text/javascript" src="jquery-extend.js"></script>
<script>
obj1 = { a : 'a', b : 'b' };
obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };
$.extend(true, obj1, obj2);
alert(obj1.x.xxx);  // 得到"xxx"
obj2.x.xxx = 'zzz';
alert(obj2.x.xxx); // 得到"zzz"
alert(obj1.x.xxx); // 得到"xxx"
</script>

現(xiàn)在如果指定為深復(fù)制的話,對obj2的修改將不會對obj1產(chǎn)生影響了;不過這個代碼還存在一些問題,比如“instanceof Array”在IE5中可能存在不兼容的情況。jQuery中的實現(xiàn)實際上會更復(fù)雜一些。

更完整的實現(xiàn)

下面的實現(xiàn)與jQuery中的extend()會更接近一些:

復(fù)制代碼 代碼如下:

$ = function() {
    var copyIsArray,
        toString = Object.prototype.toString,
        hasOwn = Object.prototype.hasOwnProperty;

    class2type = {
        '[object Boolean]' : 'boolean',
        '[object Number]' : 'number',
        '[object String]' : 'string',
        '[object Function]' : 'function',
        '[object Array]' : 'array',
        '[object Date]' : 'date',
        '[object RegExp]' : 'regExp',
        '[object Object]' : 'object'
    },

    type = function(obj) {
        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
    },

    isWindow = function(obj) {
        return obj && typeof obj === "object" && "setInterval" in obj;
    },

    isArray = Array.isArray || function(obj) {
        return type(obj) === "array";
    },

    isPlainObject = function(obj) {
        if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
            return false;
        }

        if (obj.constructor && !hasOwn.call(obj, "constructor")
                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
            return false;
        }

        var key;
        for (key in obj) {
        }

        return key === undefined || hasOwn.call(obj, key);
    },

    extend = function(deep, target, options) {
        for (name in options) {
            src = target[name];
            copy = options[name];

            if (target === copy) { continue; }

            if (deep && copy
                    && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) {
                if (copyIsArray) {
                    copyIsArray = false;
                    clone = src && isArray(src) ? src : [];

                } else {
                    clone = src && isPlainObject(src) ? src : {};
                }

                target[name] = extend(deep, clone, copy);
            } else if (copy !== undefined) {
                target[name] = copy;
            }
        }

        return target;
    };

    return { extend : extend };
}();


首先是 $ =  function(){...}();這種寫法,可以理解為與下面的寫法類似:
復(fù)制代碼 代碼如下:

func = function(){...};
$ =  func();

也就是立即執(zhí)行函數(shù),并將結(jié)果賦給$。這種寫法可以利用function來管理作用域,避免局部變量或局部函數(shù)影響全局域。另外,我們只希望使用者調(diào)用$.extend(),而將內(nèi)部實現(xiàn)的函數(shù)隱藏,因此最終返回的對象中只包含extend:
復(fù)制代碼 代碼如下:

return { extend : extend };

接下來,我們看看extend函數(shù)與之前的區(qū)別,首先是多了這句話:
復(fù)制代碼 代碼如下:

if (target === copy) { continue; }

這是為了避免無限循環(huán),要復(fù)制的屬性copy與target相同的話,也就是將“自己”復(fù)制為“自己的屬性”,可能導(dǎo)致不可預(yù)料的循環(huán)。

然后是判斷對象是否為數(shù)組的方式:

復(fù)制代碼 代碼如下:

   type = function(obj) {
        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
   },
   isArray = Array.isArray || function(obj) {
        return type(obj) === "array";
    }

如果瀏覽器有內(nèi)置的Array.isArray 實現(xiàn),就使用瀏覽器自身的實現(xiàn)方式,否則將對象轉(zhuǎn)為String,看是否為"[object Array]"。

最后逐句地看看isPlainObject的實現(xiàn):

復(fù)制代碼 代碼如下:

if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
    return false;
}

如果定義了obj.nodeType,表示這是一個DOM元素;這句代碼表示以下四種情況不進(jìn)行深復(fù)制:
1. 對象為undefined;
2. 轉(zhuǎn)為String時不是"[object Object]";
3. obj是一個DOM元素;
4. obj是window。
之所以不對DOM元素和window進(jìn)行深復(fù)制,可能是因為它們包含的屬性太多了;尤其是window對象,所有在全局域聲明的變量都會是其屬性,更不用說內(nèi)置的屬性了。

接下來是與構(gòu)造函數(shù)相關(guān)的測試:

復(fù)制代碼 代碼如下:

  if (obj.constructor && !hasOwn.call(obj, "constructor")
                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
        return false;
    }

如果對象具有構(gòu)造函數(shù),但卻不是自身的屬性,說明這個構(gòu)造函數(shù)是通過prototye繼承來的,這種情況也不進(jìn)行深復(fù)制。這一點可以結(jié)合下面的代碼結(jié)合進(jìn)行理解:
復(fù)制代碼 代碼如下:

        var key;
        for (key in obj) {
        }
        return key === undefined || hasOwn.call(obj, key);

這幾句代碼是用于檢查對象的屬性是否都是自身的,因為遍歷對象屬性時,會先從自身的屬性開始遍歷,所以只需要檢查最后的屬性是否是自身的就可以了。
這說明如果對象是通過prototype方式繼承了構(gòu)造函數(shù)或者屬性,則不對該對象進(jìn)行深復(fù)制;這可能也是考慮到這類對象可能比較復(fù)雜,為了避免引入不確定的因素或者為復(fù)制大量屬性而花費大量時間而進(jìn)行的處理,從函數(shù)名也可以看出來,進(jìn)行深復(fù)制的只有"PlainObject"。
如果我們用如下代碼進(jìn)行測試:
復(fù)制代碼 代碼如下:

<script type="text/javascript" src="jquery-1.5.2.js"></script>
<script>
function O() {
 this.yyy = 'yyy';
}

function X() {
 this.xxx = 'xxx';
}

X.prototype = new O();

x = new X();

obj1 = { a : 'a', b : 'b' };
obj2 = { x : x };
$.extend(true, obj1, obj2);

alert(obj1.x.yyy);  // 得到"xxx"
obj2.x.yyy = 'zzz';
alert(obj1.x.yyy);  // 得到"zzz"
</script>


可以看到,這種情況是不進(jìn)行深復(fù)制的。
總之,jQuery中的extend()的實現(xiàn)方式,考慮了兼容瀏覽器的兼容,避免性能過低,和避免引入不可預(yù)料的錯誤等因素。

相關(guān)文章

最新評論