美文网首页
四(补充)、jQuery中的继承方法extend(庖丁解牛版)

四(补充)、jQuery中的继承方法extend(庖丁解牛版)

作者: 雪燃归来 | 来源:发表于2020-06-12 15:22 被阅读0次

       深入学习jQuery源码后,才晓得了jQuery设计的优美和巧妙。我在前面虽然已经写过了jQuery.extend方法源码的相关文章,勉强还是能看的,但不是太令人满意,所以我在本篇中对其进行补充说明,虽然这将消耗我大量的时间(写博客真的很费时间)。

       jQuery.extend方式可以是jQuery的静态工具方法,也可以是jQuery对象的实例方法,简单的说,就是既可以通过$.extend()的形式调用,也可以通过$().extend()的形式调用。之所以这么说,是因为其源码中的第一行代码。

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

一、extend方法的使用场景

1、jQuery中扩展插件

       当给extend方法传递一个参数(对象字面量)的时候,我们就可以给jQuery扩展插件了。

$.extend({
   aaa: function(){
      alert('aaa')
   },
   bbb:function(){
      alert('bbb')
   }
})
$.fn.extend({
    ccc:function(){
        alert('ccc')
    },
     ddd:function(){
        alert('eee')
     }
})
// 将依次弹窗 aaa、bbb、ccc、ddd
$.aaa()
$.bbb()
$().ccc()
$().ddd()

       这两种方式定义的方式差别在于,调用它们的this对象不一致。

$.extend() -> this -> $ ->this.aaa -> $.aaa()      // this对象是jQuery/$(jQuery构造函数的静态方法)

$.fn.extend() -> this -> this.ccc -> $().ccc()       // this对象是JQ对象

2、合并多个对象

       当给extend传递多个参数(对象字面量)的时候,第2个参数到最后一个参数的属性全都会扩展到第一个参数上面。

var a = {}
$.extend( a, { name: 'hello'}, { age: 30})
console.log(a)  //{name: "hello", age: 30}

3、实现深拷贝和浅拷贝

       对于深拷贝和浅拷贝的概念,相信大家都比较熟悉了,在jQuery中,我们可以通过给extend的第一个参数传递一个boolean来控制实现是深拷贝还是浅拷贝。

//   浅拷贝
var a = {}
var b = { name: { age: 18 }}
$.extend( true, a, b)
a.name.age = 20
console.log(b.name.age)  //20

// 深拷贝
var a = {}
var b = { name: { age: 18 }}
$.extend( true, a, b)
a.name.age = 20
console.log(b.name.age)  //18

二、extend方法源码分析

       extend方法的源码不长,处理逻辑也不是很复杂,下面,我们现将其源码的逻辑结构进行简单梳理,然后按照具体的逻辑逐一进行说明。

jQuery.extend = jQuery.fn.extend = function(){
    //   定义一些变量
    if(){}        看是不是深拷贝的情况
    if(){}        看参数正不正确
    if(){}        看是不是插件情况
    for(){       可能有多个对象的情况
      if(){}      防止循环引用
      if(){}      深拷贝
      else if(){}   钱拷贝
    }
}

1、定义了一些初始变量值

var options, name, src, copy, copyIsArray, clone,
    target = arguments[0] || {},
    i = 1,
    length = arguments.length,
    deep = false;

       对于这些定义的变量,我们要关心他们的初始值。比如target的值。

2、处理深度复制的情形

// Handle a deep copy situation
if ( typeof target === "boolean" ) {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    i = 2;
}

       默认情况下进行的是浅拷贝,也就是说第一个参数可传也可以不传。如果要传,必须是boolean类型的值,这时,我们就需要矫正target的值了,因为我们默认的target值是第一个参数,顺便将下面要在for循环中使用的i的修正为2.

3、验证参数的合法性

// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
    target = {};
}

       要确保target为对象或者函数。

4、处理只传入一个参数的情况

// extend jQuery itself if only one argument is passed
if ( length === i ) {
    target = this;
    --i;
}

       变量i递减问0,不再进行下面的for循环,也就是处理插件的情况。

5、处理传入多个参数的情况

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

       代码也对复杂,我们继续分解。👉

5.1 防止循环引用
// Prevent never-ending loop
if ( target === copy ) {
    continue;
}

       假如注释掉这段代码,执行下面的代码,会有怎样的效果呢。看到了吧,无限循环引用,具体原因您需要脑补一下。

var a = {};
var b = { name: a }
console.log($.extend(a, b))

       执行结果如下:


循环引用
5.2 实现深拷贝
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 );
}

       深拷贝用到了递归,这里我们纠正一个错误,extend不仅可以拷贝对象字面量,也可以拷贝数组。不信,你可以尝试一下。

5.3 实现浅拷贝
else if ( copy !== undefined ) {
    target[ name ] = copy;
}

       浅拷贝没有什么特别的,直接拷贝即可。

       至此,extend方法已经被我庖丁解牛般的解析完了,整体还是比较容易的。从中可以看出,jquery中的继承我们采用了拷贝继承,并没有使用我们经典的继承方式,当然,你可以通过查阅《红宝书》来学习Javascript中经典的继承方式。

       最后,感谢您的阅读,祝你学习进步!

相关文章

网友评论

      本文标题:四(补充)、jQuery中的继承方法extend(庖丁解牛版)

      本文链接:https://www.haomeiwen.com/subject/wdwgtktx.html