Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从Console中看看jQuery的原型链 #7

Open
Aaaaaaaty opened this issue Jun 19, 2017 · 0 comments
Open

从Console中看看jQuery的原型链 #7

Aaaaaaaty opened this issue Jun 19, 2017 · 0 comments

Comments

@Aaaaaaaty
Copy link
Owner

Aaaaaaaty commented Jun 19, 2017

写在最前

这不是一篇分析源码的文章——因为作者也没有怎么看源码。本文主要分析jQuery中到底是如何进行构造原型链的。思路是通过逆推来抛出问题再用正推的方式来分析解决问题。欢迎关注作者博客,不定期更新中——

jQuery是什么

首先你知道jQuery有两种使用方法吧?
一种是jQuery('#xxx');一种是new jQuery('#xxx');
这两种方式都会返回一个实例。其原型链应该有一大堆方法。比如:jQuery('#xxx').css;jQuery('#xxx').attr;jQuery('#xxx').html...等等。
并且我们应该认识到jQuery这个函数一方面返回了一个实例,另一方面其本身也是构造函数(因为 new jQuery),那么其原型链也应该指向了那一大堆方法。我们一步步打印一下来验证下猜测:

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属性上是不是有我们猜测的一大堆方法。

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中有着封装的方法可供实例调用。

new jQuery('#xxx')

验证了无new构造实例的形式之后再来看下对于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的一样都指向了那一大堆方法。

init方法

让我们再看下这段代码:

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!。那么是不是。。让我们打印一下我们的猜测:

console.log(jQuery.fn.init.prototype.init === jQuery.fn.init) //true

发现了么同学们!既然jQuery.fn可以调用jQuery.fn.init其原型链上的方法,那么一定有:

jQuery.fn.init.prototype === jQuery.fn // true

小结

好的现在大家可能有种似懂非懂的感觉?来看下面这张图来总结下我们的发现。

通过前文加上我们上图的展示,原型链的关系已经很明了了。在原型链上绑定了很多很多方法确定无疑。与此同时有三个东西指向了该原型链即:

jQuery.fn === jQuery.fn.init.prototype //true
jQuery.fn.init.prototype === jQuery.prototype //true

在完成这三个的指向之后就可以满足我们起初的需求:

  • 调用jQuery()可以返回一个实例
  • jQuery自己也是构造函数可以被显式new来构建实例
  • 实例的方法绑定在了原型链上

当然了jQuery里面还有方法是绑定在jQuery本身的,绑定在原型链上的方法通过jQuery('#xxx').xxx调用,这个是相对某个元素的方法,绑定在jQuery本身的方法通过$.xxx调用,这个是某种特定的静态方法。我们现在只是讨论基础的jQuery在最外层构建时这些prototype属性都是怎么关联的。想深入了解的欢迎去读源码——

正向梳理一遍

再回过头来看上文提到的三个需求:

  • 调用jQuery()可以返回一个实例
  • jQuery自己是构造函数可以被显式new来构建实例
  • 实例的方法绑定在了原型链上

如果让你来写一个你怎么写?ok,我们一步一步来

调用jQuery()可以返回一个实例

//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

init方法也要从原型链上调用

//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')

jQuery自己是构造函数可以被显式new来构建实例

//v3.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初级版原型链的一个构建。里面很多操作更多的是为了让暴露的变量尽可能的少,所以在原型链构件上有一种循环赋值的赶脚哈哈哈。有兴趣的同学可以继续研究。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant