jQuery中extend()和fn.extend()方法詳解
這兩個(gè)方法用的是相同的代碼,一個(gè)用于給jQuery對(duì)象或者普通對(duì)象合并屬性和方法一個(gè)是針對(duì)jQuery對(duì)象的實(shí)例,對(duì)于基本用法舉幾個(gè)例子:
html代碼如下:
<!doctype html>
<html>
<head>
<title></title>
<script src='jquery-1.7.1.js'></script>
</head>
<body>
<img src=''/>
</body>
</html>
下面寫(xiě)js里面的用法:
合并兩個(gè)普通對(duì)象
//給兩個(gè)普通對(duì)象合并屬性
var obj1={name:'Tom',age:22};
var obj2={name:'Jack',height:180};
console.log($.extend(obj1,obj2)); //Object {name: "Jack", age: 22, height: 180}
給jQuery對(duì)象添加屬性或者方法
$.extend({hehe:function(){alert('hehe');}});
$.hehe(); //alert('hehe')
這個(gè)用法很重要,是jQuery內(nèi)部添加實(shí)例屬性和方法以及原型屬性和方法的實(shí)現(xiàn)方法也是編寫(xiě)jQuery插件的方法,下面是jQuery1.7.1中使用extend方法擴(kuò)展自己的方法和屬性
jQuery.extend({
noConflict: function( deep ) {
if ( window.$ === jQuery ) {
window.$ = _$;
}
if ( deep && window.jQuery === jQuery ) {
window.jQuery = _jQuery;
}
return jQuery;
},
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1,
.....
在這個(gè)例子中只傳入了一個(gè)對(duì)象參數(shù),那么默認(rèn)就把this當(dāng)做待合并修改的對(duì)象
給jQuery對(duì)象實(shí)例添加屬性或者方法
//針對(duì)jQuery實(shí)例擴(kuò)展合并
console.log($('img').extend({'title':'img'}));//[img, img#img.img, prevObject: jQuery.fn.jQuery.init[1], context: document, selector: "img", title: "img", constructor: function…]
只合并不修改待合并對(duì)象
var obj1={name:'Tom',age:22};
var obj2={name:'Jack',height:180};
console.log($.extend(obj1,obj2)); //Object {name: "Jack", age: 22, height: 180}
console.log(obj1); //Object {name: "Jack", age: 22, height: 180}
默認(rèn)情況下,待合并對(duì)象跟返回結(jié)果一樣是被修改了的,如果僅僅想得到一個(gè)合并后的對(duì)象又不想破壞任何一個(gè)原來(lái)的對(duì)象可以使用此方法
var obj1={name:'Tom',age:22};
var obj2={name:'Jack',height:180};
var empty={};
console.log($.extend(empty,obj1,obj2)); //Object {name: "Jack", age: 22, height: 180}
console.log(obj1); //Object {name: "Tom", age: 22}
使用則遞歸合并或者叫深度拷貝
var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}};
var obj2={name:'Jack',love:{drink:'water',sport:'football'}};
console.log(($.extend(false,obj1,obj2)).love); //Object {drink: "water", sport: "football"}
console.log(($.extend(true,obj1,obj2)).love); //Object {drink: "water", eat: "bread", sport: "football"}
詳細(xì)的使用方法可以看參考手冊(cè)http://www.w3cschool.cc/manual/jquery/
下面來(lái)分析下1.7.1源碼中是怎么實(shí)現(xiàn)的:
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false;
...
}
首先是定義了一組變量,因?yàn)閰?shù)個(gè)數(shù)不確定所以就直接調(diào)用arguments對(duì)象訪問(wèn)傳遞的參數(shù)
變量 options:指向某個(gè)源對(duì)象。
變量 name:表示某個(gè)源對(duì)象的某個(gè)屬性名。
變量 src:表示目標(biāo)對(duì)象的某個(gè)屬性的原始值。
變量 copy:表示某個(gè)源對(duì)象的某個(gè)屬性的值。
變量 copyIsArray:指示變量 copy 是否是數(shù)組。
變量 clone:表示深度復(fù)制時(shí)原始值的修正值。
變量 target:指向目標(biāo)對(duì)象。
變量 i:表示源對(duì)象的起始下標(biāo)。
變量 length:表示參數(shù)的個(gè)數(shù),用于修正變量 target。
變量 deep:指示是否執(zhí)行深度復(fù)制,默認(rèn)為 false。
為了更好地了解代碼實(shí)現(xiàn)這里以上面舉的一個(gè)例子作為演示觀察源代碼執(zhí)行情況
var obj1={name:'Tom',love:{drink:'milk',eat:'bread'}};
var obj2={name:'Jack',love:{drink:'water',sport:'football'}};
$.extend(true,obj1,obj2)
源碼分析
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
判斷是不是深度復(fù)制,如果第一個(gè)參數(shù)是布爾值那么就把第一個(gè)參數(shù)的值給deep,然后把第二個(gè)參數(shù)作為目標(biāo)對(duì)象,如果第二個(gè)參數(shù)不存在就賦值為一個(gè)空對(duì)象,把源對(duì)象的下標(biāo)改為2,在這個(gè)例子里面 是走這里的因?yàn)榈谝粋€(gè)參數(shù)是ture然后把deep變成了true ,target被修正成了第二個(gè)參數(shù)也即是obj1,源對(duì)象的起始下標(biāo)為2就是從第三個(gè)開(kāi)始作為源對(duì)象也就是本例中的obj2
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
這里對(duì)target又進(jìn)一步進(jìn)行了處理對(duì)于非對(duì)象和函數(shù)的數(shù)據(jù)類型而言增加自定義屬性是無(wú)效的比如字符串自能調(diào)用自帶的方法和屬性
// extend jQuery itself if only one argument is passed
if ( length === i ) {
target = this;
--i;
}
如果length屬性等于i的值那就表示沒(méi)有目標(biāo)對(duì)象存在,正常情況下length應(yīng)該是大于i的值的 ,那么這個(gè)時(shí)候就把this作為目標(biāo)對(duì)象把i值減一實(shí)現(xiàn)length值大于i值(比i大1)
這個(gè)就是jQuery給自己擴(kuò)展屬性的方法的實(shí)現(xiàn)原理,只要不傳入目標(biāo)對(duì)象就可以啦
兩種可能的情況:$.extend(obj) 或者 $.extend(false/true,obj);
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
// Recurse if we're merging plain objects or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
// 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;
}
}
}
}
這個(gè)部分就是此方法的核心了,從arguements對(duì)象的第i個(gè)下標(biāo)值開(kāi)始循環(huán)操作首先過(guò)濾掉源對(duì)象是null或者是undefined的情況可以看到其實(shí)
源對(duì)象不一定真的就是對(duì)像,也可以是其他類型的值比如字符串比如這樣寫(xiě):
console.log($.extend({'name':'tom'},'aa')); //Object {0: "a", 1: "a", name: "tom"}
是不是感覺(jué)很奇怪???究竟是怎么實(shí)現(xiàn)的呢?下面接著看
過(guò)濾完之后開(kāi)始進(jìn)行for循環(huán) src保存的是目標(biāo)對(duì)象的某個(gè)鍵的值,copy屬性保存的源對(duì)象的某個(gè)鍵的值,這兩個(gè)鍵都是一樣的
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
如果源對(duì)象的某個(gè)屬性值就是目標(biāo)對(duì)象可能會(huì)造成死循環(huán)導(dǎo)致程序崩潰所以這里做了一個(gè)限制讓其跳過(guò)此次循環(huán)例如:
var o = {};
o.n1 = o;
$.extend( true, o, { n2: o } );
// 拋出異常:
// Uncaught RangeError: Maximum call stack size exceeded
但是這樣做也會(huì)冤枉一些正常的情況比如:
var obj1={a:'a'}
var obj2={a:obj1};
console.log($.extend(obj1,obj2)); //Object {a: "a"}
這種情況也是滿足源對(duì)象值等于目標(biāo)對(duì)象的但是結(jié)果發(fā)現(xiàn)obj1的a的屬性值并沒(méi)有被修改,就是因?yàn)閳?zhí)行了continue,下面把源碼的這段話注釋掉在執(zhí)行
Object {a: Object}
這個(gè)時(shí)候就是正常被修改了個(gè)人感覺(jué)這個(gè)地方需要改進(jìn);
接著就是一個(gè)if判斷就是區(qū)分是不是進(jìn)行深度復(fù)制的先不看深度復(fù)制的先看一般的
target[ name ] = copy;
很簡(jiǎn)單就是只要copy有值就直接復(fù)制給目標(biāo)對(duì)象,目標(biāo)對(duì)象有的就修改沒(méi)有就增加,這樣就實(shí)現(xiàn)了合并啦。
for循環(huán)之后在把新的目標(biāo)對(duì)象返回,所以目標(biāo)對(duì)象最后是被修改的,而且結(jié)果和返回的結(jié)果是一樣的。
// Return the modified object
return target;
};
下面再來(lái)說(shuō)說(shuō)深度復(fù)制了怎么去處理
首先保證deep是true,copy有值并且是對(duì)象或者數(shù)組(如果不是對(duì)象和數(shù)組深度復(fù)制也就無(wú)從談起)然后再分?jǐn)?shù)組和對(duì)象來(lái)處理,先來(lái)看數(shù)組的情況:
if ( copyIsArray ) {
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else {
clone = src && jQuery.isPlainObject(src) ? src : {};
}
如果是數(shù)組copyIsArray的值為真然后走里面的 把值改成false ,針對(duì)當(dāng)前循環(huán)的源對(duì)象屬性,目標(biāo)對(duì)象可能有也可能沒(méi)有,有的話判斷一下是不是數(shù)組是的話就是原來(lái)的數(shù)組不變不是的話就讓它變成一個(gè)數(shù)組,因?yàn)榧热辉磳?duì)象的當(dāng)前屬性是數(shù)組最后目標(biāo)元素也必須是數(shù)組。不是數(shù)組就是對(duì)象把目標(biāo)對(duì)象當(dāng)前屬性改成對(duì)象。
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
然后把源對(duì)象的當(dāng)前屬性值(是數(shù)組或?qū)ο螅┖鸵呀?jīng)被改造過(guò)的目標(biāo)對(duì)象的當(dāng)前屬性進(jìn)行遞歸合并最后返回的新的數(shù)組或者對(duì)象賦值給目標(biāo)對(duì)象,最終實(shí)現(xiàn)了深度復(fù)制。
但是這里面還有一個(gè)比較奇怪的現(xiàn)象,比如這樣操作:
console.log($.extend({a:1},'aa')); //Object {0: "a", 1: "a", a: 1}
原來(lái)源對(duì)象不一定真的是對(duì)象e而且居然可以把字符串拆開(kāi)跟目標(biāo)對(duì)象合并,原來(lái)for...in循環(huán)是操作字符串的
var str='aa';
for(var name in str){
console.log(name);
console.log(str[name])
}
這樣也是可以的,會(huì)把字符串拆開(kāi)按數(shù)字下標(biāo)讀取,但是在源碼中
if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) )
是有數(shù)組和對(duì)象限制的,那么深度復(fù)制的時(shí)候是不是就沒(méi)有效果了呢?
經(jīng)過(guò)我測(cè)試深度復(fù)制也是可以的,因?yàn)樵谠创a里面copy的值竟然變成了匿名函數(shù)函數(shù)
alert(jQuery.isPlainObject(copy)); //true
至于為什么是函數(shù)筆者還沒(méi)搞清楚留待以后解決吧!
- jQuery.extend()、jQuery.fn.extend()擴(kuò)展方法示例詳解
- 開(kāi)發(fā)插件的兩個(gè)方法jquery.fn.extend與jquery.extend
- 淺談jquery.fn.extend與jquery.extend區(qū)別
- jQuery學(xué)習(xí)筆記之jQuery.extend(),jQuery.fn.extend()分析
- jQuery插件開(kāi)發(fā)的兩種方法及$.fn.extend的詳解
- jquery的extend和fn.extend的使用說(shuō)明
- Jquery實(shí)現(xiàn)$.fn.extend和$.extend函數(shù)
- jquery簡(jiǎn)單插件制作(fn.extend)完整實(shí)例
- 深入理解jquery的$.extend()、$.fn和$.fn.extend()
- jQuery.extend 與 jQuery.fn.extend的用法及區(qū)別實(shí)例分析
相關(guān)文章
詳解Jquery Easyui的驗(yàn)證擴(kuò)展
本文主要介紹了Jquery Easyui驗(yàn)證擴(kuò)展,Easyui驗(yàn)證,Easyui校驗(yàn),js正則表達(dá)式。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01Android中的jQuery:AQuery簡(jiǎn)介
jQuery的流行已經(jīng)成為了事實(shí),它極大地減少了執(zhí)行異步任務(wù)和操作DOM所需要的代碼數(shù)量。新項(xiàng)目AQuery想要為Android開(kāi)發(fā)者提供同樣的功能2014-05-05JavaScript對(duì)象之間的轉(zhuǎn)換 jQuery對(duì)象和原聲DOM
jQuery對(duì)象和原聲DOM,JavaScript對(duì)象之間的轉(zhuǎn)換,學(xué)習(xí)jquery的朋友可以參考下。2011-03-03jQuery中slidedown與slideup方法用法示例
這篇文章主要介紹了jQuery中slidedown與slideup方法用法,結(jié)合實(shí)例形式分析了jQuery基于slidedown與slideup方法實(shí)現(xiàn)頁(yè)面元素展開(kāi)與折疊的實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-09-09EasyUi 打開(kāi)對(duì)話框后控件賦值及賦值后不顯示的問(wèn)題解決辦法
這篇文章主要介紹了easyUi 打開(kāi)對(duì)話框后控件賦值,以及賦值后不顯示的問(wèn)題解決辦法,解決方法非常簡(jiǎn)單,只需要將賦值語(yǔ)句修改下就好,下面小編給大家簡(jiǎn)單介紹下,需要的朋友參考下2017-01-01