jQuery的源码解读(一)

jQuery的整体架构

对架构有个清晰的认识,才不会一头雾水,漫无目的解读源代码。
mark

jQuery的无new构建

1
2
$().find().css()
$().hide().html('....').hide()

这里涉及到两个问题(无new构建和链式调用)

  1. $(“XXX”)到底是怎样实现的,内部发生了什么使我们能够如此轻易获取到对象。
    * $是什么鬼?
    window 是对象,它有两个属性,分别为 jQuery 和 $,其值是一函数,此函数的名字是 jQuery。在函数
    的定义实体中,其实是通过 jQuery.fn.init 函数来构造对象的,init 函数才是真正的构造函数。也就是说, 我们通过 ${ … ) 得到的其实就是 jQuery.fn.init 的实例。
    1
    2
    3
    4
    var jQuery = window.jQuery = window.$ = function( selector, context ) {
    // The jQuery object is actually just the init constructor 'enhanced'
    return new jQuery.fn.init( selector, context );
    };

*jQuery.fn?fn解释下,其实这个fn没有什么特殊意思,只是jQuery.prototype的引用

1
理解这几句很重要jQuery.fn.init.prototype = jQuery.fn;

  • 首先要明确,使用 $(‘xxx’) 这种实例化方式,其内部调用的是 return new jQuery.fn.init(selector, context, rootjQuery) 这一句话,也就是构造实例是交给了 jQuery.fn.init() 方法去完成。
  • 将 jQuery.fn.init 的 prototype 属性设置为 jQuery.fn,那么使用 new jQuery.fn.init() 生成的对象的原型对象就是 jQuery.fn ,所以挂载到 jQuery.fn 上面的函数就相当于挂载到 jQuery.fn.init() 生成的 jQuery 对象上,所有使用 new jQuery.fn.init() 生成的对象也能够访问到 jQuery.fn 上的所有原型方法。
  • 也就是实例化方法存在这么一个关系链
    jQuery.fn.init.prototype = jQuery.fn = jQuery.prototype ;
    new jQuery.fn.init() 相当于 new jQuery() ;
    jQuery() 返回的是 new jQuery.fn.init(),而 var obj = new jQuery(),所以这 2 者是相当的,所以我们可以无 new 实例化 jQuery 对象。

mark

链式调用

为什么DOM链式调用的处理:

  • 节约JS代码.
  • 所返回的都是同一个对象,可以提高代码的效率
  • 实现链式的基本条件:就是实例this的存在,并且是同一个
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    aQuery.prototype = {
    init: function() {
    return this;
    },
    name: function() {
    return this
    }
    }
    --------------
    aQuery().init().name()
    分解
    a = aQuery();
    a.init()
    a.name()

插件接口

  1. jQuery的主体框架就是这样,但是根据一般设计者的习惯,如果要为jQuery或jQuery prototype添加属性方法,同样如要提供给开发者对方法的扩展,从封装的角度讲是不是应该提供一个接口才对,字面就能看懂是对函数扩展,而不是看上去直接修改prototype.友好的用户接口,
    jQuery支持自己扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增加方法
    从jQuery的源码中可以看到,jQuery.extend和jQuery.fn.extend其实是同指向同一方法的不同引用
  2. jQuery.extend和jQuery.fn.extend区别:
  • jQuery.extend(object) 为扩展 jQuery 类本身,为类添加新的静态方法;
  • jQuery.fn.extend(object) 给 jQuery 对象添加实例方法,也就是通过这个 extend 添加的新方法,实例化的 jQuery 对象都能使用,因为它是挂载在 jQuery.fn 上的方法(上文有提到,jQuery.fn = jQuery.prototype )。
    它们的官方解释是:
    1)jQuery.extend(): 把两个或者更多的对象合并到第一个当中,
    2)jQuery.fn.extend():把对象挂载到 jQuery 的 prototype 属性,来扩展一个新的 jQuery 实例方法。
  • 也就是说,使用 jQuery.extend() 拓展的静态方法,我们可以直接使用 $.xxx 进行调用(xxx是拓展的方法名),
  • 而使用 jQuery.fn.extend() 拓展的实例方法,需要使用 $().xxx 调用。
1
2
3
jQuery.extend = jQuery.fn.extend = function() {}
jQuery.extend 对jQuery本身的属性和方法进行了扩展
jQuery.fn.extend 对jQuery.fn的属性和方法进行了扩展

通过extend()函数可以方便快速的扩展功能,不会破坏jQuery的原型结构
jQuery.extend = jQuery.fn.extend = function(){…}; 这个是连等,也就是2个指向同一个函数,怎么会实现不同的功能呢?这就是this 力量了!
针对fn与jQuery其实是2个不同的对象,在之前有讲述:

  • jQuery.extend 调用的时候,this是指向jQuery对象的(jQuery是函数,也是对象!),所以这里扩展在jQuery上。
  • 而jQuery.fn.extend 调用的时候,this指向fn对象,jQuery.fn 和jQuery.prototype指向同一对象,扩展fn就是扩展jQuery.prototype原型对象。
  • 这里增加的是原型方法,也就是对象方法了。所以jQuery的api中提供了以上2中扩展函数。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    // 扩展合并函数
    // 合并两个或更多对象的属性到第一个对象中,jQuery 后续的大部分功能都通过该函数扩展
    // 虽然实现方式一样,但是要注意区分用法的不一样,那么为什么两个方法指向同一个函数实现,但是却实现不同的功能呢,
    // 阅读源码就能发现这归功于 this 的强大力量
    // 如果传入两个或多个对象,所有对象的属性会被添加到第一个对象 target
    // 如果只传入一个对象,则将对象的属性添加到 jQuery 对象中,也就是添加静态方法
    // 用这种方式,我们可以为 jQuery 命名空间增加新的方法,可以用于编写 jQuery 插件
    // 如果不想改变传入的对象,可以传入一个空对象:$.extend({}, object1, object2);
    // 默认合并操作是不迭代的,即便 target 的某个属性是对象或属性,也会被完全覆盖而不是合并
    // 如果第一个参数是 true,则是深拷贝
    // 从 object 原型继承的属性会被拷贝,值为 undefined 的属性不会被拷贝
    // 因为性能原因,JavaScript 自带类型的属性不会合并
    jQuery.extend = jQuery.fn.extend = function() {
    var src, copyIsArray, copy, name, options, clone,
    target = arguments[0] || {},
    i = 1,
    length = arguments.length,
    deep = false;
    // Handle a deep copy situation
    // target 是传入的第一个参数
    // 如果第一个参数是布尔类型,则表示是否要深递归,
    if (typeof target === "boolean") {
    deep = target;
    target = arguments[1] || {};
    // skip the boolean and the target
    // 如果传了类型为 boolean 的第一个参数,i 则从 2 开始
    i = 2;
    }
    // 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
    // 如果参数的长度为 1 ,表示是 jQuery 静态方法
    if (length === i) {
    target = this;
    --i;
    }
    // 可以传入多个复制源
    // i 是从 1或2 开始的
    for (; i < length; i++) {
    // Only deal with non-null/undefined values
    // 将每个源的属性全部复制到 target 上
    if ((options = arguments[i]) != null) {
    // Extend the base object
    for (name in options) {
    // src 是源(即本身)的值
    // copy 是即将要复制过去的值
    src = target[name];
    copy = options[name];
    // Prevent never-ending loop
    // 防止有环,例如 extend(true, target, {'target':target});
    if (target === copy) {
    continue;
    }
    // Recurse if we're merging plain objects or arrays
    // 这里是递归调用,最终都会到下面的 else if 分支
    // jQuery.isPlainObject 用于测试是否为纯粹的对象
    // 纯粹的对象指的是 通过 "{}" 或者 "new Object" 创建的
    // 如果是深复制
    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
    // 返回新的 target
    // 如果 i < length ,是直接返回没经过处理的 target,也就是 arguments[0]
    // 也就是如果不传需要覆盖的源,调用 $.extend 其实是增加 jQuery 的静态方法
    return target;
    };

需要注意的是这一句 jQuery.extend = jQuery.fn.extend = function() {} ,也就是 jQuery.extend 的实现和 jQuery.fn.extend 的实现共用了同一个方法,但是为什么能够实现不同的功能了,这就要归功于 Javascript 强大(怪异?)的 this 了。
1)在 jQuery.extend() 中,this 的指向是 jQuery 对象(或者说是 jQuery 类),所以这里扩展在 jQuery 上;
2)在 jQuery.fn.extend() 中,this 的指向是 fn 对象,前面有提到 jQuery.fn = jQuery.prototype ,也就是这里增加的是原型方法,也就是对象方法。

参考链接:http://www.cnblogs.com/aaronjs/p/3278578.html

文章目录
  1. 1. jQuery的整体架构
  2. 2. jQuery的无new构建
  3. 3. 链式调用
  4. 4. 插件接口
|