这不是一篇分析源码的文章——由于做者也没有怎么看源码。本文主要分析jQuery中究竟是如何进行构造原型链的。思路是经过逆推来抛出问题推测答案再用正推的方式来分析梳理。欢迎关注做者博客,不按期更新中——javascript
首先你知道jQuery有两种使用方法吧?
一种是jQuery('#xxx');一种是new jQuery('#xxx');
这两种方式都会返回一个实例。其原型链应该有一大堆方法。好比:jQuery('#xxx').css;jQuery('#xxx').attr;jQuery('#xxx').html...等等。
而且咱们应该认识到jQuery这个函数一方面返回了一个实例,另外一方面其自己也是构造函数(由于 new jQuery),那么其原型链也应该指向了那一大堆方法。咱们一步步打印一下来验证下猜想:css
console.log(jQuery) // 来看下jQuery函数体
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
} //小技巧,能够引入没有压缩过的jQuery进行学习,这样备注,变量名会抱持原样。复制代码
好的果真没猜错,咱们看到了一个构造函数为jQuery.fn.init
。经过new这个构造函数就能够采用第一种jQuery()的形式来生成实例。接下来验证下jQuery.fn.init
的prototype属性上是否是有咱们猜想的一大堆方法。html
console.log(Object.keys(jQuery.fn.init.prototype))
// ["jquery","constructor","init","show","hide","toggle","on","one", "off","detach","remove","text","append", ...]复制代码
从结果中也能够知道咱们的推测是正确的。在jQuery.fn.init
的prototype中有着封装的方法可供实例调用。java
验证了无new构造实例的形式以后再来看下对于jQuery同时应该是个构造函数的猜想。jquery
console.log(Object.keys(jQuery.prototype))
//["jquery","constructor","init","show","hide","toggle","on","one", "off","detach","remove","text","append", ...]
console.log(jQuery.prototype === jQuery.fn.init.prototype) //true复制代码
能够看出jQuery也确实是一个构造函数其prototype和jQuery.fn.init的同样都指向了那一大堆方法。git
让咱们再看下这段代码:github
function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
}复制代码
这里面返回的构造函数jQuery.fn.init
咱们能够当作是调用了jQuery.fn的init方法。同时细心的同窗们应该能够观察到,在jQuery.fn.init.prototype
中也有个方法叫init!。那么是否是。。让咱们打印一下咱们的猜想:app
console.log(jQuery.fn.init.prototype.init === jQuery.fn.init) //true复制代码
发现了么同窗们!既然jQuery.fn能够调用jQuery.fn.init其原型链上的方法,那么必定有:ide
jQuery.fn.init.prototype === jQuery.fn // true复制代码
好的如今你们可能有种似懂非懂的感受?来看下面这张图来总结下咱们的发现。
函数
经过前文加上咱们上图的展现,原型链的关系已经很明了了。在原型链上绑定了不少不少方法肯定无疑。与此同时有三个东西指向了该原型链即:
jQuery.fn === jQuery.fn.init.prototype //true
jQuery.fn.init.prototype === jQuery.prototype //true复制代码
在完成这三个的指向以后就能够知足咱们起初的需求:
固然了jQuery里面还有方法是绑定在jQuery自己的,绑定在原型链上的方法经过jQuery('#xxx').xxx
调用,这个是相对某个元素的方法,绑定在jQuery自己的方法经过$.xxx
调用,这个是某种特定的静态方法。咱们如今只是讨论基础的jQuery在最外层构建时这些prototype属性都是怎么关联的。想深刻了解的欢迎去读源码——
再回过头来看上文提到的三个需求:
若是让你来写一个你怎么写?ok,咱们一步一步来
//v1.0
var j = function(selector){
return new init(selector);
}
var init = function() {...}复制代码
//v2.0
//即fn.init的原型应该是j.prototype
var fn = {}
var xxx = function() {}
fn.init = function(selector) {console.log(selector)}
var j = function(selector){
return new fn.init(selector);
}
xxx.prototype = {
setColor: function(color){console.log(color)}
...
}
fn.init.prototype = xxx.prototype
var a = new j(1) //1
a.setColor('red') // red复制代码
//v3.0
var xxx = function() {}
var j = function(selector){
return new j.fn.init(selector); //借用j.fn来找到原型链方法,否则找不到
}
j.fn = xxx.prototype = { //j自己是构造函数
init: function(selector) {
console.log(selector)
},
setColor: function(color) {
console.log('setColor:' + color)
}
}
j.fn.init.prototype = j.fn
var a = new j(1)
a.setColor('red')复制代码
//v4.0
//将xxx替换为j,那么j当作构造函数后其原型链也指向了那一堆方法
var j = function(selector){
return new j.fn.init(selector); //借用j.fn来找到原型链方法,否则找不到
}
j.fn = j.prototype = { //j自己是构造函数
init: function(selector) {
console.log(selector)
},
setColor: function(color) {
console.log('setColor:' + color)
}
}
j.fn.init.prototype = j.fn
var a = new j(1)
a.setColor('red')复制代码
至此咱们便写好了一个jQuery初级版原型链的一个构建。里面不少操做更多的是为了让暴露的变量尽量的少,因此在原型链构件上有一种循环赋值的赶脚哈哈哈。有兴趣的同窗能够继续研究。
惯例po做者的博客,不定时更新中——有问题欢迎在issues下交流,捂脸求star=。=