这篇文章,来源于一场事故...
关于,今天会写这个小结,也是来源于一场小事故。为了偷懒,就喜欢将一些方法,封装起来,只暴露一个接口,通过传入指定的参数,只调用一个API就能做成一件小事。那么不可避免,我们就需要给这个方法设置一些默认的参数,然后通过传入的参数去覆盖它,或者通过它来写一个jQuery的插件。于是,就理所当然的用了jQuery的extend().
相信大部分的人都非常的熟悉jQuery,也十分的熟悉jQuery用来合并对象和扩展方法的extend().但是,有些时候,我们在开发中,要把jQuery的功能用到淋漓尽致,充分发挥它的才能的时候,其实很少。我们不可能说只是为了用jQuery的选择器,或者只是为了用它的extend方法,就把整个库都引入我们的工程当中去,那真是十分耗资源。
于是先写了个1.0的low版本用来扩展属性
var Kingsley = function(){};
//v1.0
Kingsley.prototype.extend = fucntion(defaults,parameters){
for(var param in parameters){
if(defaults.hasOwnProperty(param) && parameters.hasOwnProperty(param)){
defaults[param] = parameters[param];
}
}
return defaults;
}
但是,很快就发现了不足。于是做了些改进,有了2.0版本
Kingsley.prototype.extend = function(){
var options,target,copy,src,
i = 1,
len = arguments.length;
target = arguments[0];
for(;i < len;i++){
if((options = arguments[i]) != null){
for(var param in options){
if(target.hasOwnProperty(param) && options.hasOwnProperty(param)){
src = target[param];
copy = options[param];
if(typeof src === "object"){
target[param] = Kingsley.prototype.extend(src,copy);
}else{
target[param] = copy;
}
}
}
}
}
return target;
}
2.0的版本看起来稍微有点起色了,但好像也不好,还是需要改进。
于是呢。。。铺垫了这么久,该上jQuery的源代码了。
我们先来看
jQuery.extend = jQuery.fn.extend = function() {
var options, name, src, copy, copyIsArray, clone,
target = arguments[ 0 ] || {},
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
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
if ( i === length ) {
target = this;
i--;
}
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;
}
}
}
}
// Return the modified object
return target;
};
一看,就很容易看出区别了。jQuery的代码写的非常的优雅,而且做了许多了处理机制,防止意外发生。
调用jQuery的extend,除了可以扩展属性,还能用来扩展jQuery的静态方法和实例方法。
1.$.extend(true,defaults,options);
2.$.extend({say:function(){ alert("Hello World")}});
我们把它一部分一部分拆解开来
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
// Skip the boolean and the target
target = arguments[ i ] || {};
i++;
}
很明显 ,deep是我们传入的第一个参数,这个参数也是为了告诉jQuery,我们进行的是深拷贝还是浅拷贝。所谓的深拷贝,其实就是,当你进行多个参数的合并后,如果改变了某个对象的属性的值,相应的对象的属性会不会也发生了改变。深拷贝是相互独立的。改变了一个属性的值,并不会影响到另一个属性的值。
// 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
if ( i === length ) {
target = this;
i--;
}
上面这段代码,只是做了个简单的判断。如果传入的参数不是对象,或者不是一个函数,那么就把它初始化为一个空对象。而target = this,则是在只传入一个对象的时候,把jQuery本身赋值给了target。这么做的目的,就是为了能够在jQuery上扩展方法。
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;
}
}
}
}
后面的这一大串,大部分所作的事情,就是为了将一个对象的属性扩展到另一个对象的身上。其中,比较关键的是
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
和
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
前者为了防止死循环,而后者,则是在出现对象的属性又是个对象的时候如何解决,那就是递归又调用了自己。
嗯。其实非常简单,但是,能够写得优雅,其实并不容易。