jQuery中extend函数详解,写自已的js类库需要的核心
分类:关于美高梅

复制代码 代码如下:

一、jQuery extend方法介绍

jQuery中extend函数详解

 在jQuery的API手册中,我们看到,extend实际上是挂载在jQuery和jQuery.fn上的两个不同方法,尽管在jQuery内部jQuery.extend()和jQuery.fn.extend()是用相同的代码实现的,但是它们的功能却不太一样。来看一下 官方API对extend 的解释:

代码如下:

 

代码如下:

jQuery.extend(): Merge the contents of two or more objects together into the first object.(把两个或者更多的对象合并到第一个当中)
jQuery.fn.extend():Merge the contents of an object onto the jQuery prototype to provide new jQuery instance methods.(把对象挂载到jQuery的prototype属性,来扩展一个新的jQuery实例方法)

 

我们知道,jQuery有静态方法和实例方法之分, 那么jQuery.extend()和jQuery.fn.extend()的第一个区别就是一个用来扩展静态方法,一个用来扩展实例方法。用法如下:

 

代码如下:

jQuery.extend({
sayhello: function (){
console.log( "Hello,This is jQuery Library" );
}
})
$.sayhello(); //Hello, This is jQuery Library
jQuery.fn.extend({
check: function () {
return this .each( function () {
this .checked = true ;
});
},
uncheck: function () {
return this .each( function () {
this .checked = false ;
});
}
})
$( "input[type='checkbox']" ).check(); //所有的checkbox都会被选择

 

注意两种调用插件的方式,一种是直接用$调用,另外一种是用$()调用,另外jQuery.extend()接收多个对象作为参数,如果只有一个参数,则把这个对象的属性方法附加到jQuery上,如果含有多个参数,则把后面的对象的属性和方法附加到第一个对象上。jQuery extend的实现源码:

 

代码如下:

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;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
if ( length === i ) {
target = this ;
--i;
}
for ( ; i < length; i++ ) {
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 ;
}
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 : {};
}
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.extend()中有多个参数时的对象合并,深度拷贝问题,如果去掉这些功能,让extend只有扩展静态和实例方法的功能,那么代码如下:

 

代码如下:

jQuery.extend = jQuery.fn.extend = function (obj){
//obj是传递过来扩展到this上的对象
var target= this ;
for ( var name in obj){
//name为对象属性
//copy为属性值
copy=obj[name];
//防止循环调用
if (target === copy) continue ;
//防止附加未定义值
if ( typeof copy === 'undefined' ) continue ;
//赋值
target[name]=copy;
}
return target;
}

 

下面再来对extend方法进行注释解释:

 

代码如下:

jQuery.extend = jQuery.fn.extend = function () {
// 定义默认参数和变量
// 对象分为扩展对象和被扩展的对象
//options 代表扩展的对象中的方法
//name 代表扩展对象的方法名
//i 为扩展对象参数起始值
//deep 默认为浅复制
var options, name, src, copy, copyIsArray, clone,
target = arguments[0] || {},
i = 1,
length = arguments.length,
deep = false ;
//对接下来的参数进行处理
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
i = 2;
}
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
if ( length === i ) {
target = this ;
--i;
}
//对从i开始的多个参数进行遍历
for ( ; i < length; i++ ) {
// 只处理有定义的值
if ( (options = arguments[ i ]) != null ) {
// 展开扩展对象
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
// 防止循环引用
if ( target === copy ) {
continue ;
}
// 递归处理深拷贝
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 : {};
}
target[ name ] = jQuery.extend( deep, clone, copy );
// 不处理未定义值
} else if ( copy !== undefined ) {
//给target增加属性或方法
target[ name ] = copy;
}
}
}
}
//返回
return target;
};

 

弄懂了jQuery扩展的原理,相信以后再也不用为编写jQuery插件而烦恼了。

在jQuery的API手册中,我们看到,extend实际上是挂载在jQuery和jQuery.fn上的两个不同方法,尽管在jQuery内部jQuery.extend()和...

