【JS】723- 前端如何優(yōu)雅的處理類數(shù)組對象?

一、背景介紹
Leo 部門最近來了位前端實習(xí)生 Robin,作為師傅,Leo 認真的為 Robin 介紹了公司業(yè)務(wù)、部門工作等情況,還有前端的新人學(xué)習(xí)地圖。
接下來 Robin 開始一周愉快的學(xué)習(xí)啦~?
一周后,Leo 為 Robin 同學(xué)布置了學(xué)習(xí)作業(yè),開發(fā)一個【人員搜索選擇】的頁面,效果大致如下:

Robin 看完這個效果圖后,一臉得意的樣子,這確實不難呀~
過幾天后,Robin 帶著自己寫的代碼,給 Leo 展示了她的代碼,并疑惑的問到:

她將這個“數(shù)組”輸出到控制臺:

Leo 看了看代碼:
getUserList(){
???const?memberList?=?$('#MemberList?li');
???memberList.map(item?=>?{?console.log(item)?});
???console.log(memberList);
}
Leo 又問到:

Robin 一臉疑惑,然后 Leo 再原來代碼上,加了個 Array.from?方法如下:
getUserList(){
????const?memberList?=?Array.from($('#MemberList?li'));
????memberList.map(item?=>?{
????????console.log(item)
????})
????console.log(memberList)
}
然后重新執(zhí)行代碼,輸出下面結(jié)果:

Leo 輸出的結(jié)果,跟 Robin 說到:

Robin 滿臉期待望著師傅,對類數(shù)組對象更加充滿期待。
二、類數(shù)組對象介紹
2.1 概念介紹
所謂 類型化數(shù)組對象(簡稱類數(shù)組對象)?是一種類似數(shù)組的對象,它提供了一種用于訪問原始二進制數(shù)據(jù)的機制。JavaScript引擎會做一些內(nèi)部優(yōu)化,以便對數(shù)組的操作可以很快。然而,隨著Web應(yīng)用程序變得越來越強大,尤其一些新增加的功能例如:音頻視頻編輯,訪問WebSockets的原始數(shù)據(jù)等,很明顯有些時候如果使用JavaScript代碼可以快速方便地通過類型化數(shù)組來操作原始的二進制數(shù)據(jù)將會非常有幫助。—— 《MDN 類型化數(shù)組》
那么什么樣的數(shù)組我們可以歸類到類型化數(shù)組中?其實比較簡單,和數(shù)組結(jié)構(gòu)類似,擁有 length 屬性,可以通過索引來訪問或設(shè)置里面的元素,但是不能使用數(shù)組的方法,就可以歸類為類型化數(shù)組。舉個例子?:
const?arrLike?=?{
??0:?'name',
??1:?'age',
??2:?'job',
??length:?3
}
2.2 常見類數(shù)組對象
arguments對象;
function?f()?{
??return?arguments;
}
f(1,2,3)
//?Arguments(3)?[1,?2,?3,?callee:??,?Symbol(Symbol.iterator):??]
NodeList(比如document.getElementsByClassName('a')得到的結(jié)果;
document.getElementsByTagName('img')
//?HTMLCollection(3)?[img,?img,?img]
typedArray(比如Int32Array);
const?typedArray?=?new?Uint8Array([1,?2,?3,?4])
//?Uint8Array(4)?[1,?2,?3,?4]
另外使用 jQuery 獲取元素,會被 jQuery 做特殊處理成為 init 類型:
$('img')
//?init(3)?[img,?img,?img,?prevObject:?init(1),?context:?document,?selector:?"img"]
當然還有一些不常見的類數(shù)組對象,比如“Storage API 返回的結(jié)果”,這里就不一一列出。
三、類數(shù)組對象屬性
下面通過 Robin 代碼作為示例,介紹類數(shù)組對象的屬性:
const?memberList?=?$('#MemberList?li');
3.1 讀寫
//?讀取
memberList[0];
//?Node:?...
//?寫入
memberList[0]?=?document.createElement("div")
memberList[0];
//??Node:?...
3.2 長度
memberList.length;?
//?10
3.3 遍歷
for?(let?i?=?0;i?????console.log(memberList[i]);
}
/*
?Node:?...
?Node:?...
??...?共10個,省略其他
*/
memberList.map(item?=>?console.log(item));
/*
?0
??...?共10個,省略其他
*/
但如果是 HTMLCollection 就不能使用 map 咯:
const?img?=?document.getElementsByTagName("img");
img.map(item?=>?console.log(item));
//?Uncaught?TypeError:?img.map?is?not?a?function
四、類數(shù)組對象處理
Leo 看了看 Robin 處理這個列表的代碼:
getUserList(){
????const?memberList?=?$('#MemberList?li');
????const?result?=?{
????????text:?[],
????????dom?:?[],
????};
????memberList.map(item?=>?{
????????item?=?memberList[item]
????????//?判斷當前節(jié)點是否有?checked?類名
????})
????console.log(result)
????this.showToast(`選中成員:${result.text}`);
}
很明顯,Robin 并沒有對 jQuery 獲取到的 memberList 做處理,直接使用,通過索引來獲取對應(yīng)值。Leo 繼續(xù)和 Robin 介紹到:
4.1 Array.from
使用 Array.from?來將類數(shù)組對象轉(zhuǎn)為數(shù)組對象,操作起來非常簡單:
getUserList(){
????const?memberList?=?Array.from($('#MemberList?li'));
????//?省略其他代碼
}
其語法如下:
Array.from(arrayLike[,?mapFn[,?thisArg]])
參數(shù):
arrayLike想要轉(zhuǎn)換成數(shù)組的偽數(shù)組對象或可迭代對象。mapFn可選如果指定了該參數(shù),新數(shù)組中的每個元素會執(zhí)行該回調(diào)函數(shù)。thisArg可選可選參數(shù),執(zhí)行回調(diào)函數(shù)mapFn時this對象。
返回值:一個新的數(shù)組實例。
更多 Array.from ?介紹可以查看文檔。
4.2 Array.prototype.slice.call()
slice() 方法返回一個新的數(shù)組對象,這一對象是一個由 begin 和 end 決定的原數(shù)組的淺拷貝(包括 begin,不包括end)。原始數(shù)組不會被改變。
實現(xiàn)代碼:
getUserList(){
????const?memberList?=?Array.prototype.slice.call($('#MemberList?li'));
????//?省略其他代碼
}
更多 Array.prototype.slice 介紹可以查看文檔。
4.3 ES6展開運算符
展開語法(Spread syntax), 可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時, 將數(shù)組表達式或者string在語法層面展開;還可以在構(gòu)造字面量對象時, 將對象表達式按key-value的方式展開。
實現(xiàn)代碼:
getUserList(){
????const?memberList?=?[...document.getElementsByTagName("li")];
????//?省略其他代碼
}
更多 ES6展開運算符 介紹可以查看文檔。
4.4 利用concat+apply
getUserList(){
????const?memberList?=?Array.prototype.concat.apply([],?$('#MemberList?li'));
????//?省略其他代碼
}
五、案例小結(jié)
Leo 介紹完這些知識后,Robin 又優(yōu)化了下自己的代碼,涉及到類數(shù)組對象操作的核心 js 代碼如下:
class?SelectMember?{
????constructor(){
????????this.MockUsers?=?window.MockUsers;
????????this.init();
????}
????init(){
????????this.initMemberList('#MemberList',?this.MockUsers);
????????this.initBindEvent();
????}
???//?...?省略部分代碼,保留核心代碼
????submitSelect(){
????????const?memberList?=?Array.from($('#MemberList?li'));
????????const?result?=?{
????????????text:?[],
????????????dom?:?[],
????????};
????????memberList.map(item?=>?{
????????????const?hasClass?=?$(item).children('.round-checkbox').children('span').hasClass(this.selectClassName);
????????????if(hasClass){
????????????????result.text.push($(item).children('.user-data').children('h4').text());
????????????????result.dom.push(item);
????????????}
????????})
????????this.showToast(`選中成員:${result.text}`);
????}
}
let?newMember?=?new?SelectMember();
很明顯,使用正確方式來處理類數(shù)組對象,不僅能使我們代碼更加少,減少轉(zhuǎn)換處理,還能提高代碼質(zhì)量。
整個項目的完整代碼,可以在我的 github 查看:
https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Demo/10.Learn-Array-Liked-Objects/index.html
六、總結(jié)
本文我們通過一個實際場景,詳細介紹了類數(shù)組對象在實際開發(fā)中的使用,對于常見的類數(shù)組對象,我們還介紹了處理方式,能很大程度減少我們處理類數(shù)組對象的操作,將類數(shù)組統(tǒng)一轉(zhuǎn)成數(shù)組,更加方便對數(shù)據(jù)的操作。希望看完本文的你,以后再遇到類數(shù)組對象,不會再一臉懵逼咯~~~
- END -
回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
點擊“閱讀原文”查看80+篇原創(chuàng)文章
