拜读一个开源框架,最想学到的就是设计的思想和实现的技巧。
废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过,
不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍
我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧!
github上最新是jquery-master,加入了AMD规范了,我就以官方最新2.0.3为准
整体架构
jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作、
例如:
复制代码 代码如下:
$().find().css()
$().hide().html('....').hide().
从上面的写法上至少可以发现2个问题
1. jQuery对象的构建方式
2 .jQuery方法的调用方式
分析一:jQuery的无new构建
JavaScript是函数式语言,函数可以实现类,类就是面向对象编程中最基本的概念
复制代码 代码如下:
var aQuery = function(selector, context) {
//构造函数
}
aQuery.prototype = {
//原型
name:function(){},
age:function(){}
}
var a = new aQuery();
a.name();
这是常规的使用方法,显而易见jQuery不是这样玩的
jQuery没有使用new运行符将jQuery显示的实例化,还是直接调用其函数
按照jQuery的书写方式
复制代码 代码如下:
$().ready()
$().noConflict()
要实现这样,那么jQuery就要看成一个类,那么$()应该是返回类的实例才对
所以把代码改一下:
复制代码 代码如下:
var aQuery = function(selector, context) {
return new aQuery();
}
aQuery.prototype = {
name:function(){},
age:function(){}
}
通过new aQuery(),虽然返回的是一个实例,但是也能看出很明显的问题,死循环了!
那么如何返回一个正确的实例?
在javascript中实例this只跟原型有关系
那么可以把jQuery类当作一个工厂方法来创建实例,把这个方法放到jQuery.prototye原型中
复制代码 代码如下:
var aQuery = function(selector, context) {
return aQuery.prototype.init();
}
aQuery.prototype = {
init:function(){
return this;
}
name:function(){},
age:function(){}
}
当执行aQuery() 返回的实例:
很明显aQuery()返回的是aQuery类的实例,那么在init中的this其实也是指向的aQuery类的实例
问题来了init的this指向的是aQuery类,如果把init函数也当作一个构造器,那么内部的this要如何处理?
复制代码 代码如下:
var aQuery = function(selector, context) {
return aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
return this;
},
name: function() {},
age: 20
}
aQuery().age //18
这样的情况下就出错了,因为this只是指向aQuery类的,所以需要设计出独立的作用域才行
jQuery框架分隔作用域的处理
复制代码 代码如下:
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
},
很明显通过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆
那么既然都不是同一个对象那么肯定又出现一个新的问题
例如:
复制代码 代码如下:
var aQuery = function(selector, context) {
return new aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
return this;
},
name: function() {},
age: 20
}
//Uncaught TypeError: Object [object Object] has no method 'name'
console.log(aQuery().name())
抛出错误,无法找到这个方法,所以很明显new的init跟jquery类的this分离了
怎么访问jQuery类原型上的属性与方法?
做到既能隔离作用域还能使用jQuery原型对象的作用域呢,还能在返回实例中访问jQuery的原型对象?
实现的关键点
复制代码 代码如下:
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
通过原型传递解决问题,把jQuery的原型传递给jQuery.prototype.init.prototype
换句话说jQuery的原型对象覆盖了init构造器的原型对象
因为是引用传递所以不需要担心这个循环引用的性能问题
复制代码 代码如下: