1.1     分析$.extend源码

在分析源码之前,我还要加一段s测试代码,代码如下:

<script type="text/javascript">
$(document).ready(function(){console.log('==================测试06 start');var targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};var resobj = $.xjcopy(targetobj,srcobj);console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}console.log(targetobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};resobj = $.xjcopy(false,targetobj,srcobj);console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}console.log(targetobj);//Object { id="NO1111", name="xiajun", age=23, sex="man",comment=Object { test01="t01", test02="t02", test03="t03"}}targetobj = {'id':'NO1111','name':'xiajun','age':23,'sex':'man','comment':{'test01':'t01','test02':'t02','test03':'t03'}},srcobj = {'id':'NO1122','name':'sharp','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};resobj = $.xjcopy(true,targetobj,srcobj);console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test03="t03",test04="t04"}}console.log(targetobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test03="t03",test04="t04"}}console.log('==================测试06 end');
});
</script>

  我前面写的测试实例里没有大对象里包含小对象的情况,而深浅拷贝关键场景就是对象包含对象的特殊场景,因此这里把这种场景补上。从打印出来的结果我们看到当我们不设置deep属性时候,非comment属性的值是targetobj和srcobj合并的结果,而comment的返回值是srcobj的comment的值,同时extend方法的返回值和target的值是相同;当我们设定了deep属性的值,如果deep值为false时候,我们发现extend的返回值和不设定deep属性时候值是一样的,但是targetobj的值是不会改变的,有个朋友问我,当deep属性设置为false,我们看到targetobj值没变,但是如果srcobj的属性个数超过了targetobj的个数,那么srcobj的多余属性会不会合并到targetobj呢?为了这个问题我再写了一个测试,代码如下:

    targetobj = {'id':'NO1111','name':'xiajun','age':23,'comment':{'test01':'t01','test02':'t02','test03':'t03'}},srcobj = {'id':'NO1122','name':'sharp','sex':'man','comment':{'test01':'tt001','test02':'tt002','test04':'t04'}};resobj = $.xjcopy(false,targetobj,srcobj);console.log(resobj);//Object { id="NO1122", name="sharp", age=23, sex="man",comment=Object { test01="tt001", test02="tt002", test04="t04"}}console.log(targetobj);//Object { id="NO1111", name="xiajun", age=23, comment=Object { test01="t01", test02="t02", test03="t03"}}

  我们发现targetobj的值的确没变;如果我们把deep的值设置为true,那么从结果我们看到targetobj和extend方法的返回值都是被合并后的结果。

  呵呵,对于extend方法的使用现在比较清晰了吧,我们看测试结果会发现:

  jQuery里的extend方法存在着bug,如果我们不设定一个参数deep的值最终结果当然不是deep为true所代表的深拷贝,但是它和deep属性设为false时候结果也有不同,目标对象target一个被改变一个没有任何变化,所以不设定deep属性的值也不能说是deep设为false的默认操作,这个应该算是extend方法的bug吧

  

  下面我们读读extend方法的源码,看看到底是什么样的原因产生了这样的结果。

  源码的第一部分是为extend方法内部设定一些需要使用的局部变量,代码如下:

        // options是用来存储拷贝对象的源对象的临时变量,name是拷贝对象的属性值// src 用来存储拷贝对象目标的值,copy存储被拷贝对象的目标值比如我们示例代码// 里的 src = targetobj['id'],copy = targetobj['id']// copyIsArray是个布尔值,用来存储对象是不是数组的标记// clone是作为合并的的临时对象(这个大家看深拷贝的代码慢慢体会了)// target这个是我们的extend参数的target// i是extend参数objectN在arguments里的索引值// length是指参数个数,deep深浅拷贝的标记,大家可以看到默认下deep是falsevar options, name, src, copy, copyIsArray, clone,target = arguments[0] || {},i = 1,length = arguments.length,deep = false;

讲解写在注释里的这里就不在累述了,我们接着读下面的代码:

        // 如果第一个参数是布尔值,那么这是用户在设定是否要进行深浅拷贝if ( typeof target === "boolean" ) {deep = target;//设定deep的值,这个好理解target = arguments[1] || {};//如果设定deep属性,那么target要重新赋值// 如果第一个参数设置的深浅拷贝标记,那么i设为2,表明arguments的objectN参数是从索引为2的值开始i = 2;}

  注释比较清晰,这里也不啰嗦了。

  不过这段代码是有问题,是有bug的。这段代码也是我们不设置第一个参数deep值和deep值设为false最后结果不一致所在,其实代码作者的原意是deep不被设置时候的结果和deep设置为false是一样的。但是如果参数为false,typeof判断类型不是boolean而是object

  大家看下面的测试代码就明白其中道理了:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>无标题文档</title>
</head><body>
</body>
</html>
<script type="text/javascript">
window.onload = function(){//alert(typeof false);//boolean//alert(typeof true);//boolean
    typeftn(false,{},{});typeftn(true,{},{});
}function typeftn(deep,target,src){var bobj = arguments[0] || {};//console.log(typeof bobj);//如果是false,结果是object,如果是true结果就是boolean
    alert(typeof bobj);
}
</script>

先搁置一下这个bug,我们接着读extend源码:

    // 如果传入的不是对象或者是函数,可能为字符串,那么把target = {}置为空对象// extend最终返回值是target,我们看到target在方法里都是被设置为对象,所以不管我们传什么样的// 的参数到extend方法里,最终结果都是object类型,这也说明如果objectN参数是对象,extend同样会做合并操作// 如果objectN非object类型那么下面深浅拷贝操作也就没意义了if ( typeof target !== "object" && !$.isFunction(target) ) {target = {};}

下面的代码就是为什么extend可以编写插件的原因了,代码如下:

        // 当参数只有一个时候,target被设置为this,这里就是关键所在,这里的解释我要写在文档里if ( length === i ) {target = this;--i;}

  当我们只传一个参数并且这个参数是object类型,那么target把设置为this,所以当我们按jQuery.extend方式调用extend方法,那么this会指向jQuery对象,代码后面写—i,那么表明当我们只传一个参数时候,这个参数不是target参数而是变成了objectN参数,成为了被拷贝对象,最终参数的内容会被拷贝到jQuery对象内部,最终成为jQuery的全局对象的一个属性。

  接下来的代码就是做深浅拷贝的代码,这个代码我在前面已经写过,这里也不多讲了,代码如下:

    // 下面的代码就是javascript里做深浅拷贝的代码,这个和我们前面自己写的深浅拷贝的代码类似for ( ; i < length; i++ ) {// 只操作对象值非null/undefined的数据if ( (options = arguments[i]) != null ) {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 ] = $.xjcopy( deep, clone, copy );} else if ( copy !== undefined ) {target[ name ] = copy;}}}}// 返回结果return target;

下面我将我写的和extend方法一模一样的的xjcopy方法改写下,修正extend方法里的bug,代码如下:

