宣告
本系列文章內容全部梳理自以下幾個來源:
- 《JavaScript權威指南》
- MDN web docs
- Github:smyhvae/web
- Github:goddyZhao/Translation/JavaScript
作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基礎上,通過自己的理解,梳理出的知識點,或許有遺漏,或許有些理解是錯誤的,如有發現,歡迎指點下。
PS:梳理的內容以《JavaScript權威指南》這本書中的內容為主,因此接下去跟 JavaScript 語法相關的系列文章基本只介紹 ES5 標準規範的內容、ES6 等這系列梳理完再單獨來講講。
正文-陣列
資料的有序集合稱為陣列。
其實也就是個容器,但與 Java 中的陣列不同的是,JavaScript 裡的陣列不限制元素型別、本身就是個物件,因此不管在使用方面、語法方面、概念上都會一些區別。
那麼本章其實也就是學習 JavaScript 中陣列的用法:
相關術語
稀疏陣列
稀疏陣列就是指不連續索引的陣列,陣列容器中某些索引是空的、無值。相反,正常的連續索引的陣列就是非稀疏陣列,容器中各元素緊密堆放,如:
稀疏陣列:
非稀疏陣列:
陣列內每個元素緊密排列。
多維陣列
JavaScript 不支援真正的多維陣列,但可以用陣列的陣列來近似。
以二維陣列舉例,在 Java 中可直接宣告:
int[][] a = new int[][]{};
但在 JavaScript 中無法定義二維資料,會報語法錯誤:
但由於陣列在 JavaScript 中也是物件,陣列中的元素也可以是陣列,因此可以用陣列的陣列來實現多維陣列:
類陣列物件
理解類陣列物件概念可以將這個詞補充解釋完整,即:類似陣列的物件。
所以,這個概念的主語是物件,而物件如果是通過 [] 來操作它的屬性時,屬性值可以很靈活,不是必須滿足識別符號規定,只要最後能計算出一個字串值即可。因此,當如果定義了某個物件,其屬性值是非負整數:0,1,2,3…,此外再給這個物件定義了一個 length 屬性,那麼此時就可稱這個物件為類陣列物件。
因為對這種物件的操作,跟陣列很類似,而且 Array.prototype 中提供的很多運算元組的方法都可以直接用來操作這些類陣列物件。
陣列屬性-length
每個陣列都有一個 length 屬性,這個屬性是使陣列區別於常規的 JavaScript 物件的關鍵。
需要注意,length 並不是表示陣列的元素個數。
length 表示的是陣列裡最大索引 + 1,因為 JavaScript 分稀疏陣列和非稀疏陣列。
如果是非稀疏陣列,各元素都緊密堆放,那麼此時 length = 最大索引 + 1,能夠表示陣列元素的個數。
但如果是稀疏陣列,由於中間有些索引位置其實是空的,並沒有元素,索引 length = 最大索引 + 1,此時並不表示陣列元素個數。
而且,陣列索引是基於 32 位數值的,所以 length 的取值範圍:
0 ~ 2^32 – 1 => 0 ~ 4294967295
另外,由於 length 屬性預設是可讀可寫的,所以它有一些特殊功能:
- 當新增或刪除陣列元素時,length 會自動更新。
- 並不是所有刪除陣列元素的操作都會讓 length 更新,有些刪除操作只是移除索引裡儲存資料,並不移除陣列這個索引所佔的坑位。
- length 可寫性,當設定 length 比當前陣列長度小的值時,會自動刪除那些索引值大於等於 length 的元素。
- 反過來將 length 設定比當前陣列長度大,會讓陣列變成稀疏陣列,並不會實際新增一些元素進去。
陣列特性
雖然陣列也是物件,但它有一些特性是其他物件所沒有的:
- 當有新元素新增到陣列中時,自動更新 length 屬性
- 設定 length 為一個較小值將截斷陣列
- 繼承了 Array.prototype 一些運算元組的便捷方法
- 類屬性為 “Array”
- 不限制元素型別,一個陣列中可以同時儲存各種型別的資料
建立陣列
陣列的建立,或者說定義陣列,初始化陣列一共有兩種方式:
陣列直接量
var a = []; //空陣列
var b = [1, "2", true, [1+2, {}]]; //不同型別的陣列元素,陣列直接量中甚至可以是表示式
var c = [,,,3]; //省略的索引,讀取時為 undefined
建構函式 Array()
var a = new Array(); //通過建構函式建立陣列
a[0] = 1;
陣列元素的讀寫
跟 Java 語言的陣列讀寫一樣,同是通過 [] 中括號來操作。
但 JavaScript 更靈活,[] 裡可以是任何表示式,不限制於非負整數,如:
a[2] = 0; //常規操作
a["23"] = 0; //自動將 "23" 字串轉成數值型別 23,等效於 a[23]=0
a[-23] = 0; //當[]中不是非負整數時,此操作變成物件的屬性讀寫,因為陣列也是物件
a[5+6]; //[] 中可以是表示式,先計算表示式值後,再操縱陣列,等效於 a[11]
因為陣列也是物件,所以 JavaScript 中的陣列操作不存在越界的場景,當試圖查詢不存在的屬性時,只會返回 undefinded。
陣列元素的新增
新增元素都陣列最簡單的方式是通過 [] 操作符,另外也可藉助 Array.prototype 的一些方法:
var a = []; //a 是空陣列
a[0] = 0; //指定索引位置新增元素
a.push(1); //等效於 a[length] = 1,在陣列末尾新增元素
a.unshift(-1); //在陣列頭部新增元素,原本陣列中的元素依次向後移
a.splice(0, 0, "0", "1"); //插入刪除操作通用的方法,這裡等效於 a.unshift("0", "1");
[] 方式來新增元素的前提是,中括號裡的索引位置原先並沒有元素存在,如果索引位置有元素存在,則該操作變成賦值操作。
如果想在末尾新增元素,直接使用 push 即可;
如果想在開頭新增元素,並讓其他元素自動後移,可用 unshift;
splice 是個通用的方法,可在陣列指定的任何位置新增元素,並讓這位置之後的元素自動後移,同時它也可用來刪除指定位置元素,並讓後續元素自動前移補上被刪除的位置。具體引數含義後面介紹。
陣列元素的刪除
陣列元素的刪除分兩種場景:
- 單純將指定位置的元素刪除,不會觸發高索引元素往下移的填充行為,也不會觸發 length 的長度減少;
- 刪除指定位置的元素,同時後面元素會往前移來填充被刪除元素留下的空白位置,同時 length 會跟隨著減少。
所以,當有涉及陣列元素刪除操作時,需特別注意下,根據自己的需求場景,選擇對應的方法進行操作。
場景1對應的刪除操作
var a = [1,2,3]; //陣列 length = 3;
delete a[1]; //此時陣列:[1,,3],length 仍舊=3
使用 delete 可用於刪除陣列內的元素內容,但並不影響陣列的長度。
場景2對應的刪除操作
var a = [1,2,3,4,5,6,7,8]; //陣列 length = 8;
a.pop(); //陣列:[1,2,3,4,5,6,7] length = 7;
a.shift(); //陣列:[2,3,4,5,6,7] length = 6
a.splice(2, 2); //陣列:[2,3,6,7] length = 4
a.length = 2; //陣列:[2,3] length = 2
除了使用 Array.prototype 內建的方法來刪除元素,對 length 的賦值操作也可以達到刪除末尾的多個元素,超過 length 的索引位置的元素就都被清空掉。
遍歷陣列
for 迴圈語句
陣列的遍歷也是很常見的場景,常規的用法類似 Java 的 for 迴圈語句:
var a = [1,2,,,,6,7,8]; //陣列 length = 8;
for (var i = 0; i < a.length; i++) {
console.log(a[i]);
}
當陣列是稀疏陣列時,那些索引位置沒有元素存在的也仍舊需要遍歷,讀取的值是 undefined,所以需要根據需要做相應判斷處理:
var a = [1,2,,,,6,7,8]; //陣列 length = 8;
for (var i = 0; i < a.length; i++) {
if (!a[i]) continue; //跳過 null,undefined 和不存在的元素
//...
}
for (var i = 0; i < a.length; i++) {
if (a[i] === undefined) continue; //跳過undefined 和不存在的元素
//...
}
for (var i = 0; i < a.length; i++) {
if (!(i in a)) continue; //跳過不存在的元素
//...
}
for-in 迴圈語句
除了使用常規的 for 迴圈外,還可以使用 for-in 的方式:
var a = [1,2,,,,6,7,8]; //陣列 length = 8;
for(var i in a) {
console.log(a[i]);
}
因為陣列實際上也是物件,陣列的索引從物件角度來看,其實也就是屬性,那麼就可以用 for-in 這種方式遍歷屬性,這種方式可以跳過稀疏陣列中那些不存在的元素,但有個缺點,它也會遍歷那些繼承屬性,所以如果需要,可做一些過濾判斷:
var a = [1,2,,,,6,7,8]; //陣列 length = 8;
for(var i in a) {
if (!a.hasOwnProperty(i)) continue; //跳過繼承的屬性
//...
}
注意:雖然 for-in 也可以達到遍歷的效果,但不建議使用在遍歷陣列的場景,因為遍歷順序並不一定按照索引順序。
forEach 方法
上述兩種遍歷方案都需要自行處理很多情況,那麼,有沒有一種方便一點的遍歷方法,有的:forEach
var a = [1,2,,,,6,7,8]; //陣列 length = 8;
a.forEach(function (x) { //x即陣列a中存在的元素
console.log(x);
});
這種方式可以遍歷陣列中存在的元素,不需做額外的判斷處理。如果函式中需要陣列元素的索引資訊、陣列本身的物件引用資訊,此時,可增加額外引數實現:
//x:陣列元素, i:元素的索引, a:陣列的引用
a.forEach(function (x, i, a) {
console.log(a[i] + " = " + x);
});
陣列方法
Array.prototype 中定義了一些很有用的運算元組的函式,可用於作為陣列的方法呼叫,足夠滿足開發中所需的陣列相關的操作需求,列舉一些常見的,更多可參考 API:
join()
將陣列各元素按照指定字串拼接起來後輸出字串:
var a = [1,,2,3];
a.join(); //輸出:1,,2,3 沒有引數預設以逗號,拼接
a.join(" ") //輸出:1 2 3 以空格拼接
不存在的元素也會佔據一個拼接符,所以可以結合其他方法過濾使用,後續介紹。
reverse()
顛倒陣列,將原陣列進行逆序操作:
var a = [1,,2,3];
a.reverse();
a.join(); //輸出:3,2,,1 原陣列被逆序
sort()
將原陣列按照指定規則對元素進行排序,預設以字母表順序排序:
var a = [22,,3,0,1];
a.sort();
a.join(); //輸出:0,1,22,3,,
注意:預設排序行為是將所有元素按照字串形式處理,一個字元一個字元的排序,所有 22 的首字元 2 在 3 前面,排序結果才會是 22 在 3 前面,因為它並不是按照數值的大小來排序。
另外,不存的元素都排在末尾。
所以可以自行指定排序規則,如從小大到排序:
var a = [22,,3,0,1];
a.sort(function (a, b) {
return a - b; //根據順序返回:負數,0,正數
});
a.join(); //輸出:0,1,3,22,
concat()
將引數傳入的數值拼接到陣列末尾,但不是在原陣列上操作,而是會新建一個陣列,此方法的拼接操作不會影響到原陣列內容。
var a = [1,2,3];
a.concat(4,5); //返回 [1,2,3,4,5]
a.concat([4,5]); //返回 [1,2,3,4,5] 因為上述操作沒有影響到原陣列
a.concat([4,5], [6,[7,8]]); //返回 [1,2,3,4,5,6,[7,8]]
注意:如果傳入的引數是陣列,那麼會解析一層陣列,拼接陣列的元素內容,那如果陣列是多維陣列,也只拼接第一維的陣列元素,不會進一步解析陣列。
slice()
擷取原陣列的某個片段,返回一個子陣列,不會在原陣列上操作,返回的是新陣列:
var a = [1,2,3,4,5];
a.slice(0, 3); //返回 [1,2,3] 兩個引數指定起始和終點位置,關係是[),即左包含右不包含
a.slice(3); //返回 [4,5] 只有一個引數時,表示指定起點到末尾
a.slice(1, -1);//返回 [2,3,4] 負數表示倒數第n個元素,
splice()
通用的在陣列的指定位置插入或刪除元素,插入會讓後面的元素自動往後移空出位置,刪除會讓後面的元素自動往前移填補空白,length 會跟隨著變化。
如果有包含刪除操作,那麼刪除的陣列會被返回,否則返回空陣列;
var a = [1,2,3,4,5,6,7,8]; //第一個引數選擇操作的起始位置,第二個引數指定要刪除的個數
a.splice(4); //返回 [5,6,7,8] 原陣列 a:[1,2,3,4]
a.splice(1,2); //返回 [2,3] 原陣列 a:[1,4]
a.splice(1,1); //返回 [4] 原陣列 a:[1]
第一個引數選擇要操作的起始位置,第二個引數指定要刪除的元素個數,如果只有一個引數,那麼就刪除從起始位置到末尾的元素。被刪除的元素會組成新陣列返回,刪除操作是直接在原陣列上進行的。
var a = [1,2,3,4,5]; //第三個引數開始之後的任意引數都會被插入到指定的位置
a.splice(2,0,`a`,`b`); //返回 [] 原陣列 a:[1,2,`a`,`b`,3,4,5]
a.splice(2,2,[1,2],3); //返回 [`a`,`b`] 原陣列 a:[1,2,[1,2],3,3,4,5]
第三個引數開始之後的任意數量的引數都會被插入的第一個引數指定的位置,先進行刪除操作,再進行新增操作。
push() 和 pop()
在陣列末尾新增或移除元素,pop()
時,被移除的元素會返回。
unshift() 和 shift()
在陣列開頭新增或移除元素,都會觸發陣列元素進行遷移行為。
toString()
陣列的 toString()
行為跟 join()
輸出的一致。
forEach()
遍歷陣列內每個元素,每遍歷一個元素,會呼叫一次指定的函式,並將元素的相關資訊通過引數傳入函式內。
map()
原陣列按照指定規則對映到新陣列的操作,跟 forEach()
很類似,遍歷陣列內的每個元素時,都會呼叫一次指定的函式,並將元素相關資訊通過引數傳入函式內。但這個函式需要有一個返回值,用於生產新的陣列的元素。
var a = [1,2,3];
var b = a.map(function (value, index, array) {
return value + index;
}); //b:[1,3,5]
新陣列與原陣列的對映關係為:新陣列元素 = 原陣列元素 + 元素索引;
當有需要對原陣列根據某種規則換算出新陣列時,可用此方法。
filter()
原陣列元素根據某種規則進行過濾操作,過濾完後的元素作為新陣列返回。跟 forEach()
也類似,都一樣是在遍歷每個元素時呼叫指定的方法,並將元素進行傳入。這個方法需要一個 boolean 返回值,用來表示是否可以加入新陣列。
var a = [1,2,3,4,5];
var b = a.filter(function (x) {
return x > 3;
}); //b:[4,5]
用此方法也將稀疏陣列轉成非稀疏陣列,函式內預設返回 true 即可,因為這些方法的遍歷是隻遍歷陣列記憶體在的元素。
every() 和 some()
用於檢測陣列的元素是否滿足指定的條件,這兩個方法都返回 boolean 值。檢測行為就命名錶示的意思,every()
表示陣列裡每個元素都需要滿足條件,最終才會返回 true,一旦某個元素不滿足,後續元素不會再進行檢測,方法直接返回 false。some()
則剛好相反,只要有一個元素滿足條件,後續元素不用檢測,方法直接返回 true。
var a = [1,2,3,4,5];
a.some(function (value, index, array) {
return value > 3; //返回 true,因為存在大於3的元素
});
a.every(function (value, index, array) {
return value > 3; //返回false,因為不是所有元素都大於3
});
reduce() 和 reduceRight()
依次對陣列裡每個元素按照指定規則進行計算,計算之後的結果繼續跟下一個元素按照規則計算,常用於求和,最大值之類的場景。
var a = [1,2,3,4,5];
a.reduce(function (x,y) { //陣列求和
return x + y;
}, 0);
區別上述幾個方法,這個的引數需要有兩個引數,第一引數是函式,用於指定按照某種規則計算,這個函式也需要有兩個引數,以及返回值,它的返回值會和下一個元素再一次傳入該函式中計算。reduce 的第二個引數會和陣列第一個元素被傳入函式內計算,這裡是求和,所以初始值傳 0,求積可以傳1,以此類推。
如果不傳第二個引數,那麼預設以陣列第一個元素的值作為第二個引數的值。
reduceRight 和 reduce 用途,用法一致,唯一的區別,它是從陣列的末尾往前一個個處理元素的。一個左到右處理陣列,一個右到左。
indexOf() 和 lastIndexOf()
在陣列內搜尋指定元素,返回找到的第一個元素的索引位置,沒有找到返回 -1
兩個方法,一個從左往右尋找,一個從右往左尋找。
Array.isArray()
用於判斷某個物件是否是陣列型別。
大家好,我是 dasu,歡迎關注我的公眾號(dasuAndroidTv),公眾號中有我的聯絡方式,歡迎有事沒事來嘮嗑一下,如果你覺得本篇內容有幫助到你,可以轉載但記得要關注,要標明原文哦,謝謝支援~