整体架构
$().find().css().hide()
从jQuery的表达式可以看出两点:
- jQuery的构建方式
- jQuery的调用方式
下面从这两方面来窥探jQuery的整体架构:
分析一:无new构建
这一点是推测jQ构建方式的重点。既然没有使用new,jQ必定在函数中返回了一个实例,也就是通过工厂模式来创建对象。
var aQuery = function(select){
var o = new Object();
o.select = select;
o.myFunc = function(){};
}
然而这种方法显然非常笨拙,每次新建一个对象实例都必须声明其所有共用方法。能不能把构造函数与原型模式融入到这里面来呢?
var aQuery = function(selector){
return aQuery.prototype.init(selector);
}; aQuery.prototype = { // aQuery的原型
init: function(selector){ // init作为新的构造函数
this.selector = selector;
return this;
},
myFunc: function(){}
}; console.log(aQuery('s1'));
代码运行之后,在控制台显示的是下面这样的结果:
{
init: [Function],
myFunc: [Function],
selector: 's1'
}
this指向了aQuery的原型对象,并且共用了所有方法。然而这里面的陷阱在于,由于我们并没有new一个新对象,所以构造函数内部的this指向了aQuery的原型,共享了本应该私有的属性。
var a1 = aQuery('s1');
var a2 = aQuery('s2');
console.log(a1.selector); // s2
那么是不是只要返回一个新实例就可以了呢?
var aQuery = function(selector, context){
return (new aQuery.prototype.init(selector, context));
}; aQuery.prototype = {...}; var a1 = aQuery('s1');
var a2 = aQuery('s2');
console.log(a1.selector); // s1
console.log(a1); //{ selector: 's1' } myFunc不见了!
由上可见,new关键字新建了一个空对象,使构造函数内部的this指向该对象,解决了属性公有的问题(这部分知识点请参考Javascript对象部分)。然而同时,也丢失了aQuery.prototype。那么jQuery是怎么解决这个问题的呢?这就是jQuery构建方法的关键所在:原型传递。
直接上代码:
var aQuery = function(selector){
return (new aQuery.prototype.init(selector));
}; aQuery.prototype = { // aQuery的原型
init: function(selector){ // init作为新的构造函数
this.selector = selector;
return this;
},
myFunc: function(){
return "aQuery.func";
}
};
aQuery.prototype.init.prototype = aQuery.prototype; var a1 = aQuery('s1');
var a2 = aQuery('s2');
console.log(a1.selector); // s1
console.log(a1.myFunc()); // aQuery.func
其实本质上讲,init构建出来的这个对象,就是jQuery对象。
这样,我们总算理清了jQuery无new构建的原理。使用工厂模式返回一个实例,并将该实例构造函数的原型指向aQuery原型本身,实现了私有属性和共有方法。
各中关系如图所示,其中fn指代了jQuery的原型对象。
分析二:链式调用
当使用了new关键字后,jQuery的所有方法中的this都指向了新建的实例,所以我们只要在方法最后返回this,就可以实现链式调用。然而:
摘自Aaron的JQ源码分析:
最糟糕的是所有对象的方法返回的都是对象本身,也就是说没有返回值,这不一定在任何环境下都适合。
Javascript是无阻塞语言,所以他不是没阻塞,而是不能阻塞,所以他需要通过事件来驱动,异步来完成一些本需要阻塞进程的操作,这样处理只是同步链式,异步链式jquery从1.5开始就引入了Promise,jQuery.Deferred后期在讨论。