(function(win) {
var toString = Object.prototype.toString;
var hasOwn = Object.prototype.hasOwnProperty;
var class2type = {};
class2type["[object Boolean]"] = "boolean";
class2type["[object Number]"] = "number";
class2type["[object String]"] = "string";
class2type["[object Function]"] = "function";
class2type["[object Array]"] = "array";
class2type["[object Date]"] = "date";
class2type["[object RegExp]"] = "regexp";
class2type["[object Object]"] = "object";
win.type = function(obj) {
return obj == null ? String(obj) : class2type[toString.call(obj)] || "object";
};
win.isBoolean = function(obj) {
return type(obj) === "boolean";
};
win.isNumber = function(obj) {
return type(obj) === "number";
};
win.isString = function(obj) {
return type(obj) === "string";
};
win.isDate = function(obj) {
return type(obj) === "date";
};
win.isRegExp = function(obj) {
return type(obj) === "regexp";
};
win.isObject = function(obj) {
return type(obj) === 'object';
};
win.isFunction = function(obj) {
return type(obj) === "function";
};
win.isArray = function(obj) {
return type(obj) === "array";
};
win.isWindow = function(obj) {
return obj
&& typeof obj === "object"
&& "setInterval" in obj;
};
win.isNumeric = function(obj) {
return !isNaN(parseFloat(obj)) && isFinite(obj);
};
win.isPlainObject = function(obj) {
if (!obj
|| type(obj) !== "object"
|| obj.nodeType
|| isWindow(obj)) {
return false;
}
try {
if (obj.constructor
&& !hasOwn.call(obj, "constructor")
&& !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
return false;
}
} catch (e) {
return false;
}
var key;
for (key in obj) {
}
return key === undefined || hasOwn.call(obj, key);
};
win.isEmptyObject = function(obj) {
for ( var name in obj) {
return false;
}
return true;
};
win.isPrimitive = function(obj){
var type = typeof obj;
return type === 'string' || type === 'number' || type === 'boolean';
};
//HTMLElement
win.isElement = function(obj){
return obj ? obj.nodeType === 1 : false;
};
//TextNode
win.isTextNode = function(obj){
return obj ? obj.nodeName === "#text" : false;
};
win.isIterable = function(obj){
return (obj && typeof obj !== 'string') ? obj.length !== undefined : false;
};
win.isDefined = function(obj){
return typeof obj !== 'undefined';
};
win.error = function(msg) {
throw new Error(msg);
};
win.now = function() {
return (new Date()).getTime();
};
win.print = function(value) {
document.write(value);
};
win.println = function(value) {
print(value);
document.write("<br/>");
};
win.each = function(object, callback, args) {
var name, i = 0,
length = object.length,
isObj = (length === undefined || isFunction(object));
if (args) {
if (isObj) {
for (name in object) {
if (callback.apply(object[name], args) === false) {
break;
}
}
} else {
for (; i < length;) {
if (callback.apply(object[i++], args) === false) {
break;
}
}
}
} else {
if (isObj) {
for (name in object) {
if (callback.call(object[name], name, object[name]) === false) {
break;
}
}
} else {
for (; i < length;) {
if (callback.call(object[i], i, object[i++]) === false) {
break;
}
}
}
}
return object;
};
win.Array.prototype.toString = function(){
return "[" + this.join() + "]"
}
win.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;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !isFunction(target) ) {
target = {};
}
// extend jQuery itself if only one argument is passed
if ( length === i ) {
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 && ( isPlainObject(copy) || (copyIsArray = isArray(copy)) ) ) {
if ( copyIsArray ) {
copyIsArray = false;
clone = src && isArray(src) ? src : [];
} else {
clone = src && isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[ name ] = extend( deep, clone, copy );
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
// Return the modified object
return target;
};
})(window);

jQuery的API手册中,extend方法挂载在jQuery和jQuery.fn两个不同对象上方法,但在jQuery内部代码实现的是相同的,只是功能却不太一样;

相关文章

相关搜索:

今天看啥

搜索技术库

返回首页

  • IOS8天气应用可以显示细节数据技巧
  • CAD迷你看图如何查看设备清单
  • 华为Mate7如何开启按电源键结束通话
  • 电脑回收站无法调整容量的大小怎么办?
  • 新三板是什么意思
  • 如何在win7旗舰版电脑中删除U盘使用历史记录

相关频道: HTML/CSS  HTML5  Javascript  jQuery  AJax教程  前端代码  正则表达式  Flex教程  WEB前端教程  

如果我们不用extend方法,可以象下面的方式写自己的组件:

且看官方给出解释:

帮客评论

复制代码 代码如下:

jQuery.extend(): Merge the contents of two or more objects together into the first object.(把两个或者更多的对象合并到第一个当中);

(function(win){
win.StringBuffer = function(){
this.datas = [];
}
var proto = StringBuffer.prototype;
proto.append = function(value){
this.datas.push(value);
},
proto.toString = function(){
return this.datas.join("");
}
})(window);

jQuery.fn.extend():Merge the contents of an object onto the jQuery prototype to provide new jQuery instance methods.(把对象挂载到jQuery的prototype属性,来扩展一个新的jQuery实例方法)

如果使用extend方法,可以象下面这样写组件:

简单理解两者区别:

复制代码 代码如下:

jQuery.extend(object); 为扩展jQuery类本身,为自身添加新的方法。

(function(win){
win.extend(win,{
StringBuilder : function(){
this.datas = [];
}
});
win.extend(StringBuilder.prototype, {
append : function(value){
this.datas.push(value);
},
toString : function(){
return this.datas.join("");
}
});
})(window);

jQuery.fn.extend(object);给jQuery对象添加方法。

两种方法的效果一样,但是extend方法还可以做更多事件,比如插件式开发,跟第二种方式很象。
当然,如果你本来就想写jQuery插件,那就直接用jQuery的extend就可以啦。

二、jQuery extend方法使用

1、jQuery.extend(object);

         (a)  jQuery.extend( target [, object1 ] [, objectN ] )

合并object1, objectN到target对象,如果只有一个参数,则该target对象会被合并到jQuery对象中,如下代码:

1.  var object1 = {

2.    apple: 0,

3.    banana: { weight: 52, price: 100 },

4.    cherry: 97

5.  };

6.  var object2 = {

7.    banana: { price: 200 },

8.    durian: 100

9.  };

10. 

11.// Merge object2 into object1

12.$.extend( object1, object2 );

13.console.log(object1.durian);  //100

14. 

15.// Merge object1 into jQuery

16.$.extend( object1 );

17.console.log( $.apple ); //0

 

         (2) jQuery.extend( [deep ], target, object1 [, objectN ] )

深度复制合并对象,第一个参数是boolean类型的true时,将object1, objectN深度复制后合并到target中;关于深度复制,是将除null, undefined,window对象,dom对象,通过继承创建的对象外的其它对象克隆后保存到target中;

所排除的对象,一是考虑性能,二是考虑复杂度(例如dom及window对象,如果克隆复制,消耗过大,而通过继承实现的对象,复杂程度不可预知,因此也不进行深度复制);

         深度与非深度复制区别是,深度复制的对象中如果有复杂属性值(如数组、函数、json对象等),那将会递归属性值的复制,合并后的对象修改属性值不影响原对象,如下面例子:

1.  obj1 = { a : 'a', b : 'b' };  

2.  obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' };  

3.  $.extend(true, obj1, obj2);  

4.  alert(obj1.x.xxx);  // 得到"xxx"  

5.  obj2.x.xxx = 'zzz';          //修改obj2对象属性的内联值,不影响合并后对象obj1

6.  alert(obj2.x.xxx); // 得到"zzz"  

7.  alert(obj1.x.xxx); // 得到"xxx"  //值保持;如果不加true,则得到“zzz”

后面分析源码时,可以看到具体为什么……

 

2、jQuery.fn.extend(object);

         jQuery.fn = jQuery.prototype 即指向jQuery对象的原型链,对其它进行的扩展,作用在jQuery对象上面;一般用此方法来扩展jQuery的对象插件   

1.  //将hello方法合并到jquery的实例对象中。

2.  $.fn.extend({

3.      hello:function(){alert('hello');}

4.  });

