作者:兩儀的部落格
網址:http://segmentfault.com/a/1190000002998114
點選“閱讀原文”可檢視本文網頁版
什麼是偽陣列
能透過Array.prototype.slice轉換為真正的陣列的帶有length屬性的物件。
這種物件有很多,比較特別的是arguments物件,還有像呼叫getElementsByTagName,document.childNodes之類的,它們都傳回NodeList物件都屬於偽陣列。
我們可以透過Array.prototype.slice.call(fakeArray)將偽陣列轉變為真正的Array物件。
來看個示例:
var fakeArray01 = {0:’a’,1:’b’,length:2};//這是一個標準的偽陣列物件
var arr01 = Array.prototype.slice.call(fakeArray01);
alert(arr01[0]);//a
var arr02 = [].slice.call(fakeArray01);
alert(arr02[0]);//a
slice 可以用來獲取陣列片段,它傳回新陣列,不會修改原陣列。
示例中可以看到fakeArray被成功的轉換成了Array物件。也許大家對Array.prototype.slice.call這種寫法比較陌生,其實我們也可以透過[].slice.call這種形式實現同樣的效果,那為什麼我們要透過prototype的形式實現呢,答案是以prototype的形式執行程式效率更高,同樣程式碼也更加優美。
偽陣列的實現
我們來看一些特殊的用例
var fakeArray01 = {a:’a’,b:’b’,length:2};//沒有length下標對應的值
var arr01 = Array.prototype.slice.call(fakeArray01);
alert(arr01[0]);//undefined
var fakeArray02 = {0:’a’,1:’b’,length:’num’};//length不是數值
var arr02 = Array.prototype.slice.call(fakeArray02);
alert(arr02[1]);//undefined
同樣fakeArray01和fakeArray02被轉換成了真正的陣列,但是陣列中的值都為undefined
檢視 V8 引擎 array.js 的原始碼,可以將 slice 的內部實現簡化為:
function slice(start, end) {
var len = ToUint32(this.length), result = [];
for(var i = start; i < end; i++) {
result.push(this[i]);
}
return result;
}
可以看出,slice 並不需要 this 為 array 型別,只需要有 length 屬性即可。並且 length 屬性可以不為 number 型別,當不能轉換為數值時,ToUnit32(this.length) 傳回 0.
根據以上結論可以得出:fakeArray01被轉換成了lenth為2的陣列,其值都被初始化為undefined,fakeArray02被轉換成了length為0的陣列,自然訪問下標為1的元素傳回undefined
IE的問題
針對於標準瀏覽器slice實現已經可以解釋所有的問題,但是IE在處理NodeList時出現了問題。IE中無法將NodeList轉換為真正的陣列,會出錯。這又是為什麼呢?嚴格說,在IE內部定義了一個抽象類Arraioid,Array和Arguments都繼承與此,所以可以用slice。但DOM物件是透過COM接入到JScript的,slice檢測的時候失效。
Jquery與偽陣列
Jquery內部大量運用了偽陣列。可以說整個Jquery物件,都是構建在偽陣列的基礎之上的,好讓我們來看一些Jquery的實際運用:
fakeArray