一.2-extend-用法
Posted re-is-good
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一.2-extend-用法相关的知识,希望对你有一定的参考价值。
源码 285-347行
目录
一. extend前言
二. extend用法
三: 源码解析
四: 深拷贝的小demo
一. extend前言
extend 方法可以帮我们扩展jQuery的方法(jQuery内部也是通过这个方法来扩展大量的其它方法)
因此我认为extend在jQuery源码中是十分重要的。因此先说一下extend的实现。
二. extend用法
1. 扩展jQuery工具方法(即在$函数上添加方法)
$.extend({ method: function(){ console.log("method") } }) // 调用 $.method(); // method $().method(); // 报错
2. 扩展jQuery实例方法(即在$的原型上添加方法)
// fn是prototype的简写(见源码96行) $.fn.extend({ method2: function(){ console.log("method2"); } }) // 调用 $.method2(); // 报错 $().method2(); // method2
3. 将其它对象扩展到某个对象身上
var obj = {}; $.extend(obj, {a: 1}, {b: 2}); console.log(obj); // {a: 1, b: 2} // 从第二个参数开始到最后一个参数,他们都会被扩展到obj对象身上。
4. 支持深浅拷贝
extend方法默认使用浅拷贝
如何进行深拷贝呢? 第一个实参为true即可
4.1. 浅拷贝的情况(extend默认)
var person = { info: { age: 18 } } var obj = {} $.extend(obj, person); person.info.age = 20; console.log(obj.info.age); // 20, 被顺带修改了
4.2. 深拷贝的情况
var person = { info: { age: 18 } } var obj = {} $.extend(true,obj, person); // 第一个实参为true person.info.age = 20; // 修改了 console.log(obj.info.age); // 18,完全不受影响
三: 源码解析
第一行
// 285行 jQuery.extend = jQuery.fn.extend = function () {
这两个extend方法的实现其实是同一个,那么它是怎么判断用户是通过$.extend调用的,还是通过$.fn.extend来调用的。
很简单,通过this指向。
286-290 定义一些变量
var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, // 如果没有传入参数,就给个默认值。避免报错 i = 1, length = arguments.length, deep = false; // 这是深浅拷贝的标志,false为浅拷贝,true为深拷贝
293-298 处理深浅拷贝的问题
if (typeof target === "boolean") { // 这里的 target是第一个参数的值 // 这种情况 $.extend(true, {"a":function(){}})}); deep = target; target = arguments[1] || {}; // 参数转移。deep的值目前便是第一个参数的值,(即 true), // 而target便是第二个参数(即对象 {"a":function(){}})}) i = 2; // 这个i便是决定下面for循环的起始位置(for (var i=2; )) }
306-309 如果只有一个对象时(那便是扩展jQuery工具方法或者扩展jQuery实例方法,见前面的第1,2个用法)
if (length === i) { target = this; --i; }
如这种情况 $.extend(true, {"a": function(){}}) 或者 $.extend({"a": function(){}});
那么获取当前的this,这也解释了前面的疑问? 为什么$.extend和$.fn.exntend 的实现是同一个函数
如果是 $.extend(true, {"a": function(){}})的话, i的值是不是2。但for循环遍历的开始位置应该是1(for(var i=1;)); 因此需要--i;
如果是 $.extend({"a": function(){}});的话,i的值便是1.但for循环的开始位置应该是0(for(var i=0)); 因此也要 --i;
这里做的还是很巧妙的
311-343 下面的代码便是一个大的for循环啦
可以注意到上面做的一些变量i的操作都是为了这个for循环起始位置。(保证for循环的第一个元素是一个对象)
// 311行 for (; i < length; i++) {
313 防止用户传入null, undefined ,毕竟这玩意扩展不了
// 313行 注意options已经在前面定义了 // $(undefined, {}), for循环遍历时都要判断下元素是不是null或者undefined if ((options = arguments[i]) != null) {
315-341
// 这里的options便是传入的一个个的对象。 例子 $.extend({a: function a(){}, b: function(){}})}); for (name in options) { // options 为 {a: function a(){}, b: function(){}})} src = target[name]; // 第一次循环 src = $["a"] copy = options[name]; // 第一次循环 copy = function a(){}; // Prevent never-ending loop if (target === copy) { // 防止循环引用(这种情况 var obj = {}; $.extend(obj, {prop: obj}); ) // target: obj; copy: obj。然后就会跳过循环,不用赋值了 continue; } if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) { // 深拷贝 见下面分析 // Don‘t bring in undefined values } else if (copy !== undefined) { // 浅拷贝 target[name] = copy; // 直接赋值就行 } }
深拷贝的情况(源码325-335)
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 : {}; // 这里为什么不直接赋值? clone = []; // 有可能是这种情况 // var obj = {a: {b: 1}}; $.extend(true, obj, {a: {c: 1}}); 如果直接clone = {}; // 那么 结果就是 {a: {c: 1}}。遇到相同的就被覆盖了。 } // Never move original objects, clone them target[name] = jQuery.extend(deep, clone, copy); // 递归调用 // Don‘t bring in undefined values }
什么样的情况才能进行深拷贝?
用户传入的第一个传入为 true(或者说truly, 即转布尔后为true)。
(copy)属性值要存在,不能说弄成这样的 $.extend({a: undefined});
(copy)属性值得是一个PlainObject(像 {a: 1}便是一个 PlainObject), 或者(copy) 属性值为 数组 (如 $.extend({a: [1, 2]}) )。
346行
return target;
返回这个target对象
四: 深拷贝的小demo
function deepObjCopy(obj) { var copyObj = Array.isArray(obj) ? [] : {}; for (key in obj) { if (obj.hasOwnProperty(key)) { if (obj[key] && (typeof obj[key] === "object")) { // 递归调用, 如[], {}情况。typeof null虽然是object但是转boolean后为false copyObj[key] = deepObjCopy(obj[key]); } else { // 其它数据类型 copyObj[key] = obj[key]; } } } return copyObj; } var obj = { a: { b: 1 } }; var copyedObj = deepObjCopy(obj); obj.a.b = 2; console.log(copyedObj.a.b); // 1 var obj = { a: [{ b: 1 }] }; var copyedObj = deepObjCopy(obj); // console.log(copyedObj) obj.a[0].b = 2; console.log(copyedObj.a[0].b); // 1
以上是关于一.2-extend-用法的主要内容,如果未能解决你的问题,请参考以下文章