回顧extend的和深度拷貝的知識
之前寫的extend的文章
之前寫的深淺拷貝的文章
菜鳥教程
簡單來說extend可以合并多個物件,可以拓展jQuery函式,可以設定深拷貝;也可以在jQuery物件原型上拓展
深度拷貝是針對非基礎資料型別來說的,實際上,我們定義的存盤非基礎資料型別的變數都是存盤著變數的參考地址,如果只是簡單賦值物件,原物件改變時,復制的物件也會改變,想要相互獨立,就要深拷貝,遍歷每一個key,每一層嵌套,獲取物件里實際的值
截取原始碼一部分內容(3.6.0版本),可以看到,定義在jQuery和jQuery原型上的extend是一樣的,所以我們簡單解讀一下jQuery.extend

jQuery.extend可以拓展自定義物件,拓展jQuery函式,將原物件拓展到目標物件上,還可以設定深拷貝,以下四個案例可以大概涵蓋其基本功能
// 定義物件
var obj1 = {
ccy : 18
};
var obj2 = {
ccy1 : 19,
ccy2 : {
age : 20
}
}
// 案例1:拓展自定義物件,目標物件是obj1,原物件是obj2
$.extend( obj1, obj2);
// 案例2:深拷貝拓展自定義物件
$.extend( true, obj1, obj2);
// 案例3:拓展jQuery物件,目標物件是jQuery,原物件是obj1
$.extend(obj1);
// 案例4:深拷貝拓展jQuery物件
$.extend(true, obj1);
案例3和案例4執行后,均可以直接通過:$.ccy來呼叫該資料,回傳18
不論是將obj2拓展到obj1里,還是將obj1拓展到jQuery中,我們都需考慮以下問題:
1.是否深拷貝、哪一個是目標物件、從哪里開始到哪里結束是原物件;
2.如何深拷貝,
3.而且還要有一定的容錯率
原始碼很巧妙地解決了這些問題
首先看第一個問題:

開始
我們可以看到,一開始定義了很多變數,并賦予了默認值:
1.默認目標物件target是傳入的第一個引數,索引0;
2.傳入的引數從i=1開始,到 i=length位置都是原物件;
3.默認為淺拷貝,deep為false
這時,4個案例里,這些引數分別為:

對深淺拷貝的處理:
如果第一個引數的資料型別是布爾型,則該引數是設定深拷貝,將第一個引數的內容復制給deep變數,target則往后順延,獲得下一個索引(i++)的引數
這里做了一個容錯處理,如果extend只傳了一個引數true,target就設為空物件{},一般我們不會這樣
原始碼如下:

這時4個案例的引數分別是這樣的:

對輸入的引數的資料型別也做了容錯處理,如果第一個第二個引數傳入的引數既不是布爾,也不是物件,也不是一個函式,僅僅只是一個字串,自動將目標物件設為空物件{}
如:$.extend('asdf', {name: 'ccy'}),就會自動創建一個空物件,容納后面的物件,相當于:$.extend( {}, {name: 'ccy'})
當用戶需要對jQuery做拓展
我們可以看到,經過深淺拷貝引數的修改,對jQuery函式本身做拓展的兩種案例,此時的i值和length值是一致的,我們可以以這個作為匹配條件
這時的目標物件是自己,target = this
對jQuery做拓展,會少傳入一個目標物件的引數,因此第一個引數不是Boolean,就是原物件;i本該指向target的下一位,如今前面少了target,i就需要減1

此時四個案例的引數分別是:

第一個問題也解決
來看第二個問題,如何深拷貝,此處我把物件中的鍵值都先用key和value指代
1.當傳入的引數不設定深拷貝,或設定了深拷貝但拷貝的是基本資料型別時,只需遍歷物件下的key:value,一一復制到目標物件即可
2.當傳入的引數設定了深拷貝,原物件當前key對應的value值不為空,并且(該value值是object或陣列)時,進入深拷貝環節
2.1原物件下某value是復雜物件,要合并到目標物件:如果目標物件沒有同樣的key,或同樣的key對應的value值只是基本資料類型,就新起一個空陣列或空物件用來接收原物件的合并,即一個空物件與一個有值物件的合并,啟動遞回extend(deep,空陣列/物件,原物件該key下的value),回傳原物件該key下的value的實際資料,存在target[key]下
2.2當原物件和目標物件都有某個key時,記src = 目標物件[key](第3點中此src=undefined),如果src是陣列或者物件,則原物件和目標物件在該key上以src為基礎進行合并,又是兩個復雜物件的合并,啟動遞回extend(deep,scr,原物件該key下的value),回傳兩者的合并,存盤在target[key]下
var obj1 = {
ccy : 18,
ccy2 : {sex: 'female'}
};
var obj2 = {
ccy1 : 19,
ccy2 : {
age : 20
}
}
如果要將obj2深拷貝并合并到obj1,遍歷到ccy2時,目標物件obj1也有這個key值,就需要將兩個ccy2下的物件合并,即一個物件合并到另一個物件,就可以遞回呼叫extend()方法,遍歷obj2的ccy2下的key和value,拷貝到obj1的ccy2上:extend(true, obj1[ccy2], obj2[ccy2])
如果obj1的ccy2僅是字串,如:ccy2: ‘1234’,obj2的ccy2就直接覆寫掉obj1的ccy2:extend(true, {}, obj2[ccy2])
原始碼:
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
// option存盤原物件,target代表目標物件
if ( ( options = arguments[ i ] ) != null ) {
// Extend the base object
for ( name in options ) {
copy = options[ name ];
// Prevent Object.prototype pollution防止污染物件原型
// Prevent never-ending loop 防止無限回圈
if ( name === "__proto__" || target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays 深拷貝物件或陣列
if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
( copyIsArray = Array.isArray( copy ) ) ) ) {
src = target[ name ];
// Ensure proper type for the source value
if ( copyIsArray && !Array.isArray( src ) ) {
clone = []; //新起一個空陣列,接收原物件該name的合并
} else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
clone = {};//新起一個空物件,接收原物件該name的合并
} else {
clone = src;//不覆寫、不新創建,在目標的基礎上合并
}
copyIsArray = false;
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
// Don't bring in undefined values 解決淺拷貝或基本資料型別的拷貝
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
從i開始,遍歷到length結束,當i指向的引數不為null時進入拷貝,遍歷當前物件下所有內容
拿第一個案例來看,

i此時指向obj2,遍歷obj2下所有的內容,第一次是ccy1,第二次是ccy2,我之前的解說用key,原始碼用name
為防止無限回圈的情況,如:
var a = {}
var b = {name: a}
$.extend(a, b)
這樣獲得的a里就有無限個{name:{name:…}}
所以先將當前原物件b的單個資料b[name]保存下來,與目標物件a對比,如果b[name] === a,就會出現無限回圈,此次回圈就結束,進入下一輪
如果只是一個空的jQuery物件,也結束本輪回圈,進入下一輪
第一個案例中,不設定深拷貝,所有資料不進入深拷貝代碼區,直接進入else if部分,賦值即可
第二個案例$.extend( true, obj1, obj2);,設定了深拷貝,目標物件是obj1,原物件是obj2
遍歷obj2下的key:value,第一個key:vakue是ccy1 : 19,由于是拷貝的基本資料型別,所以也進入了else if部分,直接拷貝;第二個拷貝的是物件,進入深拷貝部分的代碼,obj2[‘ccy2’]是物件型別,obj1中沒有ccy2的key,所以
clone = {}; ,用遞回來復制obj2[‘ccy2’]下具體的內容:
target[name] = jQuery.extend(deep, clone, option[name])
還有兩個案例同理
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/274744.html
標籤:其他
