jQuery中extend()和fn.extend()方法详解

   jQuery自定义了jQuery.extend()和jQuery.fn.extend()方法.其中jQuery.extend()方法能够创建全局函数或者选择器,而jQuery.fn.extend()方法能够创建jQuery对象方法.

  这两个方法用的是相同的代码,一个用于给jQuery对象或者普通对象合并属性和方法一个是针对jQuery对象的实例,对于基本用法举几个例子:

 html代码如下:

 

 代码如下:

<!doctype html>
<html>
<head>
<title></title>
<script src='jquery-1.7.1.js'></script>
</head>
<body>
<img src=''/>
</body>
</html>

  下面写js里面的用法:

  合并两个普通对象

  代码如下:

  //给两个普通对象合并属性

  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对象添加属性或者方法

  代码如下:

  $.extend({hehe:function(){alert('hehe');}});

  $.hehe(); //alert('hehe')

  这个用法很重要,是jQuery内部添加实例属性和方法以及原型属性和方法的实现方法也是编写jQuery插件的方法,下面是jQuery1.7.1中使用extend方法扩展自己的方法和属性

   代码如下:

  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,

  .....

  在这个例子中只传入了一个对象参数,那么默认就把this当做待合并修改的对象

  给jQuery对象实例添加属性或者方法

  代码如下:

  //针对jQuery实例扩展合并

  console.log($('img').extend({'title':'img'}));//[img, img#img.img, prevObject: jQuery.fn.jQuery.init[1], context: document, selector: "img", title: "img", constructor: function…]

  只合并不修改待合并对象

   代码如下:

  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}

  默认情况下,待合并对象跟返回结果一样是被修改了的,如果仅仅想得到一个合并后的对象又不想破坏任何一个原来的对象可以使用此方法

  代码如下:

  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"}

  详细的使用方法可以看参考手册http://www.w3cschool.cc/manual/jquery/

  下面来分析下1.7.1源码中是怎么实现的:

  代码如下:

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

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

  target = arguments[0] || {},

  i = 1,

  length = arguments.length,

  deep = false;

  ...

  }

  首先是定义了一组变量,因为参数个数不确定所以就直接调用arguments对象访问传递的参数

  变量 options:指向某个源对象。

  ‰ ‰ 变量 name:表示某个源对象的某个属性名。

  ‰ ‰ 变量 src:表示目标对象的某个属性的原始值。

  ‰ ‰ 变量 copy:表示某个源对象的某个属性的值。

  ‰ ‰ 变量 copyIsArray:指示变量 copy 是否是数组。

  ‰ ‰ 变量 clone:表示深度复制时原始值的修正值。

  ‰ ‰ 变量 target:指向目标对象。

  ‰ ‰ 变量 i:表示源对象的起始下标。

  ‰ ‰ 变量 length:表示参数的个数,用于修正变量 target。

  ‰ ‰ 变量 deep:指示是否执行深度复制,默认为 false。

  为了更好地了解代码实现这里以上面举的一个例子作为演示观察源代码执行情况

   代码如下:

  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;

  }

  判断是不是深度复制,如果第一个参数是布尔值那么就把第一个参数的值给deep,然后把第二个参数作为目标对象,如果第二个参数不存在就赋值为一个空对象,把源对象的下标改为2,在这个例子里面 是走这里的因为第一个参数是ture然后把deep变成了true ,target被修正成了第二个参数也即是obj1,源对象的起始下标为2就是从第三个开始作为源对象也就是本例中的obj2

   代码如下:

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

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

  target = {};

  }

  这里对target又进一步进行了处理对于非对象和函数的数据类型而言增加自定义属性是无效的比如字符串自能调用自带的方法和属性

  代码如下:

  // extend jQuery itself if only one argument is passed

  if ( length === i ) {

  target = this;

  --i;

  }

  如果length属性等于i的值那就表示没有目标对象存在,正常情况下length应该是大于i的值的 ,那么这个时候就把this作为目标对象把i值减一实现length值大于i值(比i大1)

  这个就是jQuery给自己扩展属性的方法的实现原理,只要不传入目标对象就可以啦

  两种可能的情况:$.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;

  }

  }

  }

  }

  这个部分就是此方法的核心了,从arguements对象的第i个下标值开始循环操作首先过滤掉源对象是null或者是undefined的情况可以看到其实

  源对象不一定真的就是对像,也可以是其他类型的值比如字符串比如这样写:

  代码如下:

  console.log($.extend({'name':'tom'},'aa')); //Object {0: "a", 1: "a", name: "tom"}

  是不是感觉很奇怪啊?究竟是怎么实现的呢?下面接着看

  过滤完之后开始进行for循环 src保存的是目标对象的某个键的值,copy属性保存的源对象的某个键的值,这两个键都是一样的

  代码如下:

  // Prevent never-ending loop

  if ( target === copy ) {

  continue;

  }

  如果源对象的某个属性值就是目标对象可能会造成死循环导致程序崩溃所以这里做了一个限制让其跳过此次循环例如:

  代码如下:

  var o = {};

  o.n1 = o;

  $.extend( true, o, { n2: o } );

  // 抛出异常:

  // Uncaught RangeError: Maximum call stack size exceeded

  但是这样做也会冤枉一些正常的情况比如:

  代码如下:

  var obj1={a:'a'}

  var obj2={a:obj1};

  console.log($.extend(obj1,obj2)); //Object {a: "a"}

  这种情况也是满足源对象值等于目标对象的但是结果发现obj1的a的属性值并没有被修改,就是因为执行了continue,下面把源码的这段话注释掉在执行

  代码如下:

  Object {a: Object}

  这个时候就是正常被修改了个人感觉这个地方需要改进;

  接着就是一个if判断就是区分是不是进行深度复制的先不看深度复制的先看一般的

  代码如下:

  target[ name ] = copy;

  很简单就是只要copy有值就直接复制给目标对象,目标对象有的就修改没有就增加,这样就实现了合并啦。

  for循环之后在把新的目标对象返回,所以目标对象最后是被修改的,而且结果和返回的结果是一样的。

  代码如下:

  // Return the modified object

  return target;

  };

  下面再来说说深度复制了怎么去处理

  首先保证deep是true,copy有值并且是对象或者数组(如果不是对象和数组深度复制也就无从谈起)然后再分数组和对象来处理,先来看数组的情况:

  代码如下:

  if ( copyIsArray ) {

  copyIsArray = false;

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

  } else {

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

  }

  如果是数组copyIsArray的值为真然后走里面的 把值改成false ,针对当前循环的源对象属性,目标对象可能有也可能没有,有的话判断一下是不是数组是的话就是原来的数组不变不是的话就让它变成一个数组,因为既然源对象的当前属性是数组最后目标元素也必须是数组。不是数组就是对象把目标对象当前属性改成对象。

  代码如下:

  // Never move original objects, clone them

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

  然后把源对象的当前属性值(是数组或对象)和已经被改造过的目标对象的当前属性进行递归合并最后返回的新的数组或者对象赋值给目标对象,最终实现了深度复制。

  但是这里面还有一个比较奇怪的现象,比如这样操作:

  代码如下:

  console.log($.extend({a:1},'aa')); //Object {0: "a", 1: "a", a: 1}

  原来源对象不一定真的是对象e而且居然可以把字符串拆开跟目标对象合并,原来for...in循环是操作字符串的

  代码如下:

  var str='aa';

  for(var name in str){

  console.log(name);

  console.log(str[name])

  }

  这样也是可以的,会把字符串拆开按数字下标读取,但是在源码中

  代码如下:

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

  是有数组和对象限制的,那么深度复制的时候是不是就没有效果了呢?

  经过我测试深度复制也是可以的,因为在源码里面copy的值竟然变成了匿名函数函数

  alert(jQuery.isPlainObject(copy)); //true

  至于为什么是函数笔者还没搞清楚留待以后解决吧!

时间: 2024-12-22 12:55:33

jQuery中extend()和fn.extend()方法详解的相关文章

JavaScript中关键字 in 的使用方法详解_javascript技巧

for-in循环应该用在非数组对象的遍历上,使用for-in进行循环也被称为"枚举". 对于数组 ,迭代出来的是数组元素 但不推荐,因为不能保证顺序,而且如果在Array的原型上添加了属性,这个属性也会被遍历出来,所以 最好数组使用正常的for循环,对象使用for-in循环 对于对象 ,迭代出来的是对象的属性: var obj = { "key1":"value1", "key2":"value2", &q

Lua中break语句的使用方法详解

  这篇文章主要介绍了Lua中break语句的使用方法详解,是Lua入门学习中的基础知识,需要的朋友可以参考下 当循环中遇到break语句,循环立即终止,程序控制继续下一个循环语句后面. 如果您正在使用嵌套循环(即一个循环里面另一个循环),break 语句将停止最内层循环的执行并开始执行的下一行代码的程序后段. 语法 Lua break语句语法如下: 代码如下: break 例子: 代码如下: --[ local variable definition --] a = 10--[ while l

jquery中的ajax同步和异步详解_AJAX相关

之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题.最近的项目用了到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出去假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面假死状态解除. 而异步则这个AJAX代码运行中的时候其他代码一样可以运行. jquery的async:false,这个属性 默认是true:异步,false:同步. $.ajax({ type: "post", url: "

js基础之DOM中元素对象的属性方法详解_javascript技巧

在 HTML DOM (文档对象模型)中,每个部分都是节点. 节点是DOM结构中最基本的组成单元,每一个HTML标签都是DOM结构的节点. 文档是一个    文档节点 . 所有的HTML元素都是    元素节点 所有 HTML 属性都是    属性节点 文本插入到 HTML 元素是    文本节点 注释是    注释节点. 最基本的节点类型是Node类型,其他所有类型都继承自Node,DOM操作往往是js中开销最大的部分,因而NodeList导致的问题最多.要注意:NodeList是'动态的',

Javascript中的迭代、归并方法详解_基础知识

迭代方法 在Javascript中迭代方法个人觉得尤为重要,在很多时候都会有实际上的需求,javascript提供了5个迭代方法来供我们操作,它们分别为: every() 对数组中的每一个项运用给定的函数,如果每项都返回true,那么就会返回true filter() 对数组中的每一个项运用给定的函数,把返回true的项组成一个新数组并返回 forEach() 对数组中的每一项运用给定的函数,但是没有任何的返回值 map() 对数组中的每一个项运用给定的函数并返回每次函数调用的结果组成新的数组

Struts中使用validate()输入校验方法详解_java

1.在ActionSupport中有一个validate()方法,这个方法是验证方法,它会在execute()方法执行之前执行,所以能够起到很好的验证的作用. @Override //重写Action中的validate()方法 public void validate() { if(null==this.username||this.username.length()<4||this.username.length()>6){ this.addActionError("userna

Mongodb中MapReduce实现数据聚合方法详解_MongoDB

Mongodb是针对大数据量环境下诞生的用于保存大数据量的非关系型数据库,针对大量的数据,如何进行统计操作至关重要,那么如何从Mongodb中统计一些数据呢? 在Mongodb中,给我们提供了三种用于数据聚合的方式: (1)简单的用户聚合函数: (2)使用aggregate进行统计: (3)使用mapReduce进行统计: 今天我们首先来讲讲mapReduce是如何统计,在后续的文章中,将另起文章进行相关说明. MapReduce是啥呢?以我的理解,其实就是对集合中的各个满足条件的文档进行预处理

jquery中的ajax同步和异步详解

之前一直在写JQUERY代码的时候遇到AJAX加载数据都需要考虑代码运行顺序问题.最近的项目用了到AJAX同步.这个同步的意思是当JS代码加载到当前AJAX的时候会把页面里所有的代码停止加载,页面出去假死状态,当这个AJAX执行完毕后才会继续运行其他代码页面假死状态解除. 而异步则这个AJAX代码运行中的时候其他代码一样可以运行. jquery的async:false,这个属性 默认是true:异步,false:同步. $.ajax({ type: "post", url: "

jQuery给元素添加样式的方法详解_jquery

本文实例讲述了jQuery给元素添加样式的方法.分享给大家供大家参考,具体如下: 1.获取和设置样式 $("#tow").attr("class")//获取ID为tow的class属性 $("#two").attr("class","divClass")//设置Id为two的class属性. 2.追加样式 复制代码 代码如下: $("#two").addClass("divCl

必知技巧:ASP.NET中常用的优化性能方法详解

asp.net|技巧|详解|性能|优化 1. 数据库访问性能优化 数据库的连接和关闭 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器资源.ASP.NET中提供了连接池(Connection Pool)改善打开和关闭数据库对性能的影响.系统将用户的数据库连接放在连接池中,需要时取出,关闭时收回连接,等待下一次的连接请求. 连接池的大小是有限的,如果在连接池达到最大限度后仍要求创建连接,必然大大影响性能.因此,在建立数据库连接后