亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

從零開(kāi)始,DIY一個(gè)jQuery(1)

オリジナル 2016-11-11 17:25:12 1618
サマリー:從本篇開(kāi)始會(huì)陪大家一起從零開(kāi)始走一遍 jQuery 的奇妙旅途,在整個(gè)系列的實(shí)踐中,我們會(huì)把 jQuery 的主要功能模塊都了解和實(shí)現(xiàn)一遍。這會(huì)是一段很長(zhǎng)的歷程,但也會(huì)很有意思 —— 作為前端領(lǐng)域的經(jīng)典之作,jQuery 里有著太多奇思妙想,如果能夠深入理解它,對(duì)于我們穩(wěn)固js基礎(chǔ)、提升前端大法技能來(lái)說(shuō)大有裨益。1. 免 new 實(shí)現(xiàn)我們?cè)谑褂煤芏嗖寮臅r(shí)候,都需要使用 new XXX() 的寫法

從本篇開(kāi)始會(huì)陪大家一起從零開(kāi)始走一遍 jQuery 的奇妙旅途,在整個(gè)系列的實(shí)踐中,我們會(huì)把 jQuery 的主要功能模塊都了解和實(shí)現(xiàn)一遍。

這會(huì)是一段很長(zhǎng)的歷程,但也會(huì)很有意思 —— 作為前端領(lǐng)域的經(jīng)典之作,jQuery 里有著太多奇思妙想,如果能夠深入理解它,對(duì)于我們穩(wěn)固js基礎(chǔ)、提升前端大法技能來(lái)說(shuō)大有裨益。

1. 免 new 實(shí)現(xiàn)

我們?cè)谑褂煤芏嗖寮臅r(shí)候,都需要使用 new XXX() 的寫法來(lái)實(shí)例化一個(gè)引用:

var list = new Slip(document.getElementById('slip'), {
  //options
});

jQuery 同樣作為一個(gè)面向?qū)ο蟮墓ぞ邘?kù),在我們創(chuàng)建一個(gè)實(shí)例時(shí)卻無(wú)需使用 new 語(yǔ)法,節(jié)省了一些代碼量:

var $div = $('div');
//不需要如下寫法:
//var $div = new $('div');

這種便捷的形式依賴了工廠模式,其實(shí)現(xiàn)非常簡(jiǎn)單,把 new 封裝在庫(kù)內(nèi)即可,讓每次調(diào)用 jQuery() 時(shí)自行在內(nèi)部進(jìn)行一次實(shí)例化:

(function() {
    var _jQuery = window.jQuery,
        _$ = window.$;
    var version = "0.0.1",
        jQuery = function(selector) {
            console.log(document.querySelector(selector))
        };
    jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };
    window.$ = window.jQuery = function(selector) {
        return new jQuery(selector);  //notice here~
    };
})();

留意這里我們走的 IIFE 形式,讓 jQuery 代碼庫(kù)形成自己的作用域,避免污染外部變量。

于是乎以上就是咱寫的第一個(gè) JQ 雛形,簡(jiǎn)單跑一下:

<div></div>
<script>
    var $div = $('div');  //<div></div>
    console.log($div.jquery);  //0.0.1
</script>

別忘了后續(xù)我們還希望能通過(guò) $.extend / $.fn.extend 來(lái)擴(kuò)展 JQ 的靜態(tài)方法和原型方法,我們把出口方法抽出來(lái)增加這個(gè) extend 的API:

  function Factory(selector){  //抽出構(gòu)造函數(shù)
        return new jQuery(selector);
    }
    Factory.fn = jQuery.prototype;
    Factory.extend = Factory.fn.extend = function(){
        console.log(this)
    };
    window.$ = window.jQuery = Factory;

這樣我們也能直接通過(guò) $.fn.jquery 來(lái)獲取當(dāng)前 JQ 版本號(hào)了。

如果希望可以通過(guò) $.prototype 直接訪問(wèn) jQuery 的原型對(duì)象,再修改下這句代碼即可:

Factory.prototype = Factory.fn = jQuery.prototype;

2. 寫法優(yōu)化

事實(shí)上我們不太喜歡再寫多一個(gè)冗余的 Factory 構(gòu)造函數(shù)來(lái)作為 window.jQuery 的引用,也不喜歡(在模塊內(nèi)部)使用 Factory.extend() 來(lái)擴(kuò)展 JQ,它聽(tīng)起來(lái)和 JQ 沒(méi)有半毛錢關(guān)系。

如果可以,直接把 jQuery 方法作為接口輸出,且在模塊內(nèi)部能以 jQuery.extend()  的形式來(lái)調(diào)用擴(kuò)展接口,這樣的形式更佳。

也就是說(shuō)我們希望代碼應(yīng)該是這樣寫的:

    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };
    window.jQuery = window.$ = jQuery;

“直接把 jQuery 方法作為接口輸出”意味著我們要把工廠模式挪入 jQuery 方法中,顯然我們不能這樣改:

    var version = "0.0.1",
        jQuery = function (selector) {
            return new jQuery(selector);
        };

這樣死循環(huán)了,調(diào)用棧會(huì)直接爆掉~(yú)

于是我們可以抽出一個(gè) init 方法來(lái)做初始化處理(比如簡(jiǎn)單地注入檢索到的元素到JQ對(duì)象中),把 jQuery 方法中的內(nèi)容更改為 return new init(selector) 就行了。

保證兩個(gè)前提:

1. this 指向 jQuery 上下文

2. 其原型指向 jQuery 的原型

第一點(diǎn)很好理解,方便我們直接在 init 方法中通過(guò)對(duì) this 的操作來(lái)處理 JQ 實(shí)例上下文,如:

  //注入元素到 JQ 實(shí)例對(duì)象中
  this[0] = elem;
  this.length = 1;

針對(duì)這點(diǎn),我們不妨把 init 作為 jQuery.prototype 的屬性方法來(lái)實(shí)現(xiàn):

  var version = "0.0.1",
        jQuery = function(selector) {
            return new jQuery.fn.init()  //修改點(diǎn)1
        };
    //方便我們使用 jQuery.fn 來(lái)引用 jQuery 原型對(duì)象
    jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };
     //修改點(diǎn)2 —— init 作為原型方法,確保 this 指向正確
    jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };
    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };
    window.$ = window.jQuery = jQuery;

然而這時(shí)候存在一個(gè)問(wèn)題 —— JQ實(shí)例對(duì)象無(wú)法訪問(wèn)原型屬性/方法:

    var $div = $('div');
    console.log($div.jquery);  //undefined

原因很簡(jiǎn)單——我們還未實(shí)現(xiàn)上述提及的第二個(gè)前提——“init 原型指向 jQuery 的原型”

在 js 中,實(shí)例的內(nèi)部原型(__proto__)總是指向其構(gòu)造函數(shù)的原型(prototype),而經(jīng)過(guò)我們這番修改,JQ實(shí)例的構(gòu)造函數(shù)已經(jīng)變成了 jQuery.fn.init ,而其原型并非指向 jQuery 的原型,這導(dǎo)致 JQ 實(shí)例無(wú)法順其原型鏈爬取到 jQuery.prototype。

要實(shí)現(xiàn)這個(gè)條件,只需要做小小改動(dòng)——把 jQuery.fn.init 的原型指向 jQuery 的原型(jQuery.prototype / jQuery.fn)即可:

var init = jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };
    init.prototype = jQuery.fn;  //修改點(diǎn)

這里貼下完整代碼:

var version = "0.0.1",
        jQuery = function(selector) {
            return new jQuery.fn.init(selector)
        };

    jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery
    };

    var init = jQuery.fn.init = function( selector, context, root ) {
        if ( !selector ) {
            return;
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
        }
    };

    init.prototype = jQuery.fn;

    jQuery.extend = jQuery.fn.extend = function(){
        console.log(this)
    };

    window.$ = window.jQuery = jQuery;

3. 鏈?zhǔn)綄懛▽?shí)現(xiàn)

JQ 里一個(gè)很大的亮點(diǎn)是,它支持鏈?zhǔn)綄懛?,調(diào)用起來(lái)非常方便:

$('div').removeClass('hide').css('width', '100px')

其實(shí)現(xiàn)其實(shí)非常簡(jiǎn)單 —— 確保每個(gè)調(diào)用的方法尾部均返回自身即可,這里我們新增兩個(gè)實(shí)例方法做示例:

  jQuery.fn = jQuery.prototype = {
        jquery: version,
        constructor: jQuery,
        setBackground: function(){
            this[0].style.background = 'yellow';
            return this  //返回自身引用
        },
        setColor: function(){
            this[0].style.color = 'blue';
            return this  //返回自身引用
        }
    };
    var init = jQuery.fn.init = function( selector ) {
        if ( !selector ) {
            return this; 
        } else {
            var elem = document.querySelector( selector );
            if ( elem ) {
                this[0] = elem;
                this.length = 1;
            }
            return this;
        }
    };

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

<div>hello world</div>
<script>
    var $div = $('div');
    $div.setBackground().setColor();
</script>

效果如下,杠杠的:

QQ截圖20161111172524.png

4. 沖突處理

存在某些情況,用戶可能并不想拿 window.$ 甚至 window.jQuery 來(lái)引用 JQ 接口,或者已經(jīng)有其它庫(kù)使用了 window.$ 這個(gè)變量,如果我們粗暴地改變其引用肯定是不合理的。

so 我們來(lái)實(shí)現(xiàn) JQ 中沖突處理的靜態(tài)接口 jQuery.noConflict,這意味著在代碼段開(kāi)始時(shí),就得先保存下當(dāng)前 window.$ 和 window.jQuery 兩個(gè)變量:

(function(){
    var _jQuery = window.jQuery,
        _$ = window.$;
    //var version = "0.0.1"......
})()

然后是實(shí)現(xiàn) noConflict 方法,退耕還林,把保存的變量吐回去即可:

(function(){
    var _jQuery = window.jQuery,
        _$ = window.$;
    //var version = "0.0.1"......
    jQuery.noConflict = function( deep ) {
        //確保window.$沒(méi)有再次被改寫
        if ( window.$ === jQuery ) {
            window.$ = _$;
        }
        //確保window.jQuery沒(méi)有再次被改寫
        if ( deep && window.jQuery === jQuery ) {
            window.jQuery = _jQuery;
        }
        return jQuery;  //返回 jQuery 接口引用
    };
    window.jQuery = window.$ = jQuery;
})();

deep 參數(shù)類型為 Boolean,若為真,表示要求連window.jQuery 變量都需要吐回去。

留意在尾部我們返回了 jQuery 的接口引用,這意味著我們可以以

var $$$ = jQuery.noConflict()

的形式來(lái)把它賦予新的變量。

接著在外部運(yùn)行如下代碼:

<head>
    <meta charset="UTF-8">
    <title>DIY A JQ</title>
    <script>
        $ = 'old $';
        jQuery = 'old JQ'
    </script>
    <script src="jQuery.js"></script>
</head>
<body>
<div>hello world</div>
<script>
    var $div = $('div');
    $div.setBackground().setColor();
    var $$$ = $.noConflict(true);
    console.log($); 
    console.log(jQuery); 
    console.log($$$); 
</script>



手記を発表する

人気のある見(jiàn)出し語(yǔ)