JavaScript權威指南-陣列
JavaScript陣列是一種特殊型別的物件。
JavaScript陣列元素可以為任意型別,最大容納232-1個元素。
JavaScript陣列是動態的,有新元素新增時,自動更新length屬性。
JavaScript陣列元素索引可以是不連續的,它們之間可以有空缺。
建立陣列
呼叫建構函式Array()建立陣列:
var a = new Array(); //空陣列,等同於陣列直接量[]
var b = new Array(5); //建立指定長度的陣列
var c = new Array(1, 5, 9, 6); //指定一個或多個元素的非空陣列
陣列直接量表示法:
var a = []; //空陣列
var b = [2, 3, 5, 7]; //常規元素
var c = [5, true, "a"]; //元素不同型別的陣列
var d = [{ x: 2 }, { x: 3 }]; //包含物件元素的陣列
以上是陣列的幾種常見型別,但下面兩種也符合陣列語法。
var e = [1, , 3]; //該陣列有3個元素,中間的元素為undefined
var f = [1, 5, ]; //該陣列有2個元素,結尾逗號後面沒有元素
如果省略陣列直接量中的某個元素值,省略的元素值為undefined
;
陣列直接量的語法中允許有可選結尾的逗號,故[1,5,]
只有兩個元素並非三個。
兩種方法建立的陣列無本質區別,但陣列直接量表示法簡單,實際使用中更為常見。
陣列元素
讀寫陣列元素最簡單的方法就是通過索引。
var arr = ["one", "two"];
var res = arr[0]; //讀第0的元素
arr[0] = "test"; //寫第0的元素
陣列本身就是物件,使用[]
方括號訪問陣列元素就像方括號訪問物件屬性一樣。陣列的特別之處在於,當使用小於232的非負整數作為屬性時陣列會自動維護其length
屬性。當然,陣列也可以有自定義屬性,但不常見。如下:
var obj = [1, 2, 3];
obj["IsShow"] = false; //現在obj陣列為 [1, 2, 3, IsShow: false]
稀疏陣列
稀疏陣列就是包含從0開始的不連續索引的陣列。
var a = new Array(5); //陣列沒有元素,但a.length等於5
var b = [];
b[1000] = 1000; //新增一個索引為1000的元素,但b.length等於1001
通過delete
操作符刪除陣列元素可產生稀疏陣列。delete
不會改變陣列長度,高位置索引元素也不會下移填補刪除索引位置的空白。
注意,省略陣列不等同於稀疏陣列,省略的元素在陣列中是存在的,值為undefined
。
陣列長度
每個陣列都有length
屬性,代表陣列中元素的個數。針對非稀疏陣列,其值比最大索引大1。
[`a`, `b`, `c`].length; //最大索引為2,length為3
當設定length
屬性為一個小於當前陣列長度的非負整數n時,當前陣列中的那些索引大於或等於n的元素將被刪除。
var a = [1, 2, 3, 4, 5]; //陣列初始化5個元素
a.length = 3; //現在a為[1,2,3]
a.length = 0; //刪除所有元素,a為[]
a.length = 5; //陣列長度為5,但是沒有元素
在ECMAScript 5中,可以用Object.defineProperty()
讓陣列的length
屬性變成只讀的。
var b = [1, 2, 3];
Object.defineProperty(b, "length", { writable: false }); //讓length變成只讀屬性
b.length = 0; //更改無效
陣列遍歷
使用for迴圈遍歷陣列元素是最常見的方法。如下:
var obj = { height: 175, weight: 60 }; //初始化一個物件
var keys = Object.keys(obj); //獲取物件obj屬性名組成的陣列
var values = []; //values用來儲存物件obj屬性值
for (var i = 0, len = keys.length; i < len; i++) {
var key = keys[i]; //獲取當前索引的鍵值
values[i] = obj[key]; //在values陣列中儲存屬性值
}
針對稀疏陣列遍歷時,注意過濾掉不滿足條件的元素。
for (var i = 0; i < arr.length; i++) {
if (!arr[i]) continue; //跳過null,undefined和不存在的元素
if (arr[i] === undefined) continue; //跳過undefined和不存在的元素
if (!(i in arr)) continue; //跳過不存在的元素
//T0DO
}
多維陣列
JavaScript不支援真正的多維陣列,一般用陣列的陣列來近似。下面是一個具體的例子,使用二維陣列作為一個9X9乘法表。
//建立一個多維陣列
var table = new Array(10); //表格有10行
for (var i = 0; i < table.length; i++) {
table[i] = new Array(10); //每行有10列
}
//初始化陣列
for (var row = 0; row < table.length; row++) {
for (var col = 0; col < table[row].length; col++) {
table[row][col] = row * col;
}
}
//使用多維陣列來計算
var result = table[8][9]; //result = 72
陣列方法
ECMAScript 3在Array.prototype中定義了一些很有用的運算元組的方法,下面介紹這些方法的基本用法。
join()Array.join(separator)
該方法可以將陣列元素按照指定字元連線起來,返回最終生成的字串。如果不指定字元separator
,預設用逗號分隔。
var arr = [1, 2, 3];
arr.join(); //=>"1,2,3" 預設使用逗號作為元素連線符
arr.join(` `); //=>"1 2 3" 以空格作為連線符
arr.join(`|`); //=>"1|2|3" 以‘|’作為連線符
reverse()Array.reverse()
該方法將陣列中的的元素顛倒順序,在原陣列上進行操作。方法返回值為對原來陣列的引用。
var arr = [1, 2, 3];
arr.reverse().join(); //=>"3,2,1" ,並且現在arr為[3,2,1]
sort()Array.sort([compareFunction])
該方法將陣列中的元素排序並返回對原來陣列的引用。不傳遞引數呼叫時,預設按照字母順序排序。
var fruits = [`banana`, `cherry`, `apple`];
fruits.sort().join(); //=>apple,banana,cherry
當按照其他方式排序時,就要提供一個比較函式compareFunction
。該函式要比較兩個值,然後返回一個用於說明這兩個值的相對順序的數字。比較函式應該具有兩個引數 a 和 b,其返回值如下:
- 若 a 小於 b,在排序後的陣列中 a 應該出現在 b 之前,則返回一個小於 0 的值。
- 若 a 等於 b,則返回 0。
- 若 a 大於 b,則返回一個大於 0 的值。
var s = [33, 666, 12, 5];
s.sort(); //字母順序:12,33,5,666
s.sort(function (a, b) { //數字順序:5,12,33,666
return a - b;
});
如果要排序的陣列元素包含undefined
,它們會被排到陣列尾部。
concat()Array.concat(arr1[,arr2,...])
該方法用於連線兩個或多個陣列並返回一個新陣列,不會改變現有陣列本身。
var a = [1, 2];
a.concat(4, 5); //=>[1,2,4,5] 連線每一個引數值
a.concat([4, 5]); //=>[1,2,4,5] 連線一個陣列
a.concat([4, 5], [6, 7]); //=>[1,2,4,5,6,7] 連線多個陣列
slice()Array.slice(start[,end])
該方法用來從已有的陣列返回選定的元素,返回一個新的陣列。兩個引數分別指定要選定元素的開始位置和結束位置。
-
start
參數列示從什麼位置開始取。如果是負數,那麼它規定從陣列尾部開始算起的位置。也就是說,-1 指最後一個元素,-2 指倒數第二個元素,以此類推。 -
end
是一個可選引數。規定從何處結束選取,但不包括該下標元素。如果沒有指定該引數,那麼切分的陣列包含從 start 到陣列結束的所有元素。如果這個引數是負數,那麼它規定的是從陣列尾部開始算起的元素。
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]
a.slice(-3, -2); //返回[3]
splice()Array.splice(index,count[, item1[, item2[, ...]]])
該方法用來向陣列中新增或刪除元素,並且用引數列表中宣告的一個或多個值來替換那些被刪除的元素。返回被刪除的元素。該方法會改變原始陣列。
-
index
引數代表要新增或刪除元素的索引。 -
count
引數代表要從陣列中刪除的元素個數。如果省略,從index
起點到陣列結尾的元素全刪除。 -
item1,item2,...
從第三個引數開始是可選引數。代表向陣列新增的新元素。
var names = ["George", "Thomas"];
names.splice(1, 0, "John"); //在索引為1的地方插入一個新元素
names.splice(0, 1, "Tom"); //將第一個元素`George`替換成`Tom`
splice()
與slice()
方法,一個字母之差但是功能完全不同,注意區別使用。
push()和pop()Array.push(element1,element2,...)
該方法用來向陣列的末尾新增一個或多個元素,並返回新的長度。Array.pop()
方法用來刪除陣列的最後一個元素,減小陣列長度,返回刪除的元素值。
組合push()
和pop()
能夠讓JavaScript陣列實現先進後出的棧功能:push()
入棧、pop()
出棧。
var stack = []; //空棧
stack.push(1, 2); //stack:[1,2]
stack.pop(); //stack:[1]
stack.push(3); //stack:[1,3]
stack.pop(); //stack:[1]
stack.push([4, 5]); //stack:[1,[4, 5]]
stack.pop(); //stack:[1]
stack.pop(); //stack:[]
unshift()和shift()Array.unshift(element1,element2,...)
該方法可向陣列的開頭新增一個或更多元素,並返回新的長度。Array.shift()
方法用於把陣列的第一個元素從其中刪除,並返回第一個元素的值。如果陣列為空,shift()
不進行任何操作,返回undefined
。
這兩個方法行為非常類似於push()
和pop()
。不一樣的是,這兩個方法是在陣列頭部操作。
var arr = [3, 4];
arr.unshift(1,2); // arr:[1,2,3,4]
arr.shift(); // arr:[2,3,4]
toString()和toLocalString()
陣列物件和普通物件一樣擁有toString()
方法。該方法會將陣列元素轉化為字串,用逗號把生成的字串連線起來,形成一個字串。返回值與沒有引數的join()
方法返回的字串相同。
[1, 2, 3].toString(); //"1,2,3"
["a", "b", "c"].toString(); //"a,b,c"
[1, [2, `c`]].toString(); //"1,2,c"
toLocalString()
是toString()
的本地化方法。
ECMAScript 5中定義了9個新的陣列方法來遍歷,對映,過濾,檢測,簡化和搜尋陣列。有了這些方法就不用利用for迴圈來遍歷陣列了。
forEach()Array.forEach(callback[, thisArg])
方法用來從頭致尾遍歷陣列,為每個元素呼叫回撥方法。對於稀疏陣列,不存在的元素不呼叫回撥方法。
-
callback
引數就是在陣列每一項上執行的函式,接收三個引數:陣列元素、元素索引和陣列本身。 -
thisArg
是可選引數,用來當作callback
函式內this
的值的物件。如果省略了thisArg
引數,或者賦值為null
或undefined
,則this
在非嚴格模式下將是全域性物件,嚴格模式下為undefined
。
下面看個綜合例子:以陣列元素為半徑,計算所有圓的面積。
var numbers = [5, 6]; // Define an array.
var obj = {
showResults: function(value, index,array) {
var squared = this.calcSquare(value);
document.write("value: " + value);
document.write(" index: " + index);
document.write(" squared: " + squared);
document.write("<br />");
},
calcSquare: function(x) { return x * x }
};
numbers.forEach(function(value, index) { this.showResults(value, index) }, obj);
// Output:
// value: 5 index: 0 squared: 25
// value: 6 index: 1 squared: 36
注意:沒有辦法中止或者跳出forEach
迴圈,除了丟擲一個異常。它總是返回undefined
,即沒有返回值。
map()Array.map(callback[, thisArg])
方法和forEach()
同樣是用來遍歷陣列,為每個元素執行回撥方法。該方法引數與forEach()
方法引數一致,不再贅述。但是傳給map()
的函式應該有返回值。
var numbers = [1, 4, 9];
var roots = numbers.map(Math.sqrt); //求陣列中每個元素的平方根
/* roots的值為[1, 2, 3], numbers的值仍為[1, 4, 9] */
filter()Array.filter(callback[, thisArg])
方法用來過濾陣列元素,將符合規則的元素組成一個新陣列返回,不會改變原陣列。callback
引數就是用來測試陣列中元素的方法,返回true
表示通過測試。
var arr = [1, 2, 3, 4, 5];
var res = arr.filter(function (value, index, array) {
return value > 3; //過濾掉小於等於3的元素
});
alert(res.toString()); //=> 4,5
對其非稠密陣列,壓縮刪除undefined
和null
元素,可以這樣使用filter()
:
arr.filter(function (value) { return value != undefined && value != null; });
every()和some()Array.every(callback[, thisArg])
該方法用來測試陣列元素是否都通過了指定函式的測試。Array.some(callback[, thisArg])
該方法用來測試陣列某些元素是否通過了指定函式的測試。
這兩個方法就是陣列的邏輯判斷。它們對陣列元素呼叫指定方法,返回true
或false
。
var arr = [1, 2, 3, 4, 5];
arr.every(function (value) { return value < 10; }); //=>true 所有元素值<10
arr.every(function (value) { return value % 2 == 0; }); //false 並非所有元素都為偶數
arr.some(function (value) { return value % 2 == 0; }); //=>true 陣列元素包含偶數
arr.some(isNaN); //=>false 陣列不包含非數值元素
注意:當every()
和some()
已確認該返回什麼值的時候就會停止遍歷陣列。
reduce()和reduceRight()Array.reduce(callback[, initialValue])
該方法會針對陣列中每個元素呼叫指定回撥函式,將回撥函式的返回值作為累積,然後以引數的形式傳遞到下個元素的回撥方法中。
-
callback
引數是陣列元素要執行的回撥函式。最多可接收4個引數:之前元素累積值、當前元素值、元素索引、陣列本身。 -
initialValue
是可選引數,表示元素最開始呼叫回撥函式傳入的初始值。如果預設該引數,它會使用陣列第一個元素作為初始值,這樣陣列就會少迭代一次。
var arr = [1, 2, 3, 4];
var sum = arr.reduce(function (previous, current, index, array) {
return previous + current;
}, 5);
var max = arr.reduce(function (previous, current, index, array) {
return previous > current ? previous : current;
});
console.log(sum); //=>15 求和
console.log(max); //=>4 求最大值
利用reduce()
可以輕鬆實現二維陣列的扁平化:
var matrix = [ [1, 2], [3, 4], [5, 6] ];
var flatten = matrix.reduce(function (previous, current) {
return previous.concat(current);
});
console.log(flatten); //=> [1, 2, 3, 4, 5, 6]
Array.reduceRight(callback[, initialvalue])
方法的用法與reduce()
方法一致,唯一區別是該方法按元素索引降序處理元素。
indexOf()和lastIndexOf()Array.indexOf(searchvalue[, fromIndex])
方法用來搜尋陣列中給定值的元素,並返回該元素的索引,如果找不到指定的元素則返回-1。indexOf()
從陣列頭至尾開始搜,Array.lastIndexOf(searchvalue[, fromIndex])
則相反,從陣列尾部為起點開始搜。
-
searchvalue
引數代表要搜尋的元素值。 -
fromindex
是可選引數,表示檢索的起始位置。其值可以為字串數值;填入字元自動忽略,預設為0。
var data = [2, 5, 7, 3, 5];
console.log(data.indexOf(5, "x")); //=> 1 "x"被忽略,用0代替
console.log(data.indexOf(5, "3")); //=> 4 從3號位開始搜尋
大多數瀏覽器都支援以上方法。針對低版本IE6-IE8瀏覽器相容性問題,可通過Array原型擴充套件實現以上方法。例如forEach方法:
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (callback, thisArg) {
//TODO
};
}
陣列型別
陣列是具有特殊行為的物件。開發中可能會遇到這樣的情況:給定一個未知物件,判斷它是否為陣列物件。ECMAScript 5版本中可以用Array.isArray()
方法鑑別。
console.log(Array.isArray([])); //=> true
console.log(Array.isArray({})); //=> false
在ECMAScript 5版本以前沒有Array.isArray()
這個方法,typeof
可解決大部分的資料型別判斷但是在這卻幫不上忙。
instanceof
操作符可以檢測,但有侷限性,只能作用於單頁面的情形。
console.log([] instanceof Array); //=> true
console.log({} instanceof Array); //=> false
當頁面中存在子頁面iframe時,在子頁面中宣告一個陣列object
,並將其賦值給父頁面的一個變數,這時判斷該變數:object instanceof Array
會返回false
。原因是陣列是引用型別,在賦值過程中,傳遞的是引用地址。但是每個頁面都有自己的一套全域性物件,並且每個全域性物件有自己的建構函式。object
是子頁面Array物件,傳遞到父頁面,在父頁面判斷時卻是以父頁面的Array物件為標準。
ECMAScript 3版本中,檢測物件是否為陣列的isArray()
可以這樣寫:
var isArray = Array.isArray() || function isArray(arg) {
return typeof arg === `object` && //是否為物件
object.length === `number` && //驗證length屬性
Object.prototype.toString.call(arg) === `[object Array]`; //判斷基本型別*
}
根據陣列的一些特性來判斷,上面一段程式碼也正是ES5中Array.isArray()
方法的實現形式。
類陣列物件
通常把一個具有與陣列相仿屬性的常規物件叫做“類陣列”物件,即具有length屬性
和對應非負正整數屬性
。類陣列物件不能直接呼叫陣列的方法,但可以陣列的形式遍歷。
//定義一個類陣列物件
var obj = { 0: `a`, 1: `b`, 2: `c`, length: 3 };
//當作陣列遍歷
for (var i = 0; i < obj.length; i++) {
console.log(obj[i]);
}
JavaScript函式體中Arguments
物件是一個類陣列物件。一些DOM方法也返回類陣列物件,比如document.getElementsByTagName()
。
可以用下面的方法檢查物件是否為類陣列:
function isArrayLike(o) {
if (o && //判斷o非null,undefined等
typeof o === `object` && //o是物件
isFinite(o.length) && //o.length是有限數
o.length > 0 && //o.length是非負數
o.length < 4294967296 && //o.length小於2^32
o.length === Math.floor(o.length)) //o.length是整數
return true;
else
return false;
}
ES5版本中,所有Array陣列方法都是通用的,類陣列物件上同樣適用。類陣列物件沒有繼承至Array.prototype
不能直接呼叫,但可以通過Function.call
方法呼叫:
var obj = { 0: `a`, 1: `b`, 2: `c`, length: 3 };
console.log(Array.prototype.join.call(obj, `|`)); //=> a|b|c
var arr = Array.prototype.map.call(obj, function (value) {
return value.toUpperCase();
});
console.log(arr.join); //=> A,B,C 此時arr已是真正的陣列
參考與擴充套件
本篇內容源自我對《JavaScript權威指南》第7章 陣列 章節的閱讀總結和程式碼實踐。總結的比較粗糙,你也可通過原著或MDN更深入瞭解陣列。
[1] David Flanagan,JavaScript權威指南(第6版)
[2] MDN,JavaScript 參考文件 – Array – JavaScript | MDN
相關文章
- JavaScript權威指南(7)——陣列JavaScript陣列
- 《JavaScript權威指南第六版》學習筆記-陣列JavaScript筆記陣列
- JavaScript 日期權威指南JavaScript
- JavaScript權威指南(6)——物件JavaScript物件
- [譯] JAVASCRIPT 日期權威指南JavaScript
- JavaScript權威指南(8)——函式JavaScript函式
- javascript權威指南——函式篇JavaScript函式
- JavaScript權威指南(9)——類和模組JavaScript
- JavaScript 陣列方法:綜合指南JavaScript陣列
- JavaScript權威指南(2)——詞法結構JavaScript
- [心得] JavaScript權威指南學習筆記JavaScript筆記
- javascript權威指南閱讀筆記2JavaScript筆記
- JavaScript權威指南(11)——JavaScript的子集和擴充套件JavaScript套件
- JavaScript權威指南(4)——表示式和運算子JavaScript
- Git權威指南Git
- HTTP權威指南HTTP
- Netty權威指南Netty
- 《ZeroC Ice權威指南》
- 讀《Cassandra權威指南》
- JavaScript權威指南(10)——正規表示式的模式匹配JavaScript模式
- JavaScript 權威指南第七版(GPT 重譯)(一)JavaScriptGPT
- JavaScript 權威指南第七版(GPT 重譯)(二)JavaScriptGPT
- JavaScript 權威指南第七版(GPT 重譯)(三)JavaScriptGPT
- JavaScript 權威指南第七版(GPT 重譯)(四)JavaScriptGPT
- JavaScript 權威指南第七版(GPT 重譯)(五)JavaScriptGPT
- 《JavaScript權威指南第六版》學習筆記-JavaScript概述JavaScript筆記
- JavaScript 權威指南(第六版)學習筆記JavaScript筆記
- 《JavaScript權威指南第六版》學習筆記-物件JavaScript筆記物件
- Java 13權威指南 - CodeFXJava
- Elasticsearch 權威指南(中文版)Elasticsearch
- HBase權威指南【中文版】
- 微服務入門權威指南微服務
- 《http權威指南》學習感想HTTP
- ORACLE11G權威指南Oracle
- RPM包的權威指南。
- JavaScript 陣列JavaScript陣列
- 《JavaScript權威指南第六版》學習筆記-語句JavaScript筆記
- 《JavaScript權威指南第六版》學習筆記-函式JavaScript筆記函式