jQuery.extend()的實(shí)現(xiàn)方式詳解及實(shí)例
<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ò)展對(duì)象obj1,第一個(gè)參數(shù)設(shè)為true表示深復(fù)制。
雖然obj1中原來沒有"x"屬性,但經(jīng)過擴(kuò)展后,obj1不但具有了"x"屬性,而且對(duì)obj2中的"x"屬性的修改也不會(huì)影響到obj1中"x"屬性的值,這就是所謂的“深復(fù)制”了。
淺復(fù)制的實(shí)現(xiàn)
如果僅僅需要實(shí)現(xiàn)淺復(fù)制,可以采用類似下面的寫法:
$ = {
extend : function(target, options) {
for (name in options) {
target[name] = options[name];
}
return target;
}
};
也就是簡單地將options中的屬性復(fù)制到target中。我們?nèi)匀豢梢杂妙愃频拇a進(jìn)行測(cè)試,但得到的結(jié)果有所不同(假設(shè)我們的js命名為“jquery-extend.js”):
<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"屬性,但這個(gè)屬性是一個(gè)對(duì)象,對(duì)obj2中的"x"的修改也會(huì)影響到obj1,這可能會(huì)帶來難以發(fā)現(xiàn)的錯(cuò)誤。
深復(fù)制的實(shí)現(xiàn)
如果我們希望實(shí)現(xiàn)“深復(fù)制”,當(dāng)所復(fù)制的對(duì)象是數(shù)組或者對(duì)象時(shí),就應(yīng)該遞歸調(diào)用extend。如下代碼是“深復(fù)制”的簡單實(shí)現(xiàn):
$ = {
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ù)組時(shí),則將target[name]初始化為空數(shù)組,然后遞歸調(diào)用extend;
2. 屬性是對(duì)象時(shí),則將target[name]初始化為空對(duì)象,然后遞歸調(diào)用extend;
3. 否則,直接復(fù)制屬性。
測(cè)試代碼如下:
<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ù)制的話,對(duì)obj2的修改將不會(huì)對(duì)obj1產(chǎn)生影響了;不過這個(gè)代碼還存在一些問題,比如“instanceof Array”在IE5中可能存在不兼容的情況。jQuery中的實(shí)現(xiàn)實(shí)際上會(huì)更復(fù)雜一些。
更完整的實(shí)現(xiàn)
下面的實(shí)現(xiàn)與jQuery中的extend()會(huì)更接近一些:
$ = 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(){...}();這種寫法,可以理解為與下面的寫法類似:
func = function(){...};
$ = func();
也就是立即執(zhí)行函數(shù),并將結(jié)果賦給$。這種寫法可以利用function來管理作用域,避免局部變量或局部函數(shù)影響全局域。另外,我們只希望使用者調(diào)用$.extend(),而將內(nèi)部實(shí)現(xiàn)的函數(shù)隱藏,因此最終返回的對(duì)象中只包含extend:
return { extend : extend };
接下來,我們看看extend函數(shù)與之前的區(qū)別,首先是多了這句話:
if (target === copy) { continue; }
這是為了避免無限循環(huán),要復(fù)制的屬性copy與target相同的話,也就是將“自己”復(fù)制為“自己的屬性”,可能導(dǎo)致不可預(yù)料的循環(huán)。
然后是判斷對(duì)象是否為數(shù)組的方式:
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 實(shí)現(xiàn),就使用瀏覽器自身的實(shí)現(xiàn)方式,否則將對(duì)象轉(zhuǎn)為String,看是否為"[object Array]"。
最后逐句地看看isPlainObject的實(shí)現(xiàn):
if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) {
return false;
}
如果定義了obj.nodeType,表示這是一個(gè)DOM元素;這句代碼表示以下四種情況不進(jìn)行深復(fù)制:
1. 對(duì)象為undefined;
2. 轉(zhuǎn)為String時(shí)不是"[object Object]";
3. obj是一個(gè)DOM元素;
4. obj是window。
之所以不對(duì)DOM元素和window進(jìn)行深復(fù)制,可能是因?yàn)樗鼈儼膶傩蕴嗔?;尤其是window對(duì)象,所有在全局域聲明的變量都會(huì)是其屬性,更不用說內(nèi)置的屬性了。
接下來是與構(gòu)造函數(shù)相關(guān)的測(cè)試:
if (obj.constructor && !hasOwn.call(obj, "constructor")
&& !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
如果對(duì)象具有構(gòu)造函數(shù),但卻不是自身的屬性,說明這個(gè)構(gòu)造函數(shù)是通過prototye繼承來的,這種情況也不進(jìn)行深復(fù)制。這一點(diǎn)可以結(jié)合下面的代碼結(jié)合進(jìn)行理解:
var key;
for (key in obj) {
}
return key === undefined || hasOwn.call(obj, key);
這幾句代碼是用于檢查對(duì)象的屬性是否都是自身的,因?yàn)楸闅v對(duì)象屬性時(shí),會(huì)先從自身的屬性開始遍歷,所以只需要檢查最后的屬性是否是自身的就可以了。
這說明如果對(duì)象是通過prototype方式繼承了構(gòu)造函數(shù)或者屬性,則不對(duì)該對(duì)象進(jìn)行深復(fù)制;這可能也是考慮到這類對(duì)象可能比較復(fù)雜,為了避免引入不確定的因素或者為復(fù)制大量屬性而花費(fèi)大量時(shí)間而進(jìn)行的處理,從函數(shù)名也可以看出來,進(jìn)行深復(fù)制的只有"PlainObject"。
如果我們用如下代碼進(jìn)行測(cè)試:
<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()的實(shí)現(xiàn)方式,考慮了兼容瀏覽器的兼容,避免性能過低,和避免引入不可預(yù)料的錯(cuò)誤等因素。
- jQuery的deferred對(duì)象使用詳解
- jQuery Deferred和Promise創(chuàng)建響應(yīng)式應(yīng)用程序詳細(xì)介紹
- 利用jQuery的deferred對(duì)象實(shí)現(xiàn)異步按順序加載JS文件
- jQuery之Deferred對(duì)象詳解
- jQuery源碼分析-05異步隊(duì)列 Deferred 使用介紹
- jQuery $.extend()用法總結(jié)
- jQuery插件開發(fā)的兩種方法及$.fn.extend的詳解
- jQuery.extend 函數(shù)詳解
- 原生js實(shí)現(xiàn)復(fù)制對(duì)象、擴(kuò)展對(duì)象 類似jquery中的extend()方法
- jQuery中的deferred對(duì)象和extend方法詳解
相關(guān)文章
javascript實(shí)現(xiàn)多張圖片左右無縫滾動(dòng)效果
本文主要介紹了javascript實(shí)現(xiàn)多張圖片左右無縫滾動(dòng)效果的實(shí)例。具有很好的參考價(jià)值。下面跟著小編一起來看下吧2017-03-03深入理解Javascript動(dòng)態(tài)方法調(diào)用與參數(shù)修改的問題
這篇文章主要是對(duì)Javascript動(dòng)態(tài)方法調(diào)用與參數(shù)修改的問題進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助2013-12-12JS面試題之如何判斷兩個(gè)數(shù)組的內(nèi)容是否相等
這篇文章主要為大家詳細(xì)介紹了JavaScript面試的常考題,即如何判斷兩個(gè)數(shù)組的內(nèi)容是否相等,文中的示例方法講解詳細(xì),需要的小伙伴可以參考一下2023-10-10《JavaScript高級(jí)程序設(shè)計(jì)》閱讀筆記(二) ECMAScript中的原始類型
ECMAScript有5種原始類型(primitive type),即Undefined、Null、Boolean、Number和String。ECMAScript提供了typeof來判斷值的類型2012-02-02javascript 閉包函數(shù)做顯隱內(nèi)容
用閉包函數(shù)做顯隱內(nèi)容,主要優(yōu)勢(shì)就是可以增加顯示與隱藏效率。2009-03-03js實(shí)現(xiàn)驗(yàn)證碼干擾(動(dòng)態(tài))
這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)驗(yàn)證碼干擾,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-02-02