這篇文章將要告訴你:
- 什么是類數組對象
- 類數組對象是如何出現的
- 如何避免使用類數組對象的常見問題
- 將類數組對象轉換為普通對象的方法
類數組對象
類數組(Array-like Object)對象是 javascript 中比較冷門的內容,如果不了解的話,會被坑到。
類數組對象 foo 有以下兩個陷阱:
- 有 foo.length 屬性,卻沒有 foo.push() 方法
- 和 for-in 一起使用會有坑
類數組對象的主要使用/產生場景有以下兩種:
- 函數參數 arguments
- querySelectorAll 的結果
下面來分別說一下。
1. 類數組對象——函數參數arguments
function a(){
console.log(arguments); //----------------1. 函數的傳入參數,類數組對象
console.log(arguments.length); //---------2. 傳入參數的個數
console.log(arguments.callee); //---------3. 指向函數本身 a()
console.log(Object.prototype.toString.call(arguments)); //----4. 精確判斷對象類型
}
a(1,2,'aa');
執行結果是:
- [1, 2, "aa", callee: function, Symbol(Symbol.iterator): function]
- 3
- function a(){……(整個函數,此處省略)
- [object Arguments]
從執行結果可以看出,arguments的長度是傳入參數的個數,但它還包括callee這個屬性,指向自身。callee可以用于遞歸,比如在a()內可以繼續通過調用arguments.callee(3,4,5)
實現遞歸。
2. 類數組對象——querySelectorAll的結果
類數組對象應該使用var i = 0; i < a.length;i++
方式遍歷,和for-in一起使用的時候就是個災難啊。
下面我們來看一下災難現場:我們有三個DOM:
<p class="cls-p">第一段</p>
<p class="cls-p">第二段</p>
<p class="cls-p">第三段</p>
然后我們使用querySelectorAll進行選擇,使用for-in遍歷:
var doms = document.querySelectorAll('.cls-p');
console.log(doms.length); // 3
for (var i in doms) {
console.log(i); // 打印doms中屬性的key
}
for-in 的打印結果真是令人目瞪口呆:
除了0,1,2, 其他的是什么鬼?
原理是這樣的,for-in 遍歷的是元素的所有屬性,包括原型鏈中的屬性,所以會打印出keys, entries 這種原型鏈的屬性。
console.log( 'keys' in doms) // true,'keys' 是 doms的屬性
console.log(doms.hasOwnProperty('keys')) // false,‘keys’ 不是doms的自有屬性
在上面的代碼中,我們用了hasOwnProperty()
,這個方法用于判斷當前屬性是否是對象的自有屬性。
那么我們可以用hasOwnProperty()
來填平for-in
的坑么?這是可以的。
for (var i in doms) {
if (doms.hasOwnProperty(i)) {
console.log(i); //打印doms中自有屬性的key
}
};
終于打印出正常的結果了
當然,最好的辦法就是:
- 使用for循環來遍歷類數組對象
- 使用for循環來遍歷類數組對象
- 使用for循環來遍歷類數組對象
將類數組對象轉換為普通對象
文章前面提到過,類數組對象有 foo.length 屬性,卻沒有 foo.push() 方法。通過將類數組對象轉換為普通對象,可以使用push()方法。
方法一:for 循環復制到新對象中
function objToArr() {
// arguments.push[4];
// 如果運行上面的代碼,會報錯 "Uncaught TypeError: Cannot read property '4' of undefined"
var arr = [];
for (var i = 0, len = arguments.length; i < len; i++)
arr[i] = arguments[i];
return arr;
}
var arr = objToArr(1, 2, 3);
arr.push(4);
console.log(arr); // [1,2,3,4],符合預期
方法二:使用Array.prototype.slice方法
原理:slice() 不修改原數組,只會返回一個淺復制了原數組中的元素的一個新數組。
function objToArr() {
// arguments.push[4];
// 如果運行上面的代碼,會報錯 "Uncaught TypeError: Cannot read property '4' of undefined"
var arr = Array.prototype.slice.call(arguments);
return arr;
}
var arr = objToArr(1, 2, 3);
arr.push(4);
console.log(arr); // [1,2,3,4],符合預期
參考文章: