JQuery源碼閱讀(一)整體架構(gòu)

很早就聽一些大神說要讀源碼,直到前一段時(shí)間春招受挫才立下決心,先入手一個(gè)JQuery吧,以后有機(jī)會(huì)可以摸一摸Vue。

jQuery,說起都有一種張國榮、陳百強(qiáng)的感覺了,但是還是可以重溫的,面試官教育我,不能盲目跟風(fēng),人云亦云。
其實(shí)看了幾天了,一行一行看沒有重點(diǎn),直到看了幾個(gè)大佬的博文(比如下面這位),覺得可以嘗試了。


一位大佬.png

還有一位出了一個(gè)系列的,大家在中文社區(qū)應(yīng)該看得到(他也參加過螞蟻金服17春招,而且通過了,敬佩之心油然而生?。?。

從外層入手

( function( global, factory ) {
  "use strict";
} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {

  if ( !noGlobal ) {
    window.jQuery = window.$ = jQuery;
  }// 在node環(huán)境非全局執(zhí)行時(shí)不會(huì)暴露為接口
});

首先把映入眼簾的最外層拿出來,就是一個(gè)自執(zhí)行函數(shù),大佬也叫閉包??梢宰龅健接小捅┞兜綀?zhí)行環(huán)境指定的jQuery接口(noGlobal控制)。
自執(zhí)行函數(shù)里放的啥?

if ( typeof module === "object" && typeof module.exports === "object" ) {
// 自執(zhí)行函數(shù)(模塊化)commenJs?(存在document屬性?
// 執(zhí)行函數(shù):暴露包裝函數(shù)待傳入一個(gè)含document的對象):執(zhí)行函數(shù)
    module.exports = global.document ?
      factory( global, true ) :
      function( w ) {
        if ( !w.document ) {
          throw new Error( "jQuery requires a window with a document" );
        }
        return factory( w );
      };
  } else {
    factory( global );
  }  // node環(huán)境檢查是否是全局執(zhí)行,非node環(huán)境直接執(zhí)行

判斷了是不是commonJS(node的模塊化標(biāo)準(zhǔn)),傳入的執(zhí)行環(huán)境作用域變量是不是有document。

進(jìn)入正題

現(xiàn)在可以正視那個(gè)被嚴(yán)格控制的神秘又長的函數(shù)了。JQuery是啥?我找一下,看起來一時(shí)半會(huì)沒有一個(gè)全面的答案。

var
    version = "3.3.1", //版本號(hào)唄,忽視一下。
    jQuery = function( selector, context ) {
      return new jQuery.fn.init( selector, context );//jQuery ()實(shí)際實(shí)例化了init
    };
  jQuery.fn = jQuery.prototype = { };

jQuery就是一個(gè)起構(gòu)造作用的函數(shù),實(shí)例化了 jQuery.fn.init。fn是一個(gè)私有屬性,指向jQuery的原型。

init是啥
var rootjQuery,
    rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
    init = jQuery.fn.init = function( selector, context, root ) {
  var match, elem;
  // 規(guī)避: $(""), $(null), $(undefined), $(false)
  if ( !selector ) {
    return this;
  }
  // Method init() accepts an alternate rootjQuery
  // so migrate can support jQuery.sub (gh-2101)
  root = root || rootjQuery;
  // Handle HTML strings
  if ( typeof selector === "string" ) {
    // selector是string
  } else if ( selector.nodeType ) {
    // selector是DOM節(jié)點(diǎn)
  } else if ( isFunction( selector ) ) {
    // selector是函數(shù)
  }
  return jQuery.makeArray( selector, this );
};

開始有些東西試圖阻止我順藤摸瓜了,先記下來。

rootjQuery、jQuery.makeArray|init私有match,elem

注意到了return this;如果被作為一個(gè)構(gòu)造函數(shù)使用,這個(gè)return是不言自明的,但是他明說了。猜測,有非new的調(diào)用,或者定制無論何種調(diào)用的return(返回一個(gè)空對象可能想得到其原型?)。
先往下瞄一眼 眼熟吧,一句委托,return的this就是prototype指向jQuery原型的空對象。

init.prototype = jQuery.fn;

有三個(gè)流程判斷語句,我要撿軟的捏!

selector是DOM節(jié)點(diǎn)
// HANDLE: $(DOMElement)
} else if ( selector.nodeType ) {
    this[ 0 ] = selector;
    this.length = 1;
    return this;
}

第一個(gè)參數(shù)如果傳入的DOM節(jié)點(diǎn),放入0屬性,length屬性置1,返回了一個(gè)像數(shù)組的對象。

selector是函數(shù)
// HANDLE: $(function)
// Shortcut for document ready
} else if ( isFunction( selector ) ) {
    return root.ready !== undefined ?
        root.ready( selector ) :
        // Execute immediately if ready is not present
        selector( jQuery );
}

init參數(shù)root.ready

在前面jQuery實(shí)例化的時(shí)候并沒有傳入root參數(shù),在沒有root的情況下執(zhí)行selector( jQuery ),應(yīng)該可以在傳入的函數(shù)里擴(kuò)充jQuery函數(shù)的內(nèi)容吧。

selector非DOM、function、string
return jQuery.makeArray( selector, this );

那就return 讓這個(gè)未知的函數(shù)執(zhí)行唄!

selector是string
if ( typeof selector === "string" ) {
  if ( selector[ 0 ] === "<" &&
    selector[ selector.length - 1 ] === ">" &&
    selector.length >= 3 ) { // /^<[\w\W]+>$/
    match = [ null, selector, null ];
  } else {
    match = rquickExpr.exec( selector );
  }
  if ( match && ( match[ 1 ] || !context ) ) {

  } else if ( !context || context.jquery ) {

  } else {

  }
}

這段的結(jié)構(gòu)也被抽離出來了。有必要回看rquickExpr了/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,就是它,匹配到以空格或<[\w\W]+>開頭的字符串。
這樣match就有兩種樣子了,[ null, selector, null ]或正則匹配后的(數(shù)組第二項(xiàng)為去空格的標(biāo)簽字符串)。

if ( match && ( match[ 1 ] || !context ) ) {
  // HANDLE: $(html) -> $(array)
  if ( match[ 1 ] ) {
    // HANDLE: $(#id)
  } else {
    elem = document.getElementById( match[ 2 ] );
    if ( elem ) {
      // Inject the element directly into the jQuery object
      this[ 0 ] = elem;
      this.length = 1;
    }
    return this;
  }
}

如果match的第二項(xiàng)空,則按照第三項(xiàng)獲取全局的對應(yīng)id的節(jié)點(diǎn),存入類數(shù)組對象返回。

if ( match[ 1 ] ) {
    context = context instanceof jQuery ? context[ 0 ] : context;
    jQuery.merge( this, jQuery.parseHTML(
                match[ 1 ],
                context && context.nodeType ?context.ownerDocument || context 
                : document,
                true
                ) );
    // HANDLE: $(html, props)
    if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context )) {
        for ( match in context ) {
            // Properties of context are called as methods if possible
            if ( isFunction( this[ match ] ) ) {
                this[ match ]( context[ match ] );
                // ...and otherwise set as attributes
            } else {
                this.attr( match, context[ match ] );
            }
        }
    }
    return this;
}

要補(bǔ)充一句了

var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );

jQuery.merge jQuery.isPlainObject init的方法attr

其中context.ownerDocument得到頁面根節(jié)點(diǎn),而rsingleTag 可以匹配一個(gè)無文本的完整標(biāo)簽。
后面的for-in循環(huán)大概是可以判斷出操作的合法性了后,對初始化的對象拷貝屬性,在原基礎(chǔ)上重寫DOM節(jié)點(diǎn)的一些方法。

else if ( !context || context.jquery ) {
    return ( context || root ).find( selector );
    // HANDLE: $(expr, context)
    // (which is just equivalent to: $(context).find(expr)
} else {
    return this.constructor( context ).find( selector );
}

find()可能就是我們用到的find方法

其他情況則以第二參數(shù)構(gòu)造,再find第一參數(shù)。
另外,向下瞄一眼rootjQuery 找到了(init了document)。

// Initialize central reference
rootjQuery = jQuery( document );

整體架構(gòu)的入手和init函數(shù)就完成了,希望能堅(jiān)持下去吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • jQuery jQuery是JavaScript世界中使用最廣泛的一個(gè)庫。 jQuery這么流行,肯定是因?yàn)樗鉀Q...
    星騰_范特西閱讀 2,199評論 0 27
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,880評論 0 3
  • 1.JQuery 基礎(chǔ) 改變web開發(fā)人員創(chuàng)造搞交互性界面的方式。設(shè)計(jì)者無需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性。 1....
    LaBaby_閱讀 1,515評論 0 2
  • 1.JQuery 基礎(chǔ) 改變web開發(fā)人員創(chuàng)造搞交互性界面的方式。設(shè)計(jì)者無需花費(fèi)時(shí)間糾纏JS復(fù)雜的高級(jí)特性。 1....
    LaBaby_閱讀 1,277評論 0 1
  • 今早拉開窗簾陽光便落在了臉上,初冬季節(jié)能擁有這樣的陽光實(shí)屬不易。這些天扭傷了腳,醫(yī)生交代需在家里靜養(yǎng),吃完午飯一直...
    鏡中的我閱讀 497評論 0 0

友情鏈接更多精彩內(nèi)容