JQ 源码解析(二)无 new 构建 JQ 对象
接着上一篇文章,我们知道 JQ 文件创建了一个匿名函数,里面的一个函数包裹了所有 JQ 功能函数。现在我们进一步分析函数内容。
我们都知道,实例一个 JQ 对象是没有用到 new 的
var test = $('.test') //这里 $('.test') 相当于 new jQuery('.test')
如何实现的呢,截取这个功能的主体代码:
jQuery = function( selector, context ) {
return new jQuery.fn.init( selector, context );
},
jQuery.fn = jQuery.prototype = {
constructor: jQuery,
//构造方法
......
......
}
var init = jQuery.fn.init = function( selector, context, root ) {
//处理传入参数
......
......
}
init.prototype = jQuery.fn;
初看很复杂,看不明白他到到底想干什么,也看了很多说明,最终还是觉得从实现这个功能的目的反推代码,配上代码的解释更易懂。
首先,我们得构建一个 JQ 对象,
function jQuery( selector, context ) {......}
$().addClass(),因为 jQuery 是一个类,并且调用后返回的也是这个类的实例(因为返回的实例能调用自身的方法),所以这个构造函数为
function jQuery( selector, context ) {
return new jQuery( selector, context )
}
这样写会出现死循环一直调用 jQuery ,我们可以在 jQuery 原型上构建一个方法,这个方法返回 this ,指向原型本身:
function jQuery( selector, context ) {
return jQuery.prototype.init( selector, context )
}
jQuery.prototype = {
constructor: jQuery,
init:function(){
return this
},
//内部属性方法
name:'Z',
sayName:function(){ console.log(this.name)},
......
}
这里问题是解决了,但是别人比我们厉害的就在后面,深挖细节在不同场景下都能最优使用,假设我们需要继续在这个 init 函数中构造函数,并适用不同场景:
init:function(selector){
if(selector == 1)
this.name = 'P'
return this
},
jQuery(0).sayName() //输出Z
jQuery(1).sayName() //输出P
jQuery(0).sayName() //输出P
因为实例 jQuery 类时 init 构造的属性是直接作用在 jQuery 类的原型上,所以会出现上面的作用域相互影响。
解决办法就是每次类都返回一个新对象(链式调用就是如此)。
function jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context )
}
但是执行后会发现
jQuery(0).sayName() //undefined
jQuery(1).sayName() //输出P
因为新实例的 init 与 jQuery 类的 this 分离了
所以还要加上
jQuery.prototype.init.prototype = jQuery.prototype
jQuery.prototype 上的属性方法都会被 init 继承
全文,基本就是 JQ 源码的意思了:
function jQuery( selector, context ) {
return new jQuery.prototype.init( selector, context )
}
jQuery.prototype = {
constructor: jQuery,
init:function(){
return this
},
//内部属性方法
name:'Z',
sayName:function(){ console.log(this.name)},
......
}
jQuery.prototype.init.prototype = jQuery.prototype