5.   

6.  //在jquery全局对象中扩展一个net命名空间。

7.  $.extend($.net,{

8.       hello:function(){alert('hello');}

9.  });  //使用jQuery.net.hello();

二、jQuery extend实现原理

 extend()函数是jQuery的基础函数之一,作用是扩展现有的对象。例如下面的代码:

1.  <script type="text/javascript" src="jquery-1.5.2.js"></script> 

2.  <script> 

3.  obj1 = { a : 'a', b : 'b' }; 

4.  obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' }; 

5.    

6.  $.extend(true, obj1, obj2); 

7.    

8.  alert(obj1.x.xxx);  // 得到"xxx" 

9.    

10.obj2.x.xxx = 'zzz'; 

11.alert(obj2.x.xxx);  // 得到"zzz" 

12.alert(obj1.x.xxx);  // 得带"xxx" 

13.</script> 

  $.extend(true, obj1, obj2)表示以obj2中的属性扩展对象obj1,第一个参数设为true表示深复制。

  虽然obj1中原来没有"x"属性,但经过扩展后,obj1不但具有了"x"属性,而且对obj2中的"x"属性的修改也不会影响到obj1中"x"属性的值,这就是所谓的“深复制”了。

1、浅复制的实现

  如果仅仅需要实现浅复制,可以采用类似下面的写法:

1.  $ = { 

2.       extend : function(target, options) { 

3.          for (name in options) { 

4.              target[name] = options[name]; 

5.          } 

6.          return target; 

7.      } 

8.  }; 

  也就是简单地将options中的属性复制到target中。我们仍然可以用类似的代码进行测试,但得到的结果有所不同(假设我们的js命名为“jquery-extend.js”):

1.  <script type="text/javascript" src="jquery-extend.js"></script> 

2.  <script> 

3.  obj1 = { a : 'a', b : 'b' }; 

4.  obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' }; 

5.    

6.  $.extend(obj1, obj2); 

7.    

8.  alert(obj1.x.xxx);  // 得到"xxx" 

9.    

10.obj2.x.xxx = 'zzz'; 

11.alert(obj2.x.xxx);  // 得到"zzz" 

12.alert(obj1.x.xxx);  // 得带"zzz" 

13.</script> 

  obj1中具有了"x"属性,但这个属性是一个对象,对obj2中的"x"的修改也会影响到obj1,这可能会带来难以发现的错误。

2、深复制的实现

  如果我们希望实现“深复制”,当所复制的对象是数组或者对象时,就应该递归调用extend。如下代码是“深复制”的简单实现:

1.  $ = { 

2.      extend : function(deep, target, options) { 

3.          for (name in options) { 

4.              copy = options[name]; 

5.              if (deep && copy instanceof Array) { 

6.                  target[name] = $.extend(deep, [], copy); 

7.              } else if (deep && copy instanceof Object) { 

8.                  target[name] = $.extend(deep, {}, copy); 

9.              } else { 

10.                target[name] = options[name]; 

11.            } 

12.        } 

13.        return target; 

14.    } 

15.}; 

具体分为三种情况:

  1. 属性是数组时,则将target[name]初始化为空数组,然后递归调用extend;

  2. 属性是对象时,则将target[name]初始化为空对象,然后递归调用extend;

3. 否则,直接复制属性。

 

测试代码如下:

1.  <script type="text/javascript" src="jquery-extend.js"></script> 

2.  <script> 

3.  obj1 = { a : 'a', b : 'b' }; 

4.  obj2 = {  x : { xxx : 'xxx', yyy : 'yyy' },  y : 'y' }; 

5.  $.extend(true, obj1, obj2); 

6.  alert(obj1.x.xxx);  // 得到"xxx" 

7.  obj2.x.xxx = 'zzz'; 

8.  alert(obj2.x.xxx); // 得到"zzz" 

9.  alert(obj1.x.xxx); // 得到"xxx" 

10.</script> 

  现在如果指定为深复制的话,对obj2的修改将不会对obj1产生影响了;不过这个代码还存在一些问题,比如“instanceof Array”在IE5中可能存在不兼容的情况。jQuery中的实现实际上会更复杂一些。

3、更完整的实现

  下面的实现与jQuery中的extend()会更接近一些:

11.$ = function() { 

12.    var copyIsArray, 

13.        toString = Object.prototype.toString, 

14.        hasOwn = Object.prototype.hasOwnProperty; 

15.  

16.    class2type = { 

17.        '[object Boolean]' : 'boolean', 

18.        '[object Number]' : 'number', 

19.        '[object String]' : 'string', 

20.        '[object Function]' : 'function', 

21.        '[object Array]' : 'array', 

22.        '[object Date]' : 'date', 

23.        '[object RegExp]' : 'regExp', 

24.        '[object Object]' : 'object' 

25.    }, 

26.  

27.    type = function(obj) { 

28.        return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"; 

29.    }, 

30.  

31.    isWindow = function(obj) { 

32.        return obj && typeof obj === "object" && "setInterval" in obj; 

33.    }, 

34.  

35.    isArray = Array.isArray || function(obj) { 

36.        return type(obj) === "array"; 

37.    }, 

38.  

39.    isPlainObject = function(obj) { 

40.        if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) { 

41.            return false; 

42.        } 

43.  

44.        if (obj.constructor && !hasOwn.call(obj, "constructor") 

45.                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { 

46.            return false; 

47.        } 

48.  

49.        var key; 

50.        for (key in obj) { 

51.        } 

52.  

53.        return key === undefined || hasOwn.call(obj, key); 

54.    }, 

55.  

56.    extend = function(deep, target, options) { 

57.        for (name in options) { 

58.            src = target[name]; 

59.            copy = options[name]; 

60.  

61.            if (target === copy) { continue; } 

62.  

63.            if (deep && copy 

64.                    && (isPlainObject(copy) || (copyIsArray = isArray(copy)))) { 

65.                if (copyIsArray) { 

66.                    copyIsArray = false; 

67.                    clone = src && isArray(src) ? src : []; 

68.  

69.                } else { 

70.                    clone = src && isPlainObject(src) ? src : {}; 

71.                } 

72.  

73.                target[name] = extend(deep, clone, copy); 

74.            } else if (copy !== undefined) { 

75.                target[name] = copy; 

76.            } 

77.        } 

78.  

79.        return target; 

80.    }; 

81.  

82.    return { extend : extend }; 

83.}(); 

  首先是 $ =  function(){...}();这种写法,可以理解为与下面的写法类似:

1.  func = function(){...}; 

2.  $ =  func(); 

  也就是立即执行函数,并将结果赋给$。这种写法可以利用function来管理作用域,避免局部变量或局部函数影响全局域。另外,我们只希望使用者调用$.extend(),而将内部实现的函数隐藏,因此最终返回的对象中只包含extend:

1.  return { extend : extend }; 

  接下来,我们看看extend函数与之前的区别,首先是多了这句话:

1.  if (target === copy) { continue; } 

  这是为了避免无限循环,要复制的属性copy与target相同的话,也就是将“自己”复制为“自己的属性”,可能导致不可预料的循环。

 

  然后是判断对象是否为数组的方式:

1.  type = function(obj) { 

2.       return obj == null ? String(obj) : class2type[toString.call(obj)] || "object"; 

3.  }, 

4.  isArray = Array.isArray || function(obj) { 

5.       return type(obj) === "array"; 

6.   } 

  如果浏览器有内置的Array.isArray实现,就使用浏览器自身的实现方式,否则将对象转为String,看是否为"[object Array]"。

 

   最后逐句地看看isPlainObject的实现:

1.  if (!obj || type(obj) !== "object" || obj.nodeType || isWindow(obj)) { 

2.      return false; 

3.  } 

  如果定义了obj.nodeType,表示这是一个DOM元素;这句代码表示以下四种情况不进行深复制:

  1. 对象为undefined;

  2. 转为String时不是"[object Object]";

  3. obj是一个DOM元素;

  4. obj是window。

  之所以不对DOM元素和window进行深复制,可能是因为它们包含的属性太多了;尤其是window对象,所有在全局域声明的变量都会是其属性,更不用说内置的属性了。

 

  接下来是与构造函数相关的测试:

