jQuery源码分析(五): 拓展接口extend

前端语法/样式/布局 2016-11-01

起步

jquery通过jQuery prototype的方法添加属性方法。使得开发者开发第三方插件让jq越发壮大。

extend()接口也是一个添加属性方法的入口,这看起来很“多余”。但它的存在是必要的,通过原型的方式注入到jq中的影响范围是全局的,注入一个$.fn.xxx = function(){};影响到所有的jq实例,何况大量开发者的第三方插件之间存在冲突是极有可能的。所以能对单一实例注入$().extend()来对对象增加方法,是不去直接修改prototype。

另外,extend()同样可以注入到全局的jq实例:jQuery.fn.extend({xxx:function()});作用和直接修改原型一样,但就像面向对象思想里面的,如果一个属性设置私有,但又提供get和set方法,那为何不把该属性设为public呢。这是一个更安全更规范的写法,从封装的角度讲extend是一个友好的对外拓展接口。

用法

函数原型:

jQuery.extend( [deep], target, object1 [, objectN ] )

函数的作用是将两个或更多对象的内容合并到第一个对象。很多利用这个函数:var obj = $.extend({}, object1, object2);进行对象属性方法合并,第三方插件合并默认设置和用户设置基本都这么做。

实现

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

    if ( typeof target === "boolean" ) {
        deep = target;

        target = arguments[ i ] || {};
        i++;
    }

    if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
        target = {};
    }

    if ( i === length ) {
        target = this;
        i--;
    }

    for ( ; i < length; i++ ) {
        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 : {};
                    }

                    target[ name ] = jQuery.extend( deep, clone, copy );

                } else if ( copy !== undefined ) {
                    target[ name ] = copy;
                }
            }
        }
    }
    return target;
};

从源码中可以看到,jQuery.extend没有明确的参数,都是通过argments判断,函数里会对第一个参数进行判断,如果是true,合并成为递归(又叫做深拷贝)。target是个对象,将接收新的属性和方法。通过for循环遍历把数据附加到这个target上了,合并的顺序是先与object1和并再与object2合并,也就是说如果对象在属性上冲突,保留的是后面合并的。jQuery.extendjQuery.fn.extend其实是同指向同一方法,区分他们是实例还是jq本身的点睛之笔是:

target = this;

这句是这一段精妙的地方,体现在:

  • jQuery.extend调用的时候this指向的是jQuery构造器。所以合并到了jQuery上

  • jQuery.fn.extend调用的时候this指向的是jQuery构造器的实例对象了。所以合并到了jQ对象上。

总结

通过extend()函数可以方便快速的扩展功能,不会破坏jQuery的原型结构,jQuery.extend = jQuery.fn.extend = function(){...}; 这个是连等,也就是2个指向同一个函数,怎么会实现不同的功能呢?这就是js神奇的this了!


本文由 hongweipeng 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

赏个馒头吧