解析jQuery源码及其链式操作

前言

阅读本文前可能需要熟悉JavaScript原型和原型链知识,如果你还没有掌握的话,请自行传送。

jQuery概览

本概览使用的就jQuery版本是2.0.3版本,一个比较老的版本,但是基本功能与新版类似。此外,2.X相较于1.X版本,去除了对IE6/7/8的支持,这也使得其中减少了很多有关于兼容性的代码,便于我们去理解。

jQuery首先是一个 IIFE( 立即调用函数表达式),英文全称 Immediately Invoked Function Expression,这是一个被称为 自执行匿名函数 的设计模式,主要包含两部分。第一部分是包围在 圆括号运算符 () 里的一个匿名函数,这个匿名函数拥有独立的词法作用域。这不仅避免了外界访问此 IIFE 中的变量,而且又不会污染全局作用域。

(function( window, undefined ) {

   (21 , 94)     定义了一些变量和函数 jQuery = function(){};
   (96 , 283)JQ对象,添加一些方法和属性
   (285 , 347)   extend : JQ的继承方法
   (349 , 817) 	 jQuery.extend() : 扩展一些工具方法
   (877 , 2856)  Sizzle : 复杂选择器的实现
   (2880 , 3042) Callbacks : 回调对象 : 对函数的统一管理
   (3043 , 3183) Deferred : 延迟对象 : 对异步的统一管理
   (3184 , 3295) support : 功能检测
   (3308 , 3652) data() : 数据缓存
   (3653 , 3797) queue() : 队列方法 : 执行顺序的管理
   (3803 , 4299) attr() prop() val() addClass(): 对元素属性的操作
   (4300 , 5128) on() trigger() : 事件操作的相关方法
   (5140 , 6057) DOM操作 : 添加 删除 获取 包装 DOM筛选
   (6058 , 6620) css() : 样式的操作
   (6621 , 7854) 提交的数据和ajax() : ajax() load() getJSON()
   (7855 , 8584) animate() : 运动的方法
   (8585 , 8792) offset() : 位置和尺寸的方法
   (8804 , 8821) JQ支持模块化的模式
   (8826)  window.jQuery = window.$ = jQuery;
   
})(window);

以上就是整个jQuery概览,接下来我们看下jQuery的核心部分。

jQuery核心代码提炼

以下代码我个人觉得是jQuery最核心的部分,也是最难理解的部分。

(function( window, undefined ) {
	// 这里为什么参undefined呢?这是为了防止undefined在外部被修改

	var jQuery = function(selector) {
     	return new jQuery.fn.init(selector);
    }
    
    // 下面语句是重写了函数jQuery的原型prototype,故需要将constructor
    // 指向函数jQuery。
    jQuery.fn = jQuery.prototype = {
        wQuery: '2.0.3',
        constructor: jQuery,
        init: function(selector) {
            return this;
        },
        // 其它jQuery原型上的方法
    }

    jQuery.fn.init.prototype = jQuery.fn;

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

	window.jQuery = window.$ = jQuery;
	
})(window);

下面我们就来解析下jQuery的各部分实现。

jQuery无 new 构造

当我们使用jQuery的时候,是如何实例化jQuery构造函数的呢?

// 无 new 构造
$('#test').text('Test');
 
// 当然也可以使用 new
// 也许你会疑问 $===jQuery函数 不是本身就返回一个实例化后的对象
// (return new jQuery.fn.init())吗?为什么还可以 new $() 呢?
// 这个问题查下 new 操作符的实现就是到原因了,这里就不详述。
var test = new $('#test');
test.text('Test');

大部分人使用jQuery的时候都是使用第一种无new的实例化方式,直接$('')实例化jQuery,这也是jQuery十分便捷的一个地方。当我们使用第一种无new实例化的时候,其本质就是相当于new jQuery(),那么在jQuery内部是如何实现的呢?看看:

(function(window, undefined) {
    var
    // ...
    jQuery = function(selector, context) {
        // The jQuery object is actually just the init constructor 'enhanced'
        // 看这里,实例化方法 jQuery() 实际上是调用了其拓展的原型方法 jQuery.fn.init
        return new jQuery.fn.init(selector, context, rootjQuery);
    },
 
    // jQuery.prototype 即是 jQuery 的原型,挂载在上面的方法,即可让所有生成的 jQuery 对象使用
    jQuery.fn = jQuery.prototype = {
        // 实例化化方法,这个方法可以称作 jQuery 对象构造器
        init: function(selector, context, rootjQuery) {
            // ...
        }
    }
    // 这一句很关键,也很绕
    // jQuery 没有使用 new 运算符将 jQuery 实例化,而是直接调用其函数
    // 要实现这样,那么 jQuery 就要看成一个类,且返回一个正确的实例
    // 且实例还要能正确访问 jQuery 类原型上的属性与方法
    // jQuery 的方式是通过原型传递解决问题,把 jQuery 的原型传递给jQuery.prototype.init.prototype
    // 所以通过这个方法生成的实例 this 所指向的仍然是 jQuery.fn,所以能正确访问 jQuery 类原型上的属性与方法
    jQuery.fn.init.prototype = jQuery.fn;
 
})(window);

大部分人初看jQuery.fn.init.prototype = jQuery.fn这一句都会被卡主,很是不解。但是这句真的算是jQuery的绝妙之处。理解这几句很重要,分点解析一下:

  • 首先要明确,使用$('xxx')这种实例化方式,其内部调用的是return new jQuery.fn.init(selector, context, rootjQuery)这一句话,也就是构造实例是交给了jQuery.fn.init()方法去完成。
  • jQuery.fn.initprototype属性设置为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 对象。

jQuery的链式操作

另一个让大家喜爱使用jQuery的原因是它的链式调用,这一点的实现其实很简单,只需要在要实现链式调用的方法的返回结果里,返回this,就能够实现链式调用了。当然这样实现的链式操作是为单纯的实现函数的调用而不需要返回其他的值的情况下,如果你还要返回其他值的话就不能单纯的返回this这样来实现链式操作了。

我们看下这段代码:

$('div#box').find('span').eq(2);

这段代码的意思是找到div#box中的所有span的第三个,$('div#box').find('span')既要返回所有的span又要可以实现后面的eq(2)的链式操作。

我们来看看jQuery怎么实现的吧!

(function( window, undefined ) {
	
	// ...
    jQuery.fn = jQuery.prototype = {
        wQuery: '2.0.3',
        constructor: jQuery,
        init: function(selector) {
            return this;
        },
        getName: function() {
        	// this.constructor()实例化了一个新的jQuery对象,继承了jQuery
        	// 原型上所有方法和属性以实现链式操作,在新的示例对象上添加属性和
        	// 方法时不会改变当前jQuery原型上的属性和方法,这也是不直接用this
        	// 而用this.constructor()来实例化一个新的对象返回的原因。
        	let all = this.constructor();
        	all.jack = 'jack';
        	all.lucy = 'lucy';
        	return all;
        },
        sayName: function(who) {
        	console.log(this[who]);
        }
    }

    jQuery.fn.init.prototype = jQuery.fn;
	
	// ...
	
	window.jQuery = window.$ = jQuery;
	
})(window);

jQuery().getName().sayName('lucy'); // 'lucy'

jQuery.fn.extend 与 jQuery.extend

extend方法在jQuery中是一个很重要的方法,jQuey内部用它来扩展静态方法或实例方法,而且我们开发jQuery插件开发的时候也会用到它。但是在内部,是存在jQuery.fn.extendjQuery.extend两个extend方法的,而区分这两个extend方法是理解jQuery的很关键的一部分。先看结论:

  1. jQuery.extend(object)为扩展jQuery类本身,为类添加新的静态方法;

  2. jQuery.fn.extend(object)jQuery 对象添加实例方法,也就是通过这个 extend 添加的新方法,实例化的jQuery 对象都能使用,因为它是挂载在 jQuery.fn 上的方法(上文有提到,jQuery.fn = jQuery.prototype )。

它们的官方解释是:

  1. jQuery.extend(): 把两个或者更多的对象合并到第一个当中,

  2. jQuery.fn.extend():把对象挂载到 jQueryprototype 属性,来扩展一个新的 jQuery 实例方法。

也就是说,使用 jQuery.extend() 拓展的静态方法,我们可以直接使用 $.xxx 进行调用(xxx是拓展的方法名),

而使用 jQuery.fn.extend()拓展的实例方法,需要使用 $().xxx 调用。

需要注意的是这一句 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 ,也就是这里增加的是原型方法,也就是对象方法。

文章最后就到这里了
最后感谢【深入浅出jQuery】源码浅析–整体架构提供技术支持。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值