聲明 本系列文章內容全部梳理自以下幾個來源: 《JavaScript權威指南》 "MDN web docs" "Github:smyhvae/web" "Github:goddyZhao/Translation/JavaScript" 作為一個前端小白,入門跟著這幾個來源學習,感謝作者的分享,在其基 ...
聲明
本系列文章內容全部梳理自以下幾個來源:
- 《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),公眾號中有我的聯繫方式,歡迎有事沒事來嘮嗑一下,如果你覺得本篇內容有幫助到你,可以轉載但記得要關註,要標明原文哦,謝謝支持~