1.  if (obj.constructor && !hasOwn.call(obj, "constructor") 

2.                && !hasOwn.call(obj.constructor.prototype, "isPrototypeOf")) { 

3.        return false; 

4.    } 

  如果对象具有构造函数,但却不是自身的属性,说明这个构造函数是通过prototye继承来的,这种情况也不进行深复制。这一点可以结合下面的代码结合进行理解:

1.  var key; 

2.  for (key in obj) { 

3.  } 

4.    

5.  return key === undefined || hasOwn.call(obj, key); 

   这几句代码是用于检查对象的属性是否都是自身的,因为遍历对象属性时,会先从自身的属性开始遍历,所以只需要检查最后的属性是否是自身的就可以了。

 

  这说明如果对象是通过prototype方式继承了构造函数或者属性,则不对该对象进行深复制;这可能也是考虑到这类对象可能比较复杂,为了避免引入不确定的因素或者为复制大量属性而花费大量时间而进行的处理,从函数名也可以看出来,进行深复制的只有"PlainObject"。

  如果我们用如下代码进行测试:

1.  <script type="text/javascript" src="jquery-1.5.2.js"></script> 

2.  <script> 

3.  function O() { 

4.      this.yyy = 'yyy'; 

5.  } 

6.    

7.  function X() { 

8.      this.xxx = 'xxx'; 

9.  } 

10.  

11.X.prototype = new O(); 

12.  

13.x = new X(); 

14.  

15.obj1 = { a : 'a', b : 'b' }; 

16.obj2 = { x : x }; 

17.$.extend(true, obj1, obj2); 

18.  

19.alert(obj1.x.yyy);  // 得到"xxx" 

20.obj2.x.yyy = 'zzz'; 

21.alert(obj1.x.yyy);  // 得到"zzz" 

22.</script> 

  可以看到,这种情况是不进行深复制的。

 

  总之,jQuery中的extend()的实现方式,考虑了兼容浏览器的兼容,避免性能过低,和避免引入不可预料的错误等因素。

 

三、jQuery源码实现

还是先加一个例子,区别jQuery.extend及jQuery.fn.extend:

1.  jQuery.extend({

2.    sayhello:function(){

3.        console.log("Hello,This is jQuery Library");

4.      }

5.  })

6.  $.sayhello();    //Hello, This is jQuery Library

7.   

8.  jQuery.fn.extend({

9.    check: function() {

10.    return this.each(function() {

11.    this.checked = true;

12.    });

13.  },

14.  uncheck: function() {

15.    return this.each(function() {

16.      this.checked = false;

17.    });

18.  }

19.})

20.$( "input[type='checkbox']" ).check(); //所有的checkbox都会被选择

1、extend无注释的源码

文件如下

1.  jQuery.extend = jQuery.fn.extend = function() {

2.    var options, name, src, copy, copyIsArray, clone,

3.      target = arguments[0] || {},

4.      i = 1,

5.      length = arguments.length,

6.      deep = false;

7.   

8.    // Handle a deep copy situation

9.    if ( typeof target === "boolean" ) {

10.    deep = target;

11.    target = arguments[1] || {};

12.    // skip the boolean and the target

13.    i = 2;

14.  }

15. 

16.  // Handle case when target is a string or something (possible in deep copy)

17.  if ( typeof target !== "object" && !jQuery.isFunction(target) ) {

18.    target = {};

19.  }

20. 

21.  // extend jQuery itself if only one argument is passed

22.  if ( length === i ) {

23.    target = this;

24.    --i;

25.  }

26. 

27.  for ( ; i < length; i++ ) {

28.    // Only deal with non-null/undefined values

29.    if ( (options = arguments[ i ]) != null ) {

30.      // Extend the base object

31.      for ( name in options ) {

32.        src = target[ name ];

33.        copy = options[ name ];

34. 

35.        // Prevent never-ending loop

36.        if ( target === copy ) {

37.          continue;

38.        }

39. 

40.        // Recurse if we're merging plain objects or arrays

41.        if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {

42.          if ( copyIsArray ) {

43.            copyIsArray = false;

44.            clone = src && jQuery.isArray(src) ? src : [];

45. 

46.          } else {

47.            clone = src && jQuery.isPlainObject(src) ? src : {};

48.          }

49. 

50.          // Never move original objects, clone them

51.          target[ name ] = jQuery.extend( deep, clone, copy );

52. 

53.        // Don't bring in undefined values

54.        } else if ( copy !== undefined ) {

55.          target[ name ] = copy;

56.        }

57.      }

58.    }

59.  }

60. 

61.  // Return the modified object

62.  return target;

63.};

