【jQuery源码浅析】(三)--jQuery插件拓展--$.extend

来源:互联网 发布:mac os tftp 软件下载 编辑:程序博客网 时间:2024/05/22 08:18

前言

本文主要讲的不是如何使用jQuery的插件方法,而是jQuery.extendjQuery.fn.extend的实现,这两种方法实现了拓展jQuery方法的途径,前者是增加jQuery的静态方法,也就是$.do()这样;后者是增加jQuery的实例方法,也就是$(selector).do()这样。这两者的用法分别为$.extend和$.fn.extend。如果想了解jQuery的插件开发可以看这里如何制作一个最简单的jQuery插件。

这两个方法最神奇的地方是居然用同一个函数来实现,其实从源码中可知this在里面起到了巨大的作用。要了解这两个方法的实现方式,就要先从方法的实现效果去反推和理解。

核心源码

    // jQuery插件拓展195     jQuery.extend = jQuery.fn.extend = function() {            var options, name, src, copy, copyIsArray, clone,                target = arguments[ 0 ] || {},                i = 1,                length = arguments.length,                deep = false;            // Handle a deep copy situation            if ( typeof target === "boolean" ) {                deep = target;                // Skip the boolean and the target                target = arguments[ i ] || {};                i++;            }            // Handle case when target is a string or something (possible in deep copy)            if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {                target = {};            }            // Extend jQuery itself if only one argument is passed            if ( i === length ) {                target = this;                i--;            }            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;                        }                    }                }            }            // Return the modified object            return target;262     };

思路分析

  1. 简单目标:jQueryjQuery.fn方法的拓展,也就是$.do()$().do()
  2. 中等目标:把所有的对象合并到第一个对象target上面(如果只有一个对象就合并到自己身上,等于没有合并),并将target返回到方法外。
  3. 高级目标:深拷贝,也就是说把每个对象中的所有对象的属性都拷贝到第一个对象身上,从object原型继承的属性会被拷贝,值为undefined的属性不会被拷贝。$.extend()的深拷贝和浅拷贝详细讲解

源码分析

1)明确target是哪个对象

        // 是用户传进来的第一个参数,如果不填,则新建一个对象(不能为空)        var target = arguments[0] || {};        // 如果第一个参数类型为boolean,则target转为第二个参数,如果不填,则新建一个对象(不能为空)        if (typeof target === "boolean") {            target = arguments[1] || {};        }        // 如果第一个参数不是对象或方法(也就有可能为字符串或其他),则将target转换成空对象        if (typeof target !== "object" && !jQuery.isFunction(target)) {            target = {};        }        // 如果只有一个参数,表示是 jQuery 静态方法        if (length === i) { // length = arguments.length            target = this;  // this指jQuery本身                            // 所以$.extend和$.fn.extend可以写在一起,就是由这个this决定的        }        // 最后的target一定是对象,要么是用户传进来的对象,要么是空对象{}

2)复制所有对象的属性到第一个对象target的属性中,如果有相同属性名,将会覆盖target的该属性名的值

        // 如果target=this,则循环 1 次,返回原本的target        // 如果target!=this,则循环 n-1 次,n为obj的数量(包括target本身)        for (; i < length; i++) {            // 取出每个对象的key,如果用es6,可以直接用for-of            options = arguments[i]            for (name in options) {                if () {}                else if (copy !== undefined) {                    target[name] = copy;                }            }        }

3)深克隆,将所有对象的属性复制到target中,如果属性不是对象,则完全覆盖;如果是纯对象或者是数组,则不完全覆盖,只覆盖对象或数组里面的相同属性。

        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 );        // 最终会走到这里,如果是undefined,则直接退出        } else if ( copy !== undefined ) {            target[ name ] = copy;        }

最后总结

看完源码,感觉jQuery的extend方法太强大了,一个方法里面主要实现了两个目标,一是帮助jQuery和jQuery的实例增加方法,二是克隆对象。前者只能有一个参数,而后者可能有无限个参数。前者靠target = this来实现,后者靠 递归 来实现。总之,用85行的代码(含注释和空行)就能同时实现两个功能,既强大又简洁,完全符合了jQuery的 write less, do more. 的追求。

相关资料

  • jQuery API 3.2.1 速查表
  • 【深入浅出jQuery】源码浅析–整体架构
  • jQuery学习官网
  • jQuery3.1.1.js源码下载
原创粉丝点击