1. 程式人生 > >jQuery.extend函式原始碼詳解

jQuery.extend函式原始碼詳解

測試程式碼1:

var x = { name:"CodePlayer", age: 20 };
var y = { age: 18 };
var z = { site: "www.365mini.com", age: 21  };
var obj = $.extend( x, y, z );
這種呼叫下,target就是arguments[0]也就是x物件
isArray函式原始碼分析:
isArray: Array.isArray || function( obj ) {
return jQuery.type(obj) === "array";
},
測試程式碼2:
$(document).ready(function(){
var x = { name:"CodePlayer", students:[1,2,3] };
var y = { name: "xxx",students:[3,4,5]};
alert(jQuery.extend(true,x,y).students);//返回3,4,5,因為兩個students的鍵名是一樣的都是陣列下標,所以會覆蓋!
//迭代x,y時候都會迭代出來students集合,然後y的studenets就是isArray返回true
//這時候target就是x物件,
});
測試程式碼3:
 var x = [1,2,3];
var y = [3,4,5];
alert(jQuery.extend(true,x,y));//輸出3,4,5
因為上面的for..in迴圈每次迭代出來的name是0,1,2也就是陣列的下標,所以會後面的不斷覆蓋前面的!當物件中出現了陣列屬性等時候就會是這種情況和測試程式碼2是一樣的道理!
測試程式碼4:
var x = {name:"qinliang",age:12,length:2}
var y = {name:"xxx",age:23,sex:"female",length:3}
var result=jQuery.extend(true,x,y);
for(var name in result)
{
 alert(name+"->"+result[name]);//會列印sex
}
它是以options也就是第二個y迭代的,x作為target,所以當把name,age,length迭代完了以後還要跌倒y的sex屬性。src=target[name],copy=options[name],name是sex,這時候copy就不是undefined,而是female,這時候直接封裝到target上面就可以了!
測試程式碼5:
var x = [1,2,3]
var y = [7,8,9,10]
var result=jQuery.extend(true,x,y);
for(var name in result)
{
 alert(name+"->"+result[name]);//輸出7.8.9.10因為for in迭代出來的name是0,1,2,3,前面三個下標被覆蓋,後面一個下標直接新增到target中!
}
測試程式碼6:
 var x = { 
sayHi: function(){
   alert("這是新增的測試方法");
}
};
// 只有一個引數,則表示省略target引數,target引數預設為jQuery物件本身
var obj = $.extend( x );
alert( obj === $ ); // true
$.sayHi(); // 這是新增的測試方法
//target就是作為基物件用來擴充套件屬性和方法的,如果沒有第一個boolean引數,那麼就是第一個引數否則為第二個引數
var x = { name:"CodePlayer", age: 20 };
var y = { age: 18 };
var z = { site: "www.365mini.com", age: 21  };
var obj = $.extend( x, y, z );
alert( obj === x ); // true
});

測試程式碼7:

如果target不是一個物件,同時target也不是一個函式,那麼把target設為空物件{}
alert($.extend(1,2));第一個不是boolean,target就是1,但是1不是object也就是typeof 1不是[object Object]
alert(typeof 1);是number
jQuery原始碼分析:
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
//獲取第一個引數
target = arguments[0] || {},
i = 1,
//獲取引數的個數
length = arguments.length,
//預設是淺複製
deep = false;
// Handle a deep copy situation
//如果第一個引數是boolean型別,那麼把該引數賦值給區域性變數deep
if ( typeof target === "boolean" ) {
deep = target;
// skip the boolean and the target
//target預設儲存的是第一個引數,如果第一個引數是boolean,那麼直接儲存為第二個引數,只是為了匹配呼叫方式一和呼叫方式二的!
target = arguments[ i ] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
// extend jQuery itself if only one argument is passed
//如果只有一個引數,這時候i就是1,length也就是1,那麼把target設定為呼叫者,也就是jQuery物件本身!同時把i遞減為0
//alert($.extend(1));
if ( i === length ) {
target = this;//這裡this就是jQuery,因為是jQuery呼叫的!
i--;
}
//迴圈傳遞進來的引數集合
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
//獲取逐個引數
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
//把每一個object型別引數的屬性迭代出來
for ( name in options ) {
src = target[ name ];
//獲取當前迭代的options的同名屬性值,因為options是一直變化迭代出來的物件,但是target就要返回的物件,所以他是“靜態的”
copy = options[ name ];
// Prevent never-ending loop
//如果原有屬性和現在迭代出來的屬性一樣,如上面例子中x和y,z都有age屬性!如果相同那麼繼續下面的迴圈,例如如果age都是等於18就繼續下一次迴圈!
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
//copy是當前迭代出來的屬性值,deep是否表示深度克隆,如果當前迭代出來的還是{}或者new Object構造的物件或者是Array型別或者類陣列型別
//那麼表示要深度克隆
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) )
//如果是類陣列物件,[1,2,3]
if ( copyIsArray ) {
copyIsArray = false;
//如果target原來的name屬性是Array,那麼clone就是原來的Array
//下面呼叫target[ name ] = jQuery.extend( deep, clone, copy );
//也就是說會把clone作為target繼續遞迴呼叫,把遞迴呼叫的結果設定為剛才的students的集合
//所以後面的copy還是會覆蓋前面的clone
clone = src && jQuery.isArray(src) ? src : [];
} else {
//不是類陣列物件
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
//讓clone作為target,把copy上面的屬性全部克隆到clone上面去!也就是全部克隆到clone上去
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {//假如deep是false,上面的if就不會滿足,那麼就是淺層次克隆,那麼結果就是後面的覆蓋掉前面的屬性
//因為這種直接賦值就會導致覆蓋,給了target物件相同的屬性
target[ name ] = copy;
}
 }}
// Return the modified object
return target;
};

總結:

(1)第一個引數表示是否深度克隆,如果是深度克隆就會把陣列或者物件拆分出屬性,然後遍歷屬性而不是直接覆蓋!

(2)引數deep的預設值為false,你可以為該引數明確指定true值,但不能明確指定false值。簡而言之,第一個引數不能為false值。如果引數為nullundefined,則該引數將被忽略。

(3)如果只為$.extend()指定了一個引數,則意味著引數target被省略。此時,target就是jQuery物件本身。通過這種方式,我們可以為全域性物件jQuery新增新的函式。只有一個引數那麼就會滿足i===length,所以target就是this了!這時候i為0,表示用this繼承第一個引數屬性!

(4)底層的深度克隆的案例就是上面的測試程式碼2.