;(function($){$.xjcopy = $.fn.xjcopy = function(){// options是用来存储拷贝对象的源对象的临时变量,name是拷贝对象的属性值// src 用来存储拷贝对象目标的值,copy存储被拷贝对象的目标值比如我们示例代码// 里的 src = targetobj['id'],copy = targetobj['id']// copyIsArray是个布尔值,用来存储对象是不是数组的标记// clone是作为合并的的临时对象(这个大家看深拷贝的代码慢慢体会了)// target这个是我们的extend参数的target// i是extend参数objectN在arguments里的索引值// length是指参数个数,deep深浅拷贝的标记,大家可以看到默认下deep是falsevar options, name, src, copy, copyIsArray, clone,target = arguments[0] || {},i = 1,length = arguments.length,deep = false;// 如果第一个参数是布尔值,那么这是用户在设定是否要进行深浅拷贝if (length >2){if (deep === false || deep === true){deep = target;//设定deep的值,这个好理解target = arguments[1] || {};//如果设定deep属性,那么target要重新赋值// 如果第一个参数设置的深浅拷贝标记,那么i设为2,表明arguments的objectN参数是从索引为2的值开始i = 2;                }}/*if ( typeof target === "boolean") {deep = target;//设定deep的值,这个好理解target = arguments[1] || {};//如果设定deep属性,那么target要重新赋值// 如果第一个参数设置的深浅拷贝标记,那么i设为2,表明arguments的objectN参数是从索引为2的值开始i = 2;}*/// 如果传入的不是对象或者是函数,可能为字符串,那么把target = {}置为空对象// extend最终返回值是target,我们看到target在方法里都是被设置为对象,所以不管我们传什么样的// 的参数到extend方法里,最终结果都是object类型,这也说明如果objectN参数是对象,extend同样会做合并操作// 如果objectN非object类型那么下面深浅拷贝操作也就没意义了if ( typeof target !== "object" && !$.isFunction(target) ) {target = {};}// 如果传入的参数只有一个,跳过下面的步骤// 当参数只有一个时候,target被设置为this,这里就是关键所在,这里的解释我要写在文档里if ( length === i ) {target = this;--i;}    // 下面的代码就是javascript里做深浅拷贝的代码,这个和我们前面自己写的深浅拷贝的代码类似for ( ; i < length; i++ ) {// 只操作对象值非null/undefined的数据if ( (options = arguments[i]) != null ) {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 ] = $.xjcopy( deep, clone, copy );} else if ( copy !== undefined ) {target[ name ] = copy;}}}}// 返回结果return target;    };
})(jQuery)

  关于extend的内容我这里讲完了。这里我要说明下,jQuery里extend方法并不代表着jQuery插件技术,只能说extend是实现jQuery插件技术的一种手段。jQuery的插件技术还有很多内容,其中就包括不使用extend实现插件的方式,关于插件的详细内容我会在以后的博客里写道。

  最后我还想说说,对jQuery插件技术的深入理解可能是理解jQuery源码的一把很重要的钥匙,等我写完了对jQuery插件技术的介绍,我就会继续我临摹jQuery的系列,好好分析下jQuery的源码。

转载于:https://www.cnblogs.com/sharpxiajun/archive/2012/05/06/2486509.html

Javascript笔记:(实践篇)从jQuery插件技术说起-分析extend方法的源码(发现extend方法里有bug)(下篇)...相关推荐

  1. Javascript笔记:(实践篇)从jQuery插件技术说起(上篇)

    最近写了个网站,当时借鉴了很多相关网站前端技术,为了让客户的体验更加好,我在网站前端加入了相当多的校验代码,因此代码显的特别臃肿.虽然开发过程中我将前端代码重构了三次,但是我还是对我原来写的代码不满意 ...

  2. JAVA计算机毕业设计基于vue技术的汽车维修检测系统设计与实现源码+数据库+系统+lw文档

    JAVA计算机毕业设计基于vue技术的汽车维修检测系统设计与实现源码+数据库+系统+lw文档 JAVA计算机毕业设计基于vue技术的汽车维修检测系统设计与实现源码+数据库+系统+lw文档 本源码技术栈 ...

  3. 【vn.py学习笔记(八)】vn.py utility、BarGenerator、ArrayManager源码阅读

    [vn.py学习笔记(八)]vn.py utility.BarGenerator.ArrayManager源码阅读 写在前面 1 工具函数 2 BarGenerator 2.1 update_tick ...

  4. DIV布局艺购艺术品商城网页(1页) HTML+CSS+JavaScript 学生DW网页 使用html+css实现一个静态页面(含源码)

    HTML5期末大作业:艺术品商城网站设计--艺购艺术品商城网页(1页) HTML+CSS+JavaScript 学生DW网页 使用html+css实现一个静态页面(含源码) 常见网页设计作业题材有 个 ...

  5. 论文阅读笔记——基于CNN-GAP可解释性模型的软件源码漏洞检测方法

    本论文相关内容 论文下载地址--Engineering Village 论文阅读笔记--基于CNN-GAP可解释性模型的软件源码漏洞检测方法 文章目录 本论文相关内容 前言 基于CNN-GAP可解释性 ...

  6. JavaScript实现递归楼梯问题(迭代解决方案)算法(附完整源码)

    JavaScript实现递归楼梯问题(迭代解决方案)算法(附完整源码) recursiveStaircaseIT.js完整源代码 recursiveStaircaseIT.test.js完整源代码 r ...

  7. JavaScript实现递归楼梯问题(动态规划解决方案)算法(附完整源码)

    JavaScript实现递归楼梯问题(动态规划解决方案)算法(附完整源码) recursiveStaircaseDP.js完整源代码 recursiveStaircaseDP.test.js完整源代码 ...

  8. JavaScript实现递归楼梯问题(蛮力解决方案)算法(附完整源码)

    JavaScript实现递归楼梯问题(蛮力解决方案)算法(附完整源码) recursiveStaircaseBF.js完整源代码 recursiveStaircaseBF.test.js完整源代码 r ...

  9. JavaScript实现找出买卖股票的最大利润算法(附完整源码)

    JavaScript实现找出买卖股票的最大利润算法(附完整源码) dpBestTimeToBuySellStocks.js完整源代码 dpBestTimeToBuySellStocks.test.js ...

最新文章

  1. VISTA中注册表项LEGACY_****的删除
  2. android 4.4 禁止下拉,Android开发中禁止下拉式的实现技巧
  3. 企业级Nginx服务基础到架构优化详解--25条
  4. 创业星光论坛(上):汇源是否应在万荣建厂
  5. 日志分割工具cronolog
  6. Java REST JAX-RS 2.0 –如何处理日期,时间和时间戳记数据类型
  7. 查询SQLSERVER执行过的SQL记录(历史查询记录)
  8. linux命令逻辑运算:与、或、非、异或
  9. TypeScript学习(一):原始数据类型的定义
  10. QBC检索和本地SQL检索
  11. #AI边缘计算单元-想搞开发,买树莓派还是Nano?
  12. 流程型与离散型制造的区别【老外的分析】
  13. 酷睿i7 11800h和r7 5800h参数对比 锐龙r75800h和酷睿i711800h选哪个好
  14. iphone 传android,安卓和苹果手机怎么互传文件_安卓与苹果手机之间互传文件的方法教程_3DM手游...
  15. 彻底关闭FF新闻资讯
  16. Chapter7 机器人导航仿真(Ⅰ)----导航实现
  17. 【Windows】如何修改远程桌面端口3389
  18. LC39 Combination Sum
  19. rfid中间件软件的应用实例_RFID原理与应用教与学(教学大纲)
  20. excel中引用power bi模型数据

热门文章

  1. 哈工大成立人工智能研究院,NLP全国第三
  2. 深度学习(五十二)变分贝叶斯自编码器(下)
  3. mysql text 函数的使用方法_MySQL空间数据操作:GeomFromText()和astext()函数报错解决...
  4. java 测试工具 oracle_SwingBench---ORACLE压力测试工具
  5. 2017年15佳Android黑客应用
  6. 基于JBox2d物理引擎和canvas的游戏开发实例
  7. MySQL笔记(三)常用系统函数
  8. day10 Python 形参顺序
  9. lc 115. Distinct Subsequences
  10. 二、源代码=程序集及程序集概念介绍