国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

jQuery 源碼系列(一)總體架構(gòu)

svtter / 2296人閱讀

摘要:到目前為止,的貢獻(xiàn)者團(tuán)隊(duì)共名成員,多條,可想而知,是一個(gè)多么龐大的項(xiàng)目。參考源碼分析整體架構(gòu)源碼解析讀書(shū)筆記第二章構(gòu)造對(duì)象函數(shù)詳解本文在上的源碼地址,歡迎來(lái)。

歡迎來(lái)我的專欄查看系列文章。

決定你走多遠(yuǎn)的是基礎(chǔ),jQuery 源碼分析,向長(zhǎng)者膜拜!

我雖然接觸 jQuery 很久了,但也只是局限于表面使用的層次,碰到一些問(wèn)題,找到 jQuery 的解決辦法,然后使用。顯然,這種做法的弊端就是,無(wú)論你怎么學(xué),都只能是個(gè)小白。

當(dāng)我建立這個(gè)項(xiàng)目的時(shí)候,就表示,我要改變這一切了,做一些人想做,憧憬去做,但從沒(méi)踏入第一步的事情,學(xué)習(xí) jQuery 源碼。

到目前為止,jQuery 的貢獻(xiàn)者團(tuán)隊(duì)共 256 名成員,6000 多條 commits,可想而知,jQuery 是一個(gè)多么龐大的項(xiàng)目。jQuery 官方的版本目前是 v3.1.1,已經(jīng)衍生出 jQueryUI、jQueryMobile 等多個(gè)項(xiàng)目。

雖然我在前端爬摸打滾一年多,自認(rèn)基礎(chǔ)不是很好,在沒(méi)有外界幫助的情況下,直接閱讀項(xiàng)目源碼太難了,所以在邊參考遍實(shí)踐的過(guò)程中寫(xiě)下來(lái)這個(gè)項(xiàng)目。

首先,先推薦一個(gè) jQuery 的源碼查詢網(wǎng)站,這個(gè)網(wǎng)站給初學(xué)者非常大的幫助,不僅能查找不同版本的 jQuery 源碼,還能索引函數(shù),功能簡(jiǎn)直吊炸天。

另外,推薦兩個(gè)分析 jQuery 的博客:

jQuery源碼分析系列

原創(chuàng) jQuery1.6.1源碼分析系列(停止更新)

這兩個(gè)博客給我了很大的幫助,謝謝。

另外還有下面的網(wǎng)址,讓我在如何使用 jQuery 上得心應(yīng)手:

jQuery API 中文文檔

jQuery 總體架構(gòu)

首先,jQuery 是一個(gè)開(kāi)發(fā)框架,它的火爆程度已經(jīng)無(wú)法用言語(yǔ)來(lái)形容,當(dāng)你隨便打開(kāi)一個(gè)網(wǎng)站,一半以上直接使用了 jQuery。或許,早幾年,一個(gè)前端工程師,只要會(huì)寫(xiě) jQuery,就可以無(wú)憂工作。雖說(shuō)最近 react、vue 很火,但 jQuery 中許多精彩的方法和邏輯值得每一個(gè)前端人員學(xué)習(xí)。

和其眾多的框架一樣,總要把接口放到外面來(lái)調(diào)用,內(nèi)部往往是一個(gè)閉包,避免環(huán)境變量的污染。

先來(lái)看看 jQuery 使用上的幾大特點(diǎn):

$("#id") 函數(shù)方式直接生成 jQuery 對(duì)象

$("#id").css().html().hide() 鏈?zhǔn)秸{(diào)用

關(guān)于鏈?zhǔn)秸{(diào)用,我想有點(diǎn)基礎(chǔ)都很容易實(shí)現(xiàn),函數(shù)結(jié)尾 return this 即可,主要來(lái)介紹一下無(wú) new 實(shí)現(xiàn)創(chuàng)建對(duì)象。

無(wú) new 函數(shù)實(shí)現(xiàn)

下面是一個(gè)普通的函數(shù),很顯然,會(huì)陷入死循環(huán):

var jQuery = function(){
  return new jQuery();
}
jQuery.prototype = {
  ...
}

這個(gè)死循環(huán)來(lái)的太突然,jQuery() 會(huì)創(chuàng)建一個(gè) new jQuery,new jQuery 又會(huì)創(chuàng)建一個(gè) new jQuery...

jQuery 用一個(gè) init 函數(shù)來(lái)代替直接 new 函數(shù)名的方式,還要考慮到 jQuery 中分離作用域:

var jQuery = function(){
  return new jQuery.prototype.init();
}
jQuery.prototype = {
  constructor: jQuery,
  init: function(){
    this.jquery = 1.0;
    return this;
  },
  jquery: 2.0,
  each: function(){
    console.log("each");
    return this;
  }
}
jQuery().jquery //1.0
jQuery.prototype.jquery //2.0

jQuery().each() // error

上面看似運(yùn)行正常,但是問(wèn)題出在 jQuery().each() // error,訪問(wèn)不到 each 函數(shù)。實(shí)際上,new jQuery.prototype.init() 返回到是誰(shuí)的實(shí)例?是 init 這個(gè)函數(shù)的實(shí)例,所以 init 函數(shù)中的 this 就沒(méi)了意義。

那么,如果:

var jq = jQuery();
jq.__proto__ === jQuery.prototype;
jq.each === jQuery.prototype.each;

如果可以實(shí)現(xiàn)上面的 proto 的指向問(wèn)題,原型函數(shù)調(diào)用問(wèn)題就解決了,但實(shí)際上

var jq = jQuery();
jq.__proto__ === jQuery.prototype.init.prototype; //true

實(shí)際上,jq 的 proto 是指向 init 函數(shù)的原型,所以,我們可以把 jQuery.prototype.init.prototype = jQuery.prototype,這個(gè)時(shí)候,函數(shù)調(diào)用就順理成章了,而且使用的都是引用,指向的都是同一個(gè) prototype 對(duì)象,也不需要擔(dān)心循環(huán)問(wèn)題。實(shí)際上,jQuery 就是這么干的。

var jQuery = function(){
  return new jQuery.prototype.init();
}
jQuery.prototype = {
  constructor: jQuery,
  init: function(){
    this.jquery = 1.0;
    return this;
  },
  jquery: 2.0,
  each: function(){
    console.log("each");
    return this;
  }
}
jQuery.prototype.init.prototype = jQuery.prototype;
jQuery().each() //"each"
jQuery 內(nèi)部結(jié)構(gòu)圖

在說(shuō)內(nèi)部圖之前,先說(shuō)下 jQuery.fn,它實(shí)際上是 prototype 的一個(gè)引用,指向 jQuery.prototype 的,

var jQuery = function(){
  return new jQuery.prototype.init();
}
jQuery.fn = jQuery.prototype = {
  ...
}

那么為什么要用 fn 指向 prototype?我本人查閱了一些資料,貌似還是下面的回答比較中肯:簡(jiǎn)介。你不覺(jué)得 fn 比 prototype 好寫(xiě)多了嗎。

借用網(wǎng)上的一張圖:

從這張圖中可以看出,window 對(duì)象上有兩個(gè)公共的接口,分別是 $ 和 jQuery:

window.jQuery = window.$ = jQuery;

jQuery.extend 方法是一個(gè)對(duì)象拷貝的方法,包括深拷貝,后面會(huì)詳細(xì)講解源碼,暫時(shí)先放一邊。

下面的關(guān)系可能會(huì)有些亂,但是仔細(xì)看了前面的介紹,應(yīng)該能看懂。fn 就是 prototype,所以 jQuery 的 fn 和 prototype 屬性指向 fn 對(duì)象,而 init 函數(shù)本身就是 jQuery.prototype 中的方法,且 init 函數(shù)的 prototype 原型指向 fn。

鏈?zhǔn)秸{(diào)用

鏈?zhǔn)秸{(diào)用的好處,就是寫(xiě)出來(lái)的代碼非常簡(jiǎn)潔,而且代碼返回的都是同一個(gè)對(duì)象,提高代碼效率。

前面已經(jīng)說(shuō)了,在沒(méi)有返回值的原型函數(shù)后面添加 return this:

var jQuery = function(){
  return new jQuery.fn.init();
}
jQuery.fn = jQuery.prototype = {
  constructor: jQuery,
  init: function(){
    this.jquery = 3.0;
    return this;
  },
  each: function(){
    console.log("each");
    return this;
  }
}
jQuery.fn.init.prototype = jQuery.fn;
jQuery().each().each();
// "each"
// "each"
extend

jQuery 中一個(gè)重要的函數(shù)便是 extend,既可以對(duì)本身 jQuery 的屬性和方法進(jìn)行擴(kuò)張,又可以對(duì)原型的屬性和方法進(jìn)行擴(kuò)展。

先來(lái)說(shuō)下 extend 函數(shù)的功能,大概有兩種,如果參數(shù)只有一個(gè) object,即表示將這個(gè)對(duì)象擴(kuò)展到 jQuery 的命名空間中,也就是所謂的 jQuery 的擴(kuò)展。如果函數(shù)接收了多個(gè) object,則表示一種屬性拷貝,將后面多個(gè)對(duì)象的屬性全拷貝到第一個(gè)對(duì)象上,這其中,還包括深拷貝,即非引用拷貝,第一個(gè)參數(shù)如果是 true 則表示深拷貝。

jQuery.extend(target);// jQuery 的擴(kuò)展
jQuery.extend(target, obj1, obj2,..);//淺拷貝 
jQuery.extend(true, target, obj1, obj2,..);//深拷貝 

以下是 jQuery 3 之后的 extend 函數(shù)源碼,自己做了注釋:

jQuery.extend = jQuery.fn.extend = function () {
  var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},
    i = 1,
    length = arguments.length,
    deep = false;

  // 判斷是否為深拷貝
  if (typeof target === "boolean") {
    deep = target;

    // 參數(shù)后移
    target = arguments[i] || {};
    i++;
  }

  // 處理 target 是字符串或奇怪的情況,isFunction(target) 可以判斷 target 是否為函數(shù)
  if (typeof target !== "object" && !jQuery.isFunction(target)) {
    target = {};
  }

  // 判斷是否 jQuery 的擴(kuò)展
  if (i === length) {
    target = this; // this 做一個(gè)標(biāo)記,可以指向 jQuery,也可以指向 jQuery.fn
    i--;
  }

  for (; i < length; i++) {

    // null/undefined 判斷
    if ((options = arguments[i]) != null) {

      // 這里已經(jīng)統(tǒng)一了,無(wú)論前面函數(shù)的參數(shù)怎樣,現(xiàn)在的任務(wù)就是 target 是目標(biāo)對(duì)象,options 是被拷貝對(duì)象
      for (name in options) {
        src = target[name];
        copy = options[name];

        // 防止死循環(huán),跳過(guò)自身情況
        if (target === copy) {
          continue;
        }

        // 深拷貝,且被拷貝對(duì)象是 object 或 array
        // 這是深拷貝的重點(diǎn)
        if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
          // 說(shuō)明被拷貝對(duì)象是數(shù)組
          if (copyIsArray) {
            copyIsArray = false;
            clone = src && Array.isArray(src) ? src : [];
          // 被拷貝對(duì)象是 object
          } else {
            clone = src && jQuery.isPlainObject(src) ? src : {};
          }

          // 遞歸拷貝子屬性
          target[name] = jQuery.extend(deep, clone, copy);

          // 常規(guī)變量,直接 =
        } else if (copy !== undefined) {
            target[name] = copy;
        }
      }
    }
  }

  // Return the modified object
  return target;
}

extend 函數(shù)符合 jQuery 中的參數(shù)處理規(guī)范,算是比較標(biāo)準(zhǔn)的一個(gè)。jQuery 對(duì)于參數(shù)的處理很有一套,總是喜歡錯(cuò)位來(lái)使得每一個(gè)位置上的變量和它們的名字一樣,各司其職。比如 target 是目標(biāo)對(duì)象,如果第一個(gè)參數(shù)是 boolean 型的,就對(duì) deep 賦值 target,并把 target 向后移一位;如果參數(shù)對(duì)象只有一個(gè),即對(duì) jQuery 的擴(kuò)展,就令 target 賦值 this,當(dāng)前指針 i 減一。

這種方法邏輯雖然很復(fù)雜,但是帶來(lái)一個(gè)非常大的優(yōu)勢(shì):后面的處理邏輯只需要一個(gè)就可以。target 就是我們要拷貝的目標(biāo),options 就是要拷貝的對(duì)象,邏輯又顯得非常的清晰。

extend 函數(shù)還需要主要一點(diǎn),jQuery.extend = jQuery.fn.extend,不僅 jQuery 對(duì)象又這個(gè)函數(shù),連原型也有,那么如何區(qū)分對(duì)象是擴(kuò)展到哪里了呢,又是如何實(shí)現(xiàn)的?

其實(shí)這一切都要借助與 javascript 中 this 的動(dòng)態(tài)性,target = this,代碼就放在那里,誰(shuí)去執(zhí)行,this 就會(huì)指向誰(shuí),就會(huì)在它的屬性上擴(kuò)展。

由 extend 衍生的函數(shù)

再看 extend 源碼,里面有一些函數(shù),只是看名字知道了它是干什么的,我專門(mén)挑出來(lái),找到它們的源碼。

jQuery.isFunction 源碼
jQuery.isFunction = function (obj) {
    return jQuery.type(obj) === "function";
}

這也太簡(jiǎn)單了些。這里又要引出 jQuery 里一個(gè)重要的函數(shù) jQuery.type,這個(gè)函數(shù)用于類型判斷。

首先,為什么傳統(tǒng)的 typeof 不用?因?yàn)椴缓糜茫ù颂帒?yīng)有一個(gè)哭臉):

// Numbers
typeof 37 === "number";
typeof 3.14 === "number";
typeof(42) === "number";
typeof Math.LN2 === "number";
typeof Infinity === "number";
typeof NaN === "number"; // Despite being "Not-A-Number"
typeof Number(1) === "number"; // but never use this form!

// Strings
typeof "" === "string";
typeof "bla" === "string";
typeof (typeof 1) === "string"; // typeof always returns a string
typeof String("abc") === "string"; // but never use this form!

// Booleans
typeof true === "boolean";
typeof false === "boolean";
typeof Boolean(true) === "boolean"; // but never use this form!

// Symbols
typeof Symbol() === "symbol"
typeof Symbol("foo") === "symbol"
typeof Symbol.iterator === "symbol"

// Undefined
typeof undefined === "undefined";
typeof declaredButUndefinedVariable === "undefined";
typeof undeclaredVariable === "undefined"; 

// Objects
typeof {a:1} === "object";

// use Array.isArray or Object.prototype.toString.call
// to differentiate regular objects from arrays
typeof [1, 2, 4] === "object";

typeof new Date() === "object";

// The following is confusing. Don"t use!
typeof new Boolean(true) === "object"; 
typeof new Number(1) === "object"; 
typeof new String("abc") === "object";

// Functions
typeof function(){} === "function";
typeof class C {} === "function";
typeof Math.sin === "function";

// This stands since the beginning of JavaScript
typeof null === "object";

可以看得出來(lái),對(duì)于一些 new 對(duì)象,比如 new Number(1),也會(huì)返回 object。具體請(qǐng)參考typeof MDN。

網(wǎng)上有兩種解決方法(有效性未經(jīng)考證,請(qǐng)相信 jQuery 的方法),一種是用 constructor.nameObject.prototype.constructor MDN,一種是用 Object.prototype.toString.call()Object.prototype.toString(),最終 jQuery 選擇了后者。

var n1 = 1;
n1.constructor.name;//"Number"
var n2 = new Number(1);
n2.constructor.name;//"Number"

var toString = Object.prototype.toString;
toString.call(n1);//"[object Number]"
toString.call(n2);//"[object Number]"

以上屬于科普,原理不多闡述,接下來(lái)繼續(xù)看源碼 jQuery.type

// 這個(gè)對(duì)象是用來(lái)將 toString 函數(shù)返回的字符串轉(zhuǎn)成
var class2type = {
    "[object Boolean]": "boolean",
    "[object Number]": "number",
    "[object String]": "string",
    "[object Function]": "function",
    "[object Array]": "array",
    "[object Date]": "date",
    "[object RegExp]": "regexp",
    "[object Object]": "object",
    "[object Error]": "error",
    "[object Symbol]": "symbol"
}
var toString = Object.prototype.toString;

jQuery.type = function (obj) {
    if (obj == null) {
        return obj + "";
    }
    return 
      typeof obj === "object" || typeof obj === "function" ? 
        class2type[toString.call(obj)] || "object" : 
        typeof obj;
}

因?yàn)?jQuery 用的是 toString 方法,所以需要有一個(gè) class2type 的對(duì)象用來(lái)轉(zhuǎn)換。

jQuery.isPlainObject

這個(gè)函數(shù)用來(lái)判斷對(duì)象是否是一個(gè)純粹的對(duì)象,:

var getProto = Object.getPrototypeOf;//獲取父對(duì)象
var hasOwn = class2type.hasOwnProperty;
var fnToString = hasOwn.toString;
var ObjectFunctionString = fnToString.call( Object );

jQuery.isPlainObject = function (obj) {
    var proto, Ctor;

    // 排除 underfined、null 和非 object 情況
    if (!obj || toString.call(obj) !== "[object Object]") {
        return false;
    }

    proto = getProto(obj);

    // Objects with no prototype (e.g., `Object.create( null )`) are plain
    if (!proto) {
        return true;
    }

    // Objects with prototype are plain iff they were constructed by a global Object function
    Ctor = hasOwn.call(proto, "constructor") && proto.constructor;
    return typeof Ctor === "function" && fnToString.call(Ctor) === ObjectFunctionString;
}

看一下效果:

jQuery.isPlainObject({});// true
jQuery.isPlainObject({ a: 1 });// true
jQuery.isPlainObject(new Object());// true

jQuery.isPlainObject([]);// false
jQuery.isPlainObject(new String("a"));// false
jQuery.isPlainObject(function(){});// false

除了這幾個(gè)函數(shù)之外,還有個(gè) Array.isArray(),這個(gè)真的不用介紹了吧。

總結(jié)

總結(jié)還是多說(shuō)一點(diǎn)的好,現(xiàn)在已經(jīng)基本理清 jQuery 內(nèi)部的情況了?no,還差一點(diǎn),看下面的代碼:

(function(window) {
  // jQuery 變量,用閉包避免環(huán)境污染
  var jQuery = (function() {
    var jQuery = function(selector, context) {
        return new jQuery.fn.init(selector, context, rootjQuery);
    };

    // 一些變量聲明

    jQuery.fn = jQuery.prototype = {
        constructor: jQuery,
        init: function(selector, context, rootjQuery) {
          // 下章會(huì)重點(diǎn)討論
        }

        // 原型方法
    };

    jQuery.fn.init.prototype = jQuery.fn;

    jQuery.extend = jQuery.fn.extend = function() {};//已介紹

    jQuery.extend({
        // 一堆靜態(tài)屬性和方法
        // 用 extend 綁定,而不是直接在 jQuery 上寫(xiě)
    });

    return jQuery;
  })();

  // 工具方法 Utilities
  // 回調(diào)函數(shù)列表 Callbacks Object
  // 異步隊(duì)列 Defferred Object
  // 瀏覽器功能測(cè)試 Support
  // 數(shù)據(jù)緩存 Data
  // 隊(duì)列 Queue
  // 屬性操作 Attributes
  // 事件系統(tǒng) Events
  // 選擇器 Sizzle
  // DOM遍歷 Traversing
  // 樣式操作 CSS(計(jì)算樣式、內(nèi)聯(lián)樣式)
  // 異步請(qǐng)求 Ajax
  // 動(dòng)畫(huà) Effects
  // 坐標(biāo) Offset、尺寸 Dimensions

  window.jQuery = window.$ = jQuery;
})(window);

可以看出 jQuery 很巧妙的整體布局思路,對(duì)于屬性方法和原型方法等區(qū)分,防止變量污染等,都做的非常好。閱讀框架源碼只是開(kāi)頭,有趣的還在后面。

參考

jQuery 2.0.3 源碼分析core - 整體架構(gòu)
《jQuery源碼解析》讀書(shū)筆記(第二章:構(gòu)造jQuery對(duì)象)
jQuery.isPlainObject() 函數(shù)詳解

本文在 github 上的源碼地址,歡迎來(lái) star。

歡迎來(lái)我的博客交流。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/88136.html

相關(guān)文章

  • jQuery 源碼系列(十)event 總體概述

    摘要:而事件委托的概念事件目標(biāo)自身不處理事件,而是將其委托給父元素或祖先元素或根元素,而借助事件的冒泡性質(zhì)由內(nèi)向外來(lái)達(dá)到最終處理事件。而且一旦出現(xiàn),局部刷新導(dǎo)致重新綁定事件。函數(shù)的用法,代表要移除的事件,表示選擇的,表示事件處理函數(shù)。 歡迎來(lái)我的專欄查看系列文章。 這次的內(nèi)容是來(lái)介紹關(guān)于 jQuery 的事件委托。不過(guò)在之前呢有必要先來(lái)了解一下 JS 中的事件委托與冒泡,我之前也寫(xiě)過(guò)類似的博...

    liujs 評(píng)論0 收藏0
  • jQuery 源碼系列(二)init 介紹

    摘要:源碼中接受個(gè)參數(shù),空參數(shù),這個(gè)會(huì)直接返回一個(gè)空的對(duì)象,。,這是一個(gè)標(biāo)準(zhǔn)且常用法,表示一個(gè)選擇器,這個(gè)選擇器通常是一個(gè)字符串,或者等,表示選擇范圍,即限定作用,可為,對(duì)象。,會(huì)把普通的對(duì)象或?qū)ο蟀b在對(duì)象中。介紹完入口,就開(kāi)始來(lái)看源碼。 歡迎來(lái)我的專欄查看系列文章。 init 構(gòu)造器 前面一講總體架構(gòu)已經(jīng)介紹了 jQuery 的基本情況,這一章主要來(lái)介紹 jQuery 的入口函數(shù) jQu...

    Tony_Zby 評(píng)論0 收藏0
  • Underscore 源碼總體架構(gòu)

    摘要:不過(guò)這樣子又回帶來(lái)另一個(gè)問(wèn)題,對(duì)于函數(shù),函數(shù)返回什么不重要,主要是處理過(guò)程,可以支持鏈?zhǔn)秸{(diào)用,對(duì)于函數(shù),返回的是處理后的結(jié)果,可以不用鏈?zhǔn)剑院瘮?shù)就是來(lái)判斷是否需要鏈?zhǔn)剑鴮?duì)返回值進(jìn)行處理。然后后面還有一個(gè)函數(shù),也是用來(lái)作為回調(diào)函數(shù)的。 其實(shí),學(xué)習(xí)一個(gè)庫(kù)的源碼,最重要的就是先理清它的基本架構(gòu),jQuery 是這樣,Underscore 也應(yīng)該是這樣。 Underscore 這個(gè)庫(kù)提供...

    zhunjiee 評(píng)論0 收藏0
  • 前端經(jīng)驗(yàn) - 收藏集 - 掘金

    摘要:我拖拖拖拖放基礎(chǔ)篇前端掘金不要搞錯(cuò),本文不是講如何拖地的。結(jié)構(gòu)說(shuō)明前端應(yīng)該從哪些方面來(lái)優(yōu)化網(wǎng)站前端掘金不知道是哪位大牛的文章,轉(zhuǎn)過(guò)來(lái)回答。 我拖拖拖 --H5 拖放 API 基礎(chǔ)篇 - 前端 - 掘金不要搞錯(cuò),本文不是講如何拖地的。看過(guò)《javascript精粹》朋友應(yīng)該知道,他實(shí)現(xiàn)拖放的過(guò)程比較復(fù)雜,現(xiàn)在時(shí)代不同了,我們用H5的新的拖放API就能非常方便的實(shí)現(xiàn)拖放效果了。最近在園子見(jiàn)...

    MudOnTire 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<