query这么多年了分析都写烂了,老早之前就拜读过, 不过这几年都是作移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合本身的实际经验一块儿拜读吧! github上最新是jquery-master,加入了AMD规范了,我就以官方最新2.0.3为准javascript
jQuery框架的核心就是从HTML文档中匹配元素并对其执行操做、 例如:css
$().find().css()
$().hide().html('....').hide().
复制代码
从上面的写法上至少能够发现2个问题html
JavaScript是函数式语言,函数能够实现类,类就是面向对象编程中最基本的概念java
var aQuery = function(selector, context) {
//构造函数
}
aQuery.prototype = {
//原型
name:function(){},
age:function(){}
}
var a = new aQuery();
a.name();
复制代码
这是常规的使用方法,显而易见jQuery不是这样玩的 jQuery没有使用new运行符将jQuery显示的实例化,仍是直接调用其函数 按照jQuery的抒写方式jquery
$().ready()
$().noConflict()
复制代码
要实现这样,那么jQuery就要当作一个类,那么$()应该是返回类的实例才对 因此把代码改一下:git
var aQuery = function(selector, context) {
returnnew aQuery();
}
aQuery.prototype = {
name:function(){},
age:function(){}
}
复制代码
经过new aQuery(),虽然返回的是一个实例,可是也能看出很明显的问题,死循环了!github
在javascript中实例this只跟原型有关系 那么能够把jQuery类看成一个工厂方法来建立实例,把这个方法放到jQuery.prototye原型中编程
var aQuery = function(selector, context) {
return aQuery.prototype.init();
}
aQuery.prototype = {
init:function(){
returnthis;
}
name:function(){},
age:function(){}
}
复制代码
当执行aQuery() 返回的实例: api
var aQuery = function(selector, context) {
return aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
returnthis;
},
name: function() {},
age: 20
}
aQuery().age //18
复制代码
这样的状况下就出错了,由于this只是指向aQuery类的,因此须要设计出独立的做用域才行数组
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'returnnew jQuery.fn.init( selector, context, rootjQuery );
},
复制代码
很明显经过实例init函数,每次都构建新的init实例对象,来分隔this,避免交互混淆 那么既然都不是同一个对象那么确定又出现一个新的问题 例如:
var aQuery = function(selector, context) {
returnnew aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
this.age = 18
returnthis;
},
name: function() {},
age: 20
}
//Uncaught TypeError: Object [object Object] has no method 'name'
console.log(aQuery().name())
复制代码
抛出错误,没法找到这个方法,因此很明显new的init跟jquery类的this分离了
作到既能隔离做用域还能使用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构造器的原型对象 由于是引用传递因此不须要担忧这个循环引用的性能问题
var aQuery = function(selector, context) {
returnnew aQuery.prototype.init();
}
aQuery.prototype = {
init: function() {
returnthis;
},
name: function() {
returnthis.age
},
age: 20
}
aQuery.prototype.init.prototype = aQuery.prototype;
console.log(aQuery().name()) //20
复制代码
百度借网友的一张图,方便直接理解: fn解释下,其实这个fn没有什么特殊意思,只是jQuery.prototype的引用
DOM链式调用的处理: 1.节约JS代码. 2.所返回的都是同一个对象,能够提升代码的效率
经过简单扩展原型方法并经过return this的形式来实现跨浏览器的链式调用。 利用JS下的简单工厂模式,来将全部对于同一个DOM对象的操做指定同一个实例。 这个原理就超简单了
aQuery().init().name()
分解
a = aQuery();
a.init()
a.name()
复制代码
把代码分解一下,很明显实现链式的基本条件就是实例this的存在,而且是同一个
aQuery.prototype = {
init: function() {
returnthis;
},
name: function() {
returnthis
}
}
复制代码
因此咱们在须要链式的方法访问this就能够了,由于返回当前实例的this,从而又能够访问本身的原型了
aQuery.init().name()
复制代码
优势:节省代码量,提升代码的效率,代码看起来更优雅 最糟糕的是全部对象的方法返回的都是对象自己,也就是说没有返回值,这不必定在任何环境下都适合。
Javascript是无阻塞语言,因此他不是没阻塞,而是不能阻塞,因此他须要经过事件来驱动,异步来完成一些本须要阻塞进程的操做,这样处理只是同步链式,异步链式jquery从1.5开始就引入了Promise,jQuery.Deferred后期在讨论。
jQuery的主体框架就是这样,可是根据通常设计者的习惯,若是要为jQuery或者jQuery prototype添加属性方法,一样若是要提供给开发者对方法的扩展,从封装的角度讲是否是应该提供一个接口才对,字面就能看懂是对函数扩展,而不是看上去直接修改prototype.友好的用户接口,
jQuery支持本身扩展属性,这个对外提供了一个接口,jQuery.fn.extend()来对对象增长方法 从jQuery的源码中能够看到,jQuery.extend和jQuery.fn.extend实际上是同指向同一方法的不一样引用
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中扩展函数。 extend的实现
jQuery.extend = jQuery.fn.extend = function() {
var src, copyIsArray, copy, name, options, clone,
target = arguments[0] || {}, // 常见用法 jQuery.extend( obj1, obj2 ),此时,target为arguments[0]
i = 1,
length = arguments.length,
deep = false;
// Handle a deep copy situationif ( typeof target === "boolean" ) { // 若是第一个参数为true,即 jQuery.extend( true, obj1, obj2 ); 的状况
deep = target; // 此时target是true
target = arguments[1] || {}; // target改成 obj1// skip the boolean and the target
i = 2;
}
// Handle case when target is a string or something (possible in deep copy)if ( typeof target !== "object" && !jQuery.isFunction(target) ) { // 处理奇怪的状况,好比 jQuery.extend( 'hello' , {nick: 'casper})~~
target = {};
}
// extend jQuery itself if only one argument is passedif ( length === i ) { // 处理这种状况 jQuery.extend(obj),或 jQuery.fn.extend( obj )
target = this; // jQuery.extend时,this指的是jQuery;jQuery.fn.extend时,this指的是jQuery.fn
--i;
}
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined valuesif ( (options = arguments[ i ]) != null ) { // 好比 jQuery.extend( obj1, obj2, obj3, ojb4 ),options则为 obj二、obj3...// Extend the base objectfor ( name in options ) {
src = target[ name ];
copy = options[ name ];
// Prevent never-ending loopif ( target === copy ) { // 防止自引用,不赘述continue;
}
// Recurse if we're merging plain objects or arrays// 若是是深拷贝,且被拷贝的属性值自己是个对象if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
if ( copyIsArray ) { // 被拷贝的属性值是个数组
copyIsArray = false;
clone = src && jQuery.isArray(src) ? src : [];
} else { 被拷贝的属性值是个plainObject,好比{ nick: 'casper' }
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
} elseif ( copy !== undefined ) { // 浅拷贝,且属性值不为undefined
target[ name ] = copy;
}
}
}
}
// Return the modified objectreturn target;
复制代码
总结:
● 经过new jQuery.fn.init() 构建一个新的对象,拥有init构造器的prototype原型对象的方法
● 经过改变prorotype指针的指向,让这个新的对象也指向了jQuery类的原型prototype
● 因此这样构建出来的对象就继续了jQuery.fn原型定义的全部方法了