從Console中看看jQuery的原型鏈

寫在最前

這不是一篇分析源碼的文章——由於做者也沒有怎麼看源碼。本文主要分析jQuery中究竟是如何進行構造原型鏈的。思路是經過逆推來拋出問題推測答案再用正推的方式來分析梳理。歡迎關注做者博客,不按期更新中——javascript

jQuery是什麼

首先你知道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('#xxx')

驗證了無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

init方法

讓咱們再看下這段代碼: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本身也是構造函數能夠被顯式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來構建實例

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

相關文章
相關標籤/搜索