類比jQuery實作類別數組對象

來源:互聯網
上載者:User

jQuery最令人驚讚的東西就是那個類數組對象,亦即俗話中的jQuery對象。注意,在jQuery類庫中,jQuery是作為命名空間而存在的函數。它擁有許多靜態方法,由於函數也是對象,對象就有原型,而jQuery的原型方法是異常頻繁地調用它的靜態方法。它的第一個原型方法叫init,這是受Prototype類庫影響的結果。init方法也可以作為建構函式使用,它new出來的執行個體就是jQuery對象。在jQuery命名空間的原型中,也有一個叫jquery的屬性,起著其他類庫版本號碼的作用,由於名字特殊,也用作識別是否為jQuery對象的作用。換言之,這裡有三處可以集中添加方法與屬性的地方,jQuery命名空間,用於添加靜態方法,jQuery的原型,用於添加原型方法,只要依賴extend來添加,jQuery.prototype.init執行個體(jQuery對象),它的屬性比較少,只有length,prevObject,selector與context。至於jQuery.prototype.init執行個體的原型,都是往jQuery.prototype裡搬。接著下來,我們沿著jQuery的思路,實現一個類數組對象吧。

首先我們要定義一個類,我稱之為eve,它其實是個工廠。

      var eve = function(){        return new eve.prototype.init(arguments);      }

如果是數組,傳入一個數字,會返回與數字值相等長度的空數組,如果傳入幾個參數,這幾個參數會成為返回數組的元素,如果什麼都不會就返回空數組(這種方式一定要使用new操作符)。嘛,由於我們的類數組,用不著百分之一百類比,就按其方式二產生類數組。

類數組要求擁有length屬性與索引,length容易辦,但索引我們要一個個賦值,如果在最前面添加新的元素時,就要重排索引,這很要命,效率很低。不過我們可以用數組的push方法實現。

        setArray : function(els) {          this.length = 0;//設定length以及重排索引          Array.prototype.push.apply(this, els);          return this;        },

為了確保用得了setArray方法,我們必須保證els為數組,因為裡面用到 Array.prototype.push.apply(this, els),看到Array沒有?至於push方法,簡單,保證調用者擁有length屬性就行了。 下面的makeArray,就是將非數組對象轉換為數組對象,就算為空白,也要用[]包裹起來。

        makeArray : function( arr ) {//把傳入參數變成數組          var ret = [];          if( arr != null ){    var i = arr.length;            //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷            if( i == null || arr.split || arr.setInterval || arr.call ){              ret[0] = arr;            }else{              try{                ret = Array.prototype.slice.call(arr)              }catch(e){                while( i ) ret[--i] = arr[i];//Clone數組              }            }          }          return ret;        },

有了這兩個方法,我們就可以寫我們的構造方法了。

        init : function(obj){          this.setArray(this.makeArray(obj));          return this;        },

但僅僅是這樣無法new執行個體的,因為這裡的this出了些狀況。前兩個this為eve的執行個體,第三個返回的this為eve.prototype.init的執行個體。

<br /> var eve = function(){<br /> return new eve.prototype.init(arguments);<br /> }<br /> eve.prototype = {<br /> init : function(obj){<br /> this.setArray(this.makeArray(obj));<br /> return this;<br /> },<br /> setArray : function(elems) {<br /> this.length = 0;//設定length以及重排索引<br /> Array.prototype.push.apply(this, elems);<br /> return this;<br /> },<br /> makeArray : function( arr ) {//把傳入參數變成數組<br /> var ret = [];<br /> if( arr != null ){ var i = arr.length;<br /> //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷<br /> if( i == null || arr.split || arr.setInterval || arr.call ){<br /> ret[0] = arr;<br /> }else{<br /> try{<br /> ret = Array.prototype.slice.call(arr)<br /> }catch(e){<br /> while( i ) ret[--i] = arr[i];//Clone數組<br /> }<br /> }<br /> }<br /> return ret;<br /> }<br /> }<br /> try{<br /> var e = new eve(1,2,3);<br /> alert(e);<br /> }catch(err){<br /> alert(err)<br /> }<br />

運行代碼

看到沒有?報錯找不到makeArray方法,換言之eve.prototype.init執行個體無法調用eve的原型方法。如果是jQuery,就是jQuery對象無法調用其jQuery命名空間對象的原型方法。解決方案很簡單,直接把它們兩個的原型重疊起來。為其命名空間對象的原型添加方法就是為其真身的原型添加方法。

  eve.prototype.init.prototype = eve.prototype;

<br /> var eve = function(){<br /> return new eve.prototype.init(arguments);<br /> }<br /> eve.prototype = {<br /> init : function(obj){<br /> this.setArray(this.makeArray(obj));<br /> return this;<br /> },<br /> setArray : function(elems) {<br /> this.length = 0;//設定length以及重排索引<br /> Array.prototype.push.apply(this, elems);<br /> return this;<br /> },<br /> makeArray : function( arr ) {//把傳入參數變成數組<br /> var ret = [];<br /> if( arr != null ){ var i = arr.length;<br /> //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷<br /> if( i == null || arr.split || arr.setInterval || arr.call ){<br /> ret[0] = arr;<br /> }else{<br /> try{<br /> ret = Array.prototype.slice.call(arr)<br /> }catch(e){<br /> while( i ) ret[--i] = arr[i];//Clone數組<br /> }<br /> }<br /> }<br /> return ret;<br /> }<br /> }<br /> eve.prototype.init.prototype = eve.prototype;<br /> try{<br /> var e = new eve(1,2,3);<br /> alert(e);<br /> }catch(err){<br /> alert(err)<br /> }<br />

運行代碼

我們再向它添加一些方法,看到底能模擬到什麼程式。其實上面已經能用索引取值了,即e[1],會彈出2。但toString卻是[object Object],而是不數位序列。在jQuery中有個get()方法,如果不加參數,能取出裡面的數組,我們把它的toString方法賦其eve.prototype就是。

        toString : function(){//返回一個字串          var array = Array.prototype.slice.call( this );          return array.toString();        },        get: function( num ) {          return num === undefined ? Array.prototype.slice.call( this ) : this[ num ];        }

<br /> var eve = function(){<br /> return new eve.prototype.init(arguments);<br /> }<br /> eve.prototype = {<br /> init : function(obj){<br /> this.setArray(this.makeArray(obj));<br /> return this;<br /> },<br /> setArray : function(elems) {<br /> this.length = 0;//設定length以及重排索引<br /> Array.prototype.push.apply(this, elems);<br /> return this;<br /> },<br /> makeArray : function( arr ) {//把傳入參數變成數組<br /> var ret = [];<br /> if( arr != null ){ var i = arr.length;<br /> //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷<br /> if( i == null || arr.split || arr.setInterval || arr.call ){<br /> ret[0] = arr;<br /> }else{<br /> try{<br /> ret = Array.prototype.slice.call(arr)<br /> }catch(e){<br /> while( i ) ret[--i] = arr[i];//Clone數組<br /> }<br /> }<br /> }<br /> return ret;<br /> },<br /> toString : function(){//返回一個字串<br /> var array = Array.prototype.slice.call( this );<br /> return array.toString();<br /> },<br /> valueOf:function(){return Array.prototype.slice.call( this );},<br /> get: function( num ) {<br /> return num === undefined ? Array.prototype.slice.call( this ) : this[ num ];<br /> }<br /> }</p><p> eve.prototype.init.prototype = eve.prototype;<br /> try{<br /> var e = new eve(1,2,3);<br /> alert(e);<br /> alert(e[1]);<br /> alert(e.toString());<br /> alert(e.valueOf());<br /> }catch(err){<br /> alert(err)<br /> }<br />

運行代碼

很好,現在如果不用instanceof與typeof判斷,光憑感覺,是分辯不出它是數組與類數組了。我們再給添加一些數組方法。

        shift :[].shift,        push: [].push,        sort: [].sort,        pop:  [].pop,        splice: [].splice,        concat: [].concat,        slice: [].slice,        constructor:eve,//********************     eve.toString = function(){        return "function Array(){\n    [variant code]\n}"      }

<br /> var eve = function(){<br /> return new eve.prototype.init(arguments);<br /> }<br /> eve.prototype = {<br /> init : function(obj){<br /> this.setArray(this.makeArray(obj));<br /> return this;<br /> },<br /> isArray : function( obj ) {<br /> return Object.prototype.toString.call(obj) === "[object Array]";<br /> },<br /> setArray : function(elems) {<br /> this.length = 0;//設定length以及重排索引<br /> Array.prototype.push.apply(this, elems);<br /> return this;<br /> },<br /> makeArray : function( arr ) {//把傳入參數變成數組<br /> var ret = [];<br /> if( arr != null ){ var i = arr.length;<br /> //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷<br /> if( i == null || arr.split || arr.setInterval || arr.call ){<br /> ret[0] = arr;<br /> }else{<br /> try{<br /> ret = Array.prototype.slice.call(arr)<br /> }catch(e){<br /> while( i ) ret[--i] = arr[i];//Clone數組<br /> }<br /> }<br /> }<br /> return ret;<br /> },</p><p> inArray : function(elem, array) {<br /> for (var i = 0, length = array.length;i </p><p><button type="button" title="runcode4" class="runcode direct">運行代碼</button></p><p>在jQuery中,它還實現eq方法與index方法來取得元素或元素的索引值,還實現javascript1.6的幾個迭代器,each(forEach),map,filter。這樣高度模擬的類數組對象讓jQuery類庫輕易實現了鏈式操作。但顯然,它的filter與map方法比我們在網上找到的實現複雜多了,因為早在jQuery1.0.1中就搞了pushStack方法,用於儲存執行push,pop等破壞性操作前的數組對象。</p><p>可能有人對init方法感覺非常不爽,因為它的兩種this導致了需要用到兩個原型。那是jQuery的init除了有產生jQuery對象的能力,還能充當domReady用。如果是單純的類數組,我們完全可以去掉它,但產生類數組對象時,我們就一定要用new操作符了。</p><pre class="brush:javascript;gutter:false;toolbar:true;collapse:true"> var eve = function(){ this.setArray(this.makeArray(arguments)); return this; } eve.prototype = { isArray : function( obj ) { return Object.prototype.toString.call(obj) === "[object Array]"; }, setArray : function(elems) { this.length = 0;//設定length以及重排索引 Array.prototype.push.apply(this, elems); return this; }, makeArray : function( arr ) {//把傳入參數變成數組 var ret = []; if( arr != null ){ var i = arr.length; //單個元素,但window, string、 function有 'length'的屬性,加其它的判斷 if( i == null || arr.split || arr.setInterval || arr.call ){ ret[0] = arr; }else{ try{ ret = Array.prototype.slice.call(arr) }catch(e){ while( i ) ret[--i] = arr[i];//Clone數組 } } } return ret; }, inArray : function(elem, array) { for (var i = 0, length = array.length;i </pre><p>

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.