jQuery源码阅读(八)---jQuery中的继承extend

来源:互联网 发布:js 创建key value数组 编辑:程序博客网 时间:2024/05/17 22:20

今天看了jQuery源码中关于extend函数的部分,这部分就相当于jQuery继承,后面在整理的过程中说明如何继承。
还是跟之前的分析方法一样,要分析一个函数的实现,首先得知道函数是干什么的,它的功能是啥。

extend方法应用

扩展插件

在jQuery的学习当中,我们学过如何去写一个插件,而且插件分全局插件和实例插件,即一种是直接挂载在jQuery上的,另一种是挂载在jQuery实例对象上的。
比如下面两种实现:
扩展全局插件:

$.extend({    add: function(a, b){        return a + b;    }});
$.add(4,9);           //返回13

对象插件:

$.fn.extend({    add : function(a, b){        return a + b;    }})
$().add(2,8)            //返回10

扩展对象

这也可以算是jQuery中实现继承的一种方式。

a = {    name : 'liu'}b = {    name: 'yang'}var res = $.extend(a, b)  //res可以说是继承了a和b的属性和方法////默认浅拷贝,改变a.name不会影响b.nameaa = {    name: {        age: 20    }}bb = {    name: {        age: 15    }}$.extend(aa, bb)//浅拷贝,只拷贝对象,不会把内部具体的属性也拷贝//所以修改a.name.age会改变b.name.ageaa.name.age = 30;//此时bb.name.age也是30//如果改用深拷贝的话$.extend(true, aa, bb)//aa和bb中的name是不同的对象,指向不同的内存区域//因此改变aa.name.age不会影响bb.name.age

extend方法在扩展对象时,最前面可以传一个布尔值的参数true,表示扩展对象时使用深拷贝,即将所有层级的对象属性都进行拷贝。而不加布尔值的参数,则默认是浅拷贝,即不会复制内层级的属性和方法。

extend源码分析

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

首先,我想说的是 jQuery.extend和jQuery.fn.extend是同一份代码,也就是说 实现思路是一样的.那如何才能确定是在jQuery上扩展的,还是在jQuery实例对象上扩展的?

JS中有个this的概念,即指向当前的上下文。具体点来说.

//直接调用函数时function a(){    return this;}a();                //返回的是window对象

这里写图片描述

//如果通过对象调函数var obj = {    b: function(){        return this;    }}obj.b();            //返回的是Object对象

这里写图片描述

所以如果$.extend(),this指向的是jQuery;,如果$.fn.extend()this指向jQuery.fn也就是jQuery.prototype,那么在原型上扩展一个方法,就相当于在jQuery实例中可以调用该方法,从而实现扩展的目的。

接着下面的源码:

    var options, name, src, copy, copyIsArray, clone,    //从这块可以学习到,如果不确定函数中有多少个参数时,可以用arguments来访问。    target = arguments[0] || {},    //如果是深拷贝,target此时为true;    //如果不是,target此时是要扩展的第一个参数;    i = 1,    length = arguments.length,    deep = false;    // 深拷贝的情况    if ( typeof target === "boolean" ) {        deep = target;        target = arguments[1] || {};        // 跳过true和要扩展的第一个参数        i = 2;    }    //如果不是对象,不是函数,target设为空对象,以便和后面对象能够合并    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {        target = {};    }    //如果只有一个参数,那么就认为这是扩展插件,所以target就是this对象。前面也讲了什么情况下,this是jQuery,什么情况下this是jQuery.fn    if ( length === i ) {        target = this;        --i;     //这个时候就从第一个参数开始处理    }    for ( ; i < length; i++ ) {        //如果是对象扩展并且是浅拷贝,那么i从1开始        //如果是对象扩展并且是深拷贝,那么i从2开始        //如果是插件扩展,那么i从0开始        //前面已经处理好了        if ( (options = arguments[ i ]) != null ) {            for ( name in options ) {                src = target[ name ];                copy = options[ name ];                //注意这部分,下面会讲到                if ( target === copy ) {                    continue;                }                //如果是深拷贝,那么对象内层的每一级属性都要拷贝,所以是一个递归的过程                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 );                } else if ( copy !== undefined ) {                    //如果不是深拷贝,就直接将name属性加到target对象里面了                    target[ name ] = copy;                }            }        }    }    //最后返回扩展后的target对象;或者返回jQuery或者返回jQuery.fn    return target;

下来缕一缕上面提到的

if ( target === copy ) {    continue;}

比如这么一种情况:

var obj = {    age : 20}var obj1 = {    age: obj}var res = $.extend(obj, obj1);

这种情况下,在extend函数内部,target为 obj = { age : 20 }, copy = options[‘age’] = obj = { age : 20 }
所以判断target和copy相等,就会continue,跳过该次循环,那为什么要这样做呢?
我们先把if ( target === copy ) { continue; }注释掉,看结果是什么?
这里写图片描述
可以看到,返回的是一个含有一个name属性的对象,而name属性又指向该对象,这样无限循环下去了。为什么会出现这种情况呢?
因为如果去掉continue的话,会继续后面的执行,即

target[ name ] = copy;

在这句执行之前,target指向一个Object对象,copy也是指向该对象,那么对target的修改也会影响copy对象,所以target.name修改为copy,相当于copy.name也修改为copy,这样就形成了无限循环的对象,这样实际上没有什么意义,所以jQuery避免了这种情况。可见,jQuery各方面的考虑是非常全面。希望能学到更多,理解更深刻!