深入理解jquery中extend的实现(2)

那么深度拷贝就不是拷贝引用地址,而是实实在在的复制一份新对象给新的变量。 在上面使用$.extend()中,都是使用的浅度拷贝,因此若后面的参数值是object类型或array类型,修改_default(target)的值,就会影响后面参数的值。

如我们使用getOpt(_default, obj1, obj2, obj3);得到的_default值是{name: “obj2”, age: “67”, sex: {error: “sorry, I dont't kown”}},可是若:

_default.sex.error = 'hello world';

那么obj3.sex.error也会跟着修改,因为obj3.sex是一个object类型。

不过$.extend()也提供了深度拷贝的方法:jQuery.extend( [deep ], target, object1 [, objectN ] ) 。若第一个参数是boolean类型,且值是true,那么就会把第二个参数作为目标参数进行合并。

var obj = {name:'wenzi', score:80}; var obj1 = {score:{english:80, math:90}} $.extend(true, obj, obj1); obj.score.english = 10; console.log(obj.score.english); // 10 console.log(obj1.score.english); // 80

执行后我们发现,无论怎么修改obj.score里的值,都不会影响到obj1.score了。

2. jQuery中extend实现原理

其实不看源码,对extend大致的过程应该也是了解的:对后一个参数进行循环,然后把后面参数上所有的字段都给了第一个字段,若第一个参数里有相同的字段,则进行覆盖操作,否则就添加一个新的字段。

下面是jQuery中关于extend的源码,我就在源码上进行注释讲解了,随后再在后面进行总结:

// 为与源码的下标对应上,我们把第一个参数称为`第0个参数`,依次类推 jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, // 默认第0个参数为目标参数 i = 1, // i表示从第几个参数凯斯想目标参数进行合并,默认从第1个参数开始向第0个参数进行合并 length = arguments.length, deep = false; // 默认为浅度拷贝 // 判断第0个参数的类型,若第0个参数是boolean类型,则获取其为true还是false // 同时将第1个参数作为目标参数,i从当前目标参数的下一个 // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; // Skip the boolean and the target target = arguments[ i ] || {}; i++; } // 判断目标参数的类型,若目标参数既不是object类型,也不是function类型,则为目标参数重新赋值 // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) { target = {}; } // 若目标参数后面没有参数了,如$.extend({_name:'wenzi'}), $.extend(true, {_name:'wenzi'}) // 则目标参数即为jQuery本身,而target表示的参数不再为目标参数 // Extend jQuery itself if only one argument is passed if ( i === length ) { target = this; i--; } // 从第i个参数开始 for ( ; i < length; i++ ) { // 获取第i个参数,且该参数不为null和undefind,在js中null和undefined,如果不区分类型,是相等的,null==undefined为true, // 因此可以用null来同时过滤掉null和undefind // 比如$.extend(target, {}, null);中的第2个参数null是不参与合并的 // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { // 使用for~in获取该参数中所有的字段 // Extend the base object for ( name in options ) { src = target[ name ]; // 目标参数中name字段的值 copy = options[ name ]; // 当前参数中name字段的值 // 若参数中字段的值就是目标参数,停止赋值,进行下一个字段的赋值 // 这是为了防止无限的循环嵌套,我们把这个称为,在下面进行比较详细的讲解 // Prevent never-ending loop if ( target === copy ) { continue; } // 若deep为true,且当前参数中name字段的值存在且为object类型或Array类型,则进行深度赋值 // Recurse if we're merging plain objects or arrays if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { // 若当前参数中name字段的值为Array类型 // 判断目标参数中name字段的值是否存在,若存在则使用原来的,否则进行初始化 if ( copyIsArray ) { copyIsArray = false; clone = src && jQuery.isArray(src) ? src : []; } else { // 若原对象存在,则直接进行使用,而不是创建 clone = src && jQuery.isPlainObject(src) ? src : {}; } // 递归处理,此处为2.2 // Never move original objects, clone them target[ name ] = jQuery.extend( deep, clone, copy ); // deep为false,则表示浅度拷贝,直接进行赋值 // 若copy是简单的类型且存在值,则直接进行赋值 // Don't bring in undefined values } else if ( copy !== undefined ) { // 若原对象存在name属性,则直接覆盖掉;若不存在,则创建新的属性 target[ name ] = copy; } } } } // 返回修改后的目标参数 // Return the modified object return target; };

源码分析完了,下面我们来讲解下源码中存在的几个难点和重点。

2.1 若参数中字段的值就是目标参数,停止赋值

在源码中进行了一下这样的判断:

// Prevent never-ending loop if ( target === copy ) { continue; }

为什么要有这样的判断,我们来看一个简单的例子,如果没有这个判断会怎么样:

var _default = {name : 'wenzi'}; var obj = {name : _default} $.extend(_default, obj); console.log(_default);

输出的_default是什么呢:

_default = {name : _default};

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwjpxz.html