代码的大部分都是用来实现jQuery.extend()中有多个参数时的对象合并,深度拷贝问题,如果去掉这些功能,让extend只有扩展静态和实例方法的功能,那么代码如下:

1.  jQuery.extend = jQuery.fn.extend = function(obj){

2.    //obj是传递过来扩展到this上的对象

3.    var target=this;

4.    for (var name in obj){

5.        //name为对象属性

6.        //copy为属性值

7.        copy=obj[name];

8.        //防止循环调用

9.        if(target === copy) continue;

10.      //防止附加未定义值

11.      if(typeof copy === 'undefined') continue;

12.      //赋值

13.      target[name]=copy;

14.  }

15.  return target;

16.}

2、extend方法进行注释解释:

1.  jQuery.extend = jQuery.fn.extend = function() {

2.    // 定义默认参数和变量

3.    // 对象分为扩展对象和被扩展的对象

4.    //options 代表扩展的对象中的方法

5.    //name 代表扩展对象的方法名

6.    //i    为扩展对象参数起始值

7.    //deep 默认为浅复制

8.    var options, name, src, copy, copyIsArray, clone,

9.      target = arguments[0] || {},

10.    i = 1,

11.    length = arguments.length,

12.    deep = false;

13. 

14.  //当第一个参数为布尔类型是,次参数定义是否为深拷贝

15.  //对接下来的参数进行处理

16.  if ( typeof target === "boolean" ) {

17.    deep = target;

18.    target = arguments[1] || {};

19.    // 当定义是否深拷贝时,参数往后移动一位

20.    i = 2;

21.  }

22. 

23.  // 如果要扩展的不是对象或者函数,则定义要扩展的对象为空

24.  if ( typeof target !== "object" && !jQuery.isFunction(target) ) {

25.    target = {};

26.  }

27. 

28.  // 当只含有一个参数时,被扩展的对象是jQuery或jQuery.fn

29.  if ( length === i ) {

30.    target = this;

31.    --i;

32.  }

33. 

34.  //对从i开始的多个参数进行遍历

35.  for ( ; i < length; i++ ) {

36.    // 只处理有定义的值

37.    if ( (options = arguments[ i ]) != null ) {

38.      // 展开扩展对象

39.      for ( name in options ) {

40.        src = target[ name ];

41.        copy = options[ name ];

42. 

43.        // 防止循环引用

44.        if ( target === copy ) {

45.          continue;

46.        }

47. 

48.        // 递归处理深拷贝

49.        if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {

50.          if ( copyIsArray ) {

51.            copyIsArray = false;

52.            clone = src && jQuery.isArray(src) ? src : [];

53. 

54.          } else {

55.            clone = src && jQuery.isPlainObject(src) ? src : {};

56.          }

57. 

58.          target[ name ] = jQuery.extend( deep, clone, copy );

59. 

60.        // 不处理未定义值

61.        } else if ( copy !== undefined ) {

62.          //给target增加属性或方法

63.          target[ name ] = copy;

64.        }

65.      }

66.    }

67.  }

68. 

69.  //返回

70.  return target;

71.};

 

部分内容借鉴网上博客资源,记不太清了,先谢谢了……

本文由美高梅网址发布于关于美高梅,转载请注明出处:jQuery中extend函数详解,写自已的js类库需要的核心

上一篇:javascript对数组的常用操作代码,javascript数组操作 下一篇:没有了
猜你喜欢
热门排行
精彩图文