成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

初步學(xué)習(xí) jQuery 核心 API

張巨偉 / 1279人閱讀

摘要:進(jìn)一步了解類數(shù)組對象可以看這篇文章對象的構(gòu)建和分離構(gòu)造器然后我們回來看看,讓我們悲傷的代碼。。。然后又通過下面的語句,將兩個(gè)獨(dú)立的構(gòu)造器關(guān)聯(lián)起來了。

背景

不造輪子的程序員不是好程序員,所以我們今天嘗試造一下輪子。今天的主角是 jQuery ,雖然現(xiàn)在市面上已被 React,Angular,Vue 等擠的容不下它的位置,但是它的簡單 API 設(shè)計(jì)依然優(yōu)秀,值得學(xué)習(xí)和體會。

任務(wù)

今天造輪子的目標(biāo)不是實(shí)現(xiàn)功能,而是專注在 API 和架構(gòu)。你需要完成的東西支持以下功能:

1、$(selector) 根據(jù)選擇器構(gòu)造一個(gè)jQuery 對象

2、jQuery 對象是一個(gè)類數(shù)組,需要支持以下方法:

var a = $(selector);
a[0]                                   訪問元素
a.length                               元素個(gè)數(shù)
a.each(function(){ console.log(this)}) 迭代操作

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

var a = $(selector);
a.addClass("hello").click(function(){...});

4、擴(kuò)展實(shí)例方法

$.fn.tabs = function(){
  console.log(this);
};

之后就可以這樣使用

$(selector).tabs();

好,開始我們的任務(wù)。

我在 jQuery 的官網(wǎng)下載的開發(fā)版(沒有壓縮)代碼,版本 3.2.1我記的上一次用的時(shí)候好像才 1.8左右 ?。只有一個(gè) js 文件,打開一看,我的天,一萬多行代碼。。。

代碼有點(diǎn)多,我們先梳理一下結(jié)構(gòu),找個(gè)入口開始看。

jQuery 的整體架構(gòu)
( function( global, factory ) {
  //省略...
} )( typeof window !== "undefined" ? window : this,
  function( window, noGlobal ) {
    jQuery = function( selector, context ) {
      return new jQuery.fn.init( selector, context );
      //這里用new,省去了構(gòu)造函數(shù) jQuery() 前面的運(yùn)算符new,因此我們可以直接寫 jQuery()
    };
    jQuery.fn = jQuery.prototype = {
      jquery: version,
      constructor: jQuery,
      ...
    };
    // 通過覆蓋原型的方式,把 jQuery.prototype 覆蓋到 jQuery.fn.init.prototype 上
    jQuery.fn.init.prototype = jQuery.fn;
      //...
    jQuery.extend = jQuery.fn.extend = function(){
    ....//
    };
    jQuery.extend( {
      isFunction,
      type,
      isWindow,
      ...
    })
    //jQuery.extend()和jQuery.fn.extend()
    //用于合并多個(gè)對象的屬性到第一個(gè)對象,類似于 es6 的 Object.assign(),不過還是有區(qū)別的
      if ( !noGlobal ) {
        window.jQuery = window.$ = jQuery;
      }
      return jQuery;
  }));
源碼分析 立即調(diào)用表達(dá)式

jQuery 立即調(diào)用表達(dá)式簡化版

(function(window, factory) {
    factory(window)
}(this, function() {
    return function() {
       //jQuery的調(diào)用
    }
}))

一上來,是個(gè) 立即調(diào)用表達(dá)式。 解決命名空間與變量污染的問題,全局變量是魔鬼, 匿名函數(shù)可以有效的保證在頁面上寫入 JavaScript,而不會造成全局變量的污染,通過小括號,讓其加載的時(shí)候立即初始化,這樣就形成了一個(gè)單例模式的效果從而只會執(zhí)行一次。

jQuery 的這個(gè)立即調(diào)用表達(dá)式的具體講解可以參考這里。

jQuery = function( selector, context ) {
   return new jQuery.fn.init( selector, context );
}

//...

window.jQuery = window.$ = jQuery;

jQuery 賦值給了 window.jQuerywindow.$ 所以我們在使用 jQuery 的時(shí)候 $jQuery 是等價(jià)的。

類數(shù)組對象

但是 jQuery() 返回了 new jQuery.fn.init(),為什么這樣寫?一臉懵逼。。。。

悲傷先放一邊,我們先看一下這個(gè)函數(shù) jQuery.fn.init(selector, context)

init = jQuery.fn.init = function( selector, context, root ) {
        // HANDLE: $(""), $(null), $(undefined), $(false)
        // Handle HTML strings
        // HANDLE: $(html) -> $(array)
        // HANDLE: $(html, props)
        // HANDLE: $(#id)
        // HANDLE: $(expr, $(...))
        // HANDLE: $(expr, context)
        // HANDLE: $(DOMElement)
        // HANDLE: $(function)
        return jQuery.makeArray( selector, this );
    };
init.prototype = jQuery.fn;

這個(gè)函數(shù)就是對參數(shù) selector 對應(yīng)的 html、idclass 等不同選擇器的處理方式,并返回一個(gè)類數(shù)組對象。

看到這我們就能實(shí)現(xiàn)我們今天任務(wù)第一個(gè)目標(biāo)以及第二個(gè)目標(biāo)的 1/2 了。? 上代碼!

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

init = jQuery.fn.init = function( selector ) {
  var elem = document.querySelectorAll(selector);
  this.length = elem.length;
  this[0] = elem[0];
  for (i = 0; i < elem.length; i++) {
    this[i] = elem[i];
  }
  this.context = document;
  this.selector = selector;
  return this;
}

這里有一個(gè) jQuery 的特點(diǎn) 類數(shù)組對象結(jié)構(gòu)。

所謂的類數(shù)組對象:

擁有一個(gè) length 屬性和若干索引屬性的對象

舉個(gè)例子:

var array = ["name", "age", "sex"];

var arrayLike = {
    0: "name",
    1: "age",
    2: "sex",
    length: 3
}

jQuery 能像數(shù)組一樣操作,通過對象 get 方法或者直接通過下標(biāo) 0 索引就能轉(zhuǎn)成 DOM 對象。同時(shí)還擁有各種自定義方法,自定義屬性,看 jquery 對象的優(yōu)雅的訪問方式即可知是如此美妙的對象。

進(jìn)一步了解類數(shù)組對象可以看這篇文章

對象的構(gòu)建和分離構(gòu)造器

然后我們回來看看,讓我們悲傷的代碼。。。

jQuery = function( selector, context ) {
   return new jQuery.fn.init( selector, context );
}

之所以這樣寫,演變過程是這樣的:

1、出于實(shí)例化 jQuery 對象性能的考慮 jQuery 采用了原型式的結(jié)構(gòu)構(gòu)建對象?
(jQuery.prototype)

                                  ?

2、jQuery 為了初始化對象實(shí)例更方便,采用了無 new 化,初始化對象時(shí),可以不寫 new 操作符
(return new jQuery...)

                                  ?

3、jQuery 為了避免出現(xiàn) return jQuery 無限遞歸自己,這種死循環(huán)的問題,采取的手段是把原型上的一個(gè) init 方法作為構(gòu)造器

                                  ?

4、最后,就成了這樣了。
return new jQuery.fn.init()

這樣確實(shí)解決了循環(huán)遞歸的問題,但是又問題來了,init 是 jQuery 原型上作為構(gòu)造器的一個(gè)方法,那么其 this 就不是 jQuery了,所以 this 就完全引用不到 jQuery 的原型了,所以這里通過 new 把 init 方法與 jQuery 給分離成2個(gè)獨(dú)立的構(gòu)造器。

然后 jQuery 又通過下面的語句,將兩個(gè)獨(dú)立的構(gòu)造器關(guān)聯(lián)起來了。

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

這樣整個(gè)結(jié)構(gòu)就串起來了,不得不佩服作者的設(shè)計(jì)思路,別具匠心。

上面說的如果沒看懂,可以參考這兩篇文章:

jQuery 源碼解析 - 對象的構(gòu)建

jQuery 源碼解析 - 分離構(gòu)造器

靜態(tài)與實(shí)例方法共享設(shè)計(jì)

我們要實(shí)現(xiàn)目標(biāo)2中的 each 迭代操作,就要說一下 jQuery 的另一個(gè)特性 靜態(tài)與實(shí)例方法共享

$(".box").each()   //作為實(shí)例方法存在 遍歷一個(gè)jQuery對象的,是為jQuery內(nèi)部服務(wù)的
$.each()           //作為靜態(tài)方法存在 可以迭代任何集合

我們要寫兩個(gè)方法嘛?看看 jQuery 怎么做的?

jQuery.prototype = {
    each: function( callback, args ) {
        return jQuery.each( this, callback, args );
    }
}

實(shí)例方法取于靜態(tài)方法,這里是靜態(tài)與實(shí)例方法共享設(shè)計(jì),靜態(tài)方法掛在jQuery構(gòu)造器上,原型方法經(jīng)過下面的兩句代碼就掛載到 init 的原型上了,也就是對象的實(shí)例方法上了。

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

那么剩下的問題就是怎么實(shí)現(xiàn)靜態(tài)方法 jQuery.each

這個(gè)靜態(tài)方法是在

jQuery.extend({
   each: function( obj, callback ) {
     var length, i = 0;
        if ( isArrayLike( obj ) ) {
            length = obj.length;
            for ( ; i < length; i++ ) {
                if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                    break;
                }
            }
        } else {
            for ( i in obj ) {
                if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
                    break;
                }
            }
        }
        return obj;
    }
})

我們實(shí)現(xiàn) each,代碼如下:

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

jQuery.fn = jQuery.prototype = {
  constructor: jQuery,
  length:0,
  get: function( num ) {
    return this[ num ];
  },
  each: function( callback ) {
    return jQuery.each( this, callback );
  }
}

init = jQuery.fn.init = function( selector ) {
  var elem = document.querySelectorAll(selector);
  this.length = elem.length;
  this[0] = elem[0];
  for (i = 0; i < elem.length; i++) {
    this[i] = elem[i];
  }
  this.context = document;
  this.selector = selector;
  return this;
}

init.prototype = jQuery.fn;


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

  //只有一個(gè)參數(shù),就是對jQuery自身的擴(kuò)展處理
  //extend,fn.extend
  if (i === length) {
      target = this; //調(diào)用的上下文對象jQuery/或者實(shí)例
      i--;
  }
  for (; i < length; i++) {
      //從i開始取參數(shù),不為空開始遍歷
      if ((options = arguments[i]) != null) {
          for (name in options) {
              copy = options[name];
              //覆蓋拷貝
              target[name] = copy;
          }
      }
  }
  return target;
}

jQuery.extend( {
  each: function( obj, callback ) {
    var length, i = 0;
    for ( i in obj ) {
      if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
        break;
      }
    }
    return obj;
  }
});
插件接口的設(shè)計(jì)

既然出現(xiàn) extend 了,我們就先實(shí)現(xiàn)第四個(gè)小目標(biāo) 擴(kuò)展實(shí)例方法 tabs

jQuery 中

jQuery.extend = jQuery.fn.extend = function() {
}

雖然指向了同一個(gè)函數(shù),但是它們的 this 指向是不同。

fn 與 jQuery 其實(shí)是2個(gè)不同的對象,在之前有講解:jQuery.extend 調(diào)用的時(shí)候,this是指向 jQuery 對象的( jQuery 是函數(shù),也是對象!),所以這里擴(kuò)展在 jQuery 上。而jQuery.fn.extend 調(diào)用的時(shí)候,this 指向 fn 對象,jQuery.fn 和 jQuery.prototype指向同一對象,擴(kuò)展 fn 就是擴(kuò)展 jQuery.prototype 原型對象。這里增加的是原型方法,也就是對象方法了。所以jQuery的API中提供了以上2個(gè)擴(kuò)展函數(shù)。

我們這樣擴(kuò)展實(shí)例方法即可。

jQuery.fn.extend({
  tabs: function() {
    console.log("擴(kuò)展實(shí)例方法:tabs");
  }
});

jQuery 抽出了所有可復(fù)用的特性,分離出單一模塊,通過組合的用法,不管在設(shè)計(jì)思路與實(shí)現(xiàn)手法上 jQuery 都是非常高明的。因?yàn)?jQuery 的設(shè)計(jì)中最喜歡的做的一件事,就是抽出共同的特性使之模塊化,當(dāng)然也是更貼近 S.O.L.I.D 五大原則的單一職責(zé)SRP了,遵守單一職責(zé)的好處是可以讓我們很容易地來維護(hù)這個(gè)對象,比如,當(dāng)一個(gè)對象封裝了很多職責(zé)的時(shí)候,一旦一個(gè)職責(zé)需要修改,勢必會影響該對象的其它職責(zé)代碼。通過解耦可以讓每個(gè)職責(zé)更加有彈性地變化。

方法鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)

通過簡單擴(kuò)展原型方法并通過 return this 的形式來實(shí)現(xiàn)跨瀏覽器的鏈?zhǔn)秸{(diào)用。
所以我們?nèi)绻枰準(zhǔn)降奶幚恚恍枰诜椒▋?nèi)部返回當(dāng)前的這個(gè)實(shí)例對象 this 就可以了,因?yàn)榉祷禺?dāng)前實(shí)例的 this,從而又可以訪問自己的原型了,這樣的就節(jié)省代碼量,提高代碼的效率,代碼看起來更優(yōu)雅。

詳細(xì)解說請點(diǎn)擊 方法鏈?zhǔn)秸{(diào)用的實(shí)現(xiàn)

最終的代碼演示

參考

文章的很多內(nèi)容參考的慕課網(wǎng)的 jQuery源碼解析 系列,感興趣的小伙伴,可以看一下整個(gè)系列。

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/85035.html

相關(guān)文章

  • 學(xué)Java編程需要注意的地方

    摘要:學(xué)編程真的不是一件容易的事不管你多喜歡或是多會編程,在學(xué)習(xí)和解決問題上總會碰到障礙。熟練掌握核心內(nèi)容,特別是和多線程初步具備面向?qū)ο笤O(shè)計(jì)和編程的能力掌握基本的優(yōu)化策略。   學(xué)Java編程真的不是一件容易的事,不管你多喜歡或是多會Java編程,在學(xué)習(xí)和解決問題上總會碰到障礙。工作的時(shí)間越久就越能明白這個(gè)道理。不過這倒是一個(gè)讓人進(jìn)步的機(jī)會,因?yàn)槟阋恢辈粩嗟膶W(xué)習(xí)才能很好的解決你面前的難題...

    leanxi 評論0 收藏0
  • JavaScript入門的5條建議

    摘要:你是否已經(jīng)初步掌握了和,但完全不知道從何入手如果是,那么這篇文章一定會對你有所幫助,這里總結(jié)了條建議,幫助初學(xué)者總結(jié)學(xué)習(xí)方法,提高學(xué)習(xí)效率。這樣的結(jié)果就是,個(gè)小時(shí)最多只利用了個(gè)小時(shí)。 你是否已經(jīng)初步掌握了html和css,但完全不知道從何入手JavaScript?如果是,那么這篇文章一定會對你有所幫助,這里總結(jié)了5條建議,幫助JavaScript初學(xué)者總結(jié)學(xué)習(xí)方法,提高學(xué)習(xí)效率。 一、...

    李昌杰 評論0 收藏0
  • jQuery筆記總結(jié)篇

    摘要:希望在做所有事情之前,操作文檔。不受層級限制子選擇器在給定的父元素下匹配所有子元素。相鄰選擇器匹配所有緊接在元素后的元素。判斷當(dāng)前對象中的某個(gè)元素是否包含指定類名,包含返回,不包含返回下標(biāo)過濾器精確選出指定下標(biāo)元素獲取第個(gè)元素。 原文鏈接 http://blog.poetries.top/2016... 首先,來了解一下jQuery學(xué)習(xí)的整體思路 showImg(https://seg...

    NoraXie 評論0 收藏0
  • jQuery(一)-- 初步了解

    摘要:一初步了解介紹由創(chuàng)建于年一月的開源項(xiàng)目,憑借著跨平臺的兼容性,簡潔的語法,極大的簡化了人員遍歷文檔,操作,處理事件,執(zhí)行動畫,和開發(fā)的操作。只建立一個(gè)名為的對象。對發(fā)生在同一個(gè)對象上的一組動作,可以直接連寫無需重復(fù)獲取對象。 jQuery(一)-- 初步了解 jQuery介紹 由John Resig創(chuàng)建于2006年一月的開源項(xiàng)目,jQuery憑借著跨平臺的兼容性,簡潔的語法,極大的簡...

    quietin 評論0 收藏0
  • 簡聊初步嘗試服務(wù)端渲染的一些感想

    摘要:多多少少有些不開心的事覺得精力沒有被投入在重點(diǎn)上創(chuàng)業(yè)公司遇到問題變成盲人摸象也許正常吧不過最近這段時(shí)間因?yàn)榉?wù)端的策略調(diào)整我開始做一些服務(wù)端渲染主要的站點(diǎn)是簡聊的登錄頁面整體從切換到了以及做了一些整體項(xiàng)目結(jié)構(gòu)統(tǒng)一的工作或者說一些思考我估計(jì)這 多多少少有些不開心的事, 覺得精力沒有被投入在重點(diǎn)上創(chuàng)業(yè)公司遇到問題變成盲人摸象也許正常吧不過最近這段時(shí)間因?yàn)榉?wù)端的策略調(diào)整, 我開始做一些服務(wù)...

    Clect 評論0 收藏0

發(fā)表評論

0條評論

張巨偉

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<