首頁  >  文章  >  web前端  >  jQuery.extend()的實作方式詳解及實例_javascript技巧

jQuery.extend()的實作方式詳解及實例_javascript技巧

WBOY
WBOY原創
2016-05-16 17:30:581119瀏覽

複製程式碼 程式碼如下:


<script><BR>obj1 = { a : 'a', b : 'b' };<BR>obj2 = {  x : { xxx : ' xxx', yyy : 'yyy' },  y : 'y' }; <P>$.extend(true, obj1, obj2); <P>alert(obj1.x.xxx);  // 得到"xxx" <P>obj2.x.xxx = 'zzz';<BR>alert(obj2.x.xxx);  // 得到"zzz"<BR>alert(obj1.x.xxx);  // 得帶"xxx" <BR></script>

$.extend(true, obj1, obj2)表示以obj2中的屬性擴展物件obj1,第一個參數設為true表示深複製。
雖然obj1中原來沒有"x"屬性,但經過擴展後,obj1不但具有了"x"屬性,而且對obj2中的"x"屬性的修改也不會影響到obj1中"x"屬性的值,這就是所謂的「深複製」了。

淺複製的實作

如果只需要實作淺複製,可以用類似下面的寫法:

複製程式碼 程式碼如下:

$ = {
     extend : function(target, options) {
       🎜>        }
return target;
    }
};


也就是單純地將options中的屬性複製到target中。我們仍然可以用類似的程式碼進行測試,但得到的結果有所不同(假設我們的js命名為「jquery-extend.js」):


複製程式碼 程式碼如下:
<script> <BR>obj1 = { a : 'a', b : 'b' };<BR>obj2 = {  x : { xxx : 'xxx', yyy : 'b' },  y : 'y' };<BR>$.extend(obj1, obj2);<BR>alert(obj1.x.xxx);  // 得到"xxx"<BR>obj2.x.xxx = 'zzz';<BR>alert(obj2.x. xxx);  // 得到"zzz"<BR>alert(obj1.x.xxx);  // 得帶"zzz"<BR></script>


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

深複製的實作

如果我們希望實現“深複製”,當複製的物件是數組或物件時,就應該遞歸呼叫extend。如下程式碼是「深複製」的簡單實作:


複製程式碼 程式碼如下:
程式碼如下:


= {
 extend : function(deep, target, options) {
  for (name in options) {
   copy = options[name];
  >                target[name] = $.extend(deep, [], copy);
                 target[name] = $.extend(deep, {}, copy );
   } else {
    target[name] = options[name];
   }
  }

具體分為三種情況:
1. 屬性是數組時,則將target[name]初始化為空數組,然後遞歸調用extend;
2. 屬性是物件時,則將target[ name]初始化為空對象,然後遞歸呼叫extend;
3. 否則,直接複製屬性。

測試程式碼如下:


複製程式碼

程式碼如下:<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); // 得到"zzz"
alert(obj1.x.xxx); // 得到"xxx"



現在如果指定為深複製的話,對obj2的修改將不會對obj1產生影響了;不過這個程式碼還存在一些問題,例如「instanceof Array」在IE5中可能存在不相容的情況。 jQuery中的實作其實會更複雜一些。

更完整的實作

下面的實作與jQuery中的extend()會比較接近一些:

複製程式碼 程式碼如下:

$ = function() {
    var copyIsArray,
        toString = Object.prototype.to
    class2type = {        '[物件布林值]' : '布林值',

        ',
        '[對象函數]' : '函數',
        '[對像數組]' : '數組',
        '[對像日期]' : '日期',
        '[對象正規表示式]' : 'regExp',
        '[object Object]' : 'object'
    },

    type = function(obj) {
        return obj == null ? String(obj) : 類2type[toString.call(obj)] ||

    isWindow = function(obj) {

        return obj && typeof obj === "object" && "setInterval" in obj;
 
    isArray = Array.isArray || function(obj) {

        return type(obj) === "array";

    return type(obj) === "array";
    },    isPlainObject = function(obj) {

        if (!obj || type(obj) !== "object" ||  

        }

        if (obj.constructor && !hasOwn.call(obj, "constructor")

          >            return false;

}

        var key;
        for (key in obj) {

        }


        回鍵 === 未定義 || hasOwn.call(obj, key);
    },

    extends = function(deep, target, options) {

        for (name in options) {
       copy = options[name];

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


            if (deep && copy

                            if (copyIsArray) {

                      克隆= src && isArray(src) ?原始碼:[];

                } else {
                          }

                target[name] = 延伸(深層、複製、複製);

                target[name] = copy;

            }
        }

        回目標;
    };

    return { extends :extend };
}();

首先是$ = function(){...}(); 這個寫法,可以理解為與下面類似的寫法:



複製程式碼


程式碼如下:



func = function(){.. .};

$ =  func();


也就是立即執行函數,並將結果賦給$。這種寫法可以利用function來管理作用域,避免局部變數或局部函數影響全域域。另外,我們只希望使用者呼叫$.extend(),而將內部實作的函數隱藏,因此最終傳回的物件中只包含extend:
複製程式碼 程式碼如下:

return { extend : extend };

接下來,我們來看看extend函數與之前的區別,首先是多了這句話:
複製程式碼 程式碼如下:

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

這是為了避免無限循環,要複製的屬性copy與target相同的話,也就是將“自己”複製為“自己的屬性”,可能導致不可預料的循環。

然後是判斷對像是否為數組的方式:

複製代碼 代碼如下:

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


如果瀏覽器有內建的Array.isArray 實作自己的實作方式,否則將物件轉為String,看是否為"[object Array]"。

最後逐句地看看isPlainObject的實現:


複製代碼 代碼如下:
if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
    return false;
}


如果
如果定義了obj.nodeType,表示這是一個DOM元素;這句程式碼表示以下四種情況不進行深複製:
1. 物件為undefined;
2. 轉為String時不是"[object Object] ";
3. obj是DOM元素;
4. obj是window。
之所以不對DOM元素和window進行深複製,可能是因為它們包含的屬性太多了;尤其是window對象,所有在全域域宣告的變數都會是其屬性,更不用說內建的屬性了。
接下來是與建構子相關的檢定:


複製程式碼 程式碼如下:

程式碼如下:



  if (obj.constructor && !hasOwn.call(obj, "constructor")
                 return false;
    }
如果物件具有建構函數,但卻不是自身的屬性,說明這個建構子是透過prototye繼承來的,這種情況也不進行深複製。這點可以結合下面的程式碼結合來理解: 複製程式碼

程式碼如下:


程式碼如下:
        for (key in obj) {
        }
        return key ===/是用來檢查物件的屬性是否都是自身的,因為遍歷物件屬性時,會先從自身的屬性開始遍歷,所以只需要檢查最後的屬性是否是自身的就可以了。
這說明如果物件是透過prototype方式繼承了建構子或屬性,則不對該物件進行深複製;這可能也是考慮到這類物件可能比較複雜,為了避免引入不確定的因素或為複製大量屬性而花費大量時間而進行的處理,從函數名稱也可以看出來,進行深複製的只有"PlainObject"。
如果我們用以下程式碼測試: 程式碼如下:


<script><br>function O() {<br> this.yyy = 'yyy';<br>} <p>関数 X() {<br> this.xxx = 'xxx';<br>}</p> <p>X.prototype = new O();</p> <p>x = new X();</p> <p>obj1 = { a : 'a', b : 'b' };<br>obj2 = { x : x };<br>$.extend(true, obj1, obj2);</p> <p>alert(obj1.x.yyy); // "xxx" を取得します<br>obj2.x.yyy = 'zzz';<br>alert(obj1.x.yyy) // "zzz" を取得します<br></script>


ご覧のとおり、この場合、ディープ コピーは実行されません。
つまり、jQuery での extend() の実装では、ブラウザとの互換性、パフォーマンスの低下の回避、予期しないエラーの発生の回避などの要素が考慮されます。
陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn