Array物件
建構函式
Array是 JavaScript 的原生物件,同時也是一個建構函式,可以用它生成新的陣列。
如果沒有使用new,執行結果也是一樣的。
var arr = new Array(2);
// 等同於var arr = Array(2);
Array建構函式有一個很大的缺陷,就是不同的引數,會導致它的行為不一致。
// 無引數時,返回一個空陣列
new Array() // []
// 單個正整數引數,表示返回的新陣列的長度
new Array(1) // [ empty ]
new Array(2) // [ empty x 2 ]
// 非正整數的數值作為引數,會報錯
new Array(3.2) // RangeError: Invalid array length
new Array(-3) // RangeError: Invalid array length
// 單個非數值(比如字串、布林值、物件等)作為引數,
// 則該引數是返回的新陣列的成員
new Array(`abc`) // [`abc`]
new Array([1]) // [Array[1]]
// 多引數時,所有引數都是返回的新陣列的成員
new Array(1, 2) // [1, 2]
new Array(`a`, `b`, `c`) // [`a`, `b`, `c`]
可以看到,Array作為建構函式,行為很不一致。因此,不建議使用它生成新陣列,直接使用陣列字面量是更好的做法。
// bad
var arr = new Array(1, 2);
// good
var arr = [1, 2];
注:ES6 引入了Array.of( )方法來解決這個問題。該方法的作用非常類似Array構造器,但在使用單個數值引數的時候並不會導致特殊結果。Array.of( )方法總會建立一個包含所有傳入引數的陣列,而不管引數的數量與型別:
let items = Array.of(1, 2);
console.log(items.length); // 2
console.log(items[0]); // 1
console.log(items[1]); // 2
items = Array.of(2);
console.log(items.length); // 1
console.log(items[0]); // 2
Array.of基本上可以用來替代Array()或newArray(),並且不存在由於引數不同而導致的過載,而且他們的行為非常統一。
靜態方法
Array.isArray()
Array.isArray方法返回一個布林值,表示引數是否為陣列。它可以彌補typeof運算子的不足。
var arr = [1, 2, 3];
typeof arr // "object"
Array.isArray(arr) // true`
Array.from()
ES6特性,將偽陣列物件或可遍歷物件轉換為真陣列。如果一個物件的所有鍵名都是正整數或零,並且有length屬性,那麼這個物件就很像陣列,稱為偽陣列。典型的偽陣列有函式的arguments物件,以及大多數 DOM 元素集,還有字串。
Array.of()
ES6特性,
例項方法
valueOf(),toString()
valueOf方法是一個所有物件都擁有的方法,表示對該物件求值。不同物件的valueOf方法不盡一致,陣列的valueOf方法返回陣列本身。
var arr = [1, 2, 3];
arr.valueOf() // [1, 2, 3]
toString方法也是物件的通用方法,陣列的toString方法返回陣列的字串形式。
var arr = [1, 2, 3];
arr.toString() // "1,2,3"
var arr = [1, 2, 3, [4, 5, 6]];
arr.toString() // "1,2,3,4,5,6"
push()、pop()
push、pop均是在陣列末尾進行增刪元素,都會改變原陣列。push方法用於在陣列的末端新增一個或多個元素,並返回新增新元素後的陣列長度。pop方法用於刪除陣列的最後一個元素,並返回該元素。
var arr = [];
arr.push(1) // 1
arr.push(`a`) // 2
arr.push(true, {}) // 4
arr // [1, `a`, true, {}]
var arr = [`a`, `b`, `c`];
arr.pop() // `c`
arr // [`a`, `b`]
push和pop結合使用,就構成了“後進先出”的棧結構(stack)。
shift()、unshift()
shift、unshift均是在陣列頭進行增刪元素,都會改變原陣列。shift()方法用於刪除陣列的第一個元素,並返回該元素。unshift()方法用於在陣列的第一個位置新增元素,並返回新增新元素後的陣列長度。
var a = [`a`, `b`, `c`];
a.shift() // `a`
a // [`b`, `c`]
var a = [`a`, `b`, `c`];
a.unshift(`x`); // 4
a // [`x`, `a`, `b`, `c`]
push()和shift()結合使用,就構成了“先進先出”的佇列結構(queue)。
join()
join()方法以指定引數作為分隔符,將所有陣列成員連線為一個字串返回。如果不提供引數,預設用逗號分隔。
var a = [1, 2, 3, 4];
a.join(` `) // `1 2 3 4`
a.join(` | `) // "1 | 2 | 3 | 4"
a.join() // "1,2,3,4"
concat()
concat方法用於多個陣列的合併。它將新陣列的成員,新增到原陣列成員的後部,然後返回一個新陣列,原陣列不變。
[`hello`].concat([`world`])
// ["hello", "world"]
[`hello`].concat([`world`], [`!`])
// ["hello", "world", "!"]
[].concat({a: 1}, {b: 2})
// [{ a: 1 }, { b: 2 }]
[2].concat({a: 1})
// [2, {a: 1}]
[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]
reverse()
reverse方法用於顛倒排列陣列元素,返回改變後的陣列。注意,該方法將改變原陣列。
var a = [`a`, `b`, `c`];
a.reverse() // ["c", "b", "a"]
a // ["c", "b", "a"]
slice()、splice()
slice方法用於提取目標陣列的一部分,返回一個新陣列,原陣列不變。
arr.slice(start, end);
它的第一個引數為起始位置(從0開始),第二個引數為終止位置(但該位置的元素本身不包括在內)。如果省略第二個引數,則一直返回到原陣列的最後一個成員。如果slice方法的引數是負數,則表示倒數計算的位置。
var a = [`a`, `b`, `c`];
a.slice(0) // ["a", "b", "c"]
a.slice(1) // ["b", "c"]
a.slice(1, 2) // ["b"]
a.slice(2, 6) // ["c"]
a.slice() // ["a", "b", "c"]
var a = [`a`, `b`, `c`];
a.slice(-2) // ["b", "c"]
a.slice(-2, -1) // ["b"]
splice方法用於刪除原陣列的一部分成員,並可以在刪除的位置新增新的陣列成員,返回值是被刪除的元素。注意,該方法會改變原陣列。arr.splice(start, count, addElement1, addElement2, ...);
splice的第一個引數是刪除的起始位置(從0開始),第二個引數是被刪除的元素個數。如果後面還有更多的引數,則表示這些就是要被插入陣列的新元素。起始位置如果是負數,就表示從倒數位置開始刪除。如果只是單純地插入元素,splice方法的第二個引數可以設為0。如果只提供第一個引數,等同於將原陣列在指定位置拆分成兩個陣列。
var a = [`a`, `b`, `c`, `d`, `e`, `f`];
a.splice(4, 2) // ["e", "f"]
a // ["a", "b", "c", "d"]
var a = [`a`, `b`, `c`, `d`, `e`, `f`];
a.splice(4, 2, 1, 2) // ["e", "f"]
a // ["a", "b", "c", "d", 1, 2]
var a = [`a`, `b`, `c`, `d`, `e`, `f`];
a.splice(-4, 2) // ["c", "d"]
var a = [1, 1, 1];
a.splice(1, 0, 2) // []
a // [1, 2, 1, 1]
var a = [1, 2, 3, 4];
a.splice(2) // [3, 4]
a // [1, 2]
sort()
sort方法對陣列成員進行排序,預設是按照字典順序排序。排序後,原陣列將被改變。
[`d`, `c`, `b`, `a`].sort()
// [`a`, `b`, `c`, `d`]
[4, 3, 2, 1].sort()
// [1, 2, 3, 4]
[11, 101].sort()
// [101, 11]
[10111, 1101, 111].sort()
數值會被先轉成字串,再按照字典順序進行比較,所以101排在11的前面。
如果想讓sort方法按照自定義方式排序,可以傳入一個函式作為引數。
[10111, 1101, 111].sort(function (a, b) {
return a - b;
})
// [111, 1101, 10111]
[
{ name: "張三", age: 30 },
{ name: "李四", age: 24 },
{ name: "王五", age: 28 }
].sort(function (o1, o2) {
return o1.age - o2.age;
})
// [
// { name: "李四", age: 24 },
// { name: "王五", age: 28 },
// { name: "張三", age: 30 }
// ]
map()、forEach()
map方法和forEach方法很相似,也是對陣列的所有成員依次執行引數函式。map方法將陣列的所有成員依次傳入引數函式,然後把每一次的執行結果組成一個新陣列返回。forEach方法不返回值,只用來運算元據。這就是說,如果陣列遍歷的目的是為了得到返回值,那麼使用map方法,否則使用forEach方法。
map、forEach方法接受一個函式作為引數。該函式呼叫時,map方法向它傳入三個引數:當前成員、當前位置和陣列本身。
[1, 2, 3].map(function(elem, index, arr) {
return elem * index;
});
// [0, 2, 6]
function log(element, index, array) {
console.log(`[` + index + `] = ` + element);
}
[2, 5, 9].forEach(log);
// [0] = 2
// [1] = 5
// [2] = 9
上面程式碼中,map、forEach方法的回撥函式有三個引數,elem為當前成員的值,index為當前成員的位置,arr為原陣列([1, 2, 3])。map方法還可以接受第二個引數,用來繫結回撥函式內部的this變數。
var arr = [`a`, `b`, `c`];
[1, 2].map(function (e) {
return this[e];
}, arr)
// [`b`, `c`]
var out = [];
[1, 2, 3].forEach(function(elem) {
this.push(elem * elem);
}, out);
out // [1, 4, 9]
map、forEach方法不會跳過undefined和null,但是會跳過空位。
注:forEach方法無法中斷執行,總是會將所有成員遍歷完。如果希望符合某種條件時,就中斷遍歷,要使用for迴圈。
var arr = [1, 2, 3];
for (var i = 0; i < arr.length; i++) {
if (arr[i] === 2) break;
console.log(arr[i]);
}
// 1
fliter
filter方法用於過濾陣列成員,滿足條件的成員組成一個新陣列返回。它的引數是一個函式,所有陣列成員依次執行該函式,返回結果為true的成員組成一個新陣列返回。該方法不會改變原陣列。
[1, 2, 3, 4, 5].filter(function (elem, index, arr) {
return index % 2 === 0;
});
// [1, 3, 5]
filter方法還可以接受第二個引數,用來繫結引數函式內部的this變數。
some(),every()
返回一個布林值,表示判斷陣列成員是否符合某種條件。它們接受一個函式作為引數,所有陣列成員依次執行該函式。該函式接受三個引數:當前成員、當前位置和整個陣列,然後返回一個布林值。some方法是隻要一個成員的返回值是true,則整個some方法的返回值就是true,否則返回false。
every方法是所有成員的返回值都是true,整個every方法才返回true,否則返回false。
var arr = [1, 2, 3, 4, 5];
arr.every(function (elem, index, arr) {
return elem >= 3;
});
// false
some和every方法還可以接受第二個引數,用來繫結引數函式內部的this變數。
indexOf(),lastIndexOf()
indexOf方法返回給定元素在陣列中第一次出現的位置,如果沒有出現則返回-1。indexOf方法還可以接受第二個引數,表示搜尋的開始位置。
var a = [`a`, `b`, `c`];
a.indexOf(`b`) // 1
a.indexOf(`y`) // -1
[`a`, `b`, `c`].indexOf(`a`, 1) // -1
lastIndexOf方法返回給定元素在陣列中最後一次出現的位置
var a = [2, 5, 9, 2];
a.lastIndexOf(2) // 3
a.lastIndexOf(7) // -1
find() 和 findIndex()
二者皆是ES6新特性,陣列例項的find方法,用於找出第一個符合條件的陣列成員。它的引數是一個回撥函式,所有陣列成員依次執行該回撥函式,直到找出第一個返回值為true的成員,然後返回該成員。如果沒有符合條件的成員,則返回undefined。
陣列例項的findIndex方法的用法與find方法非常類似,返回第一個符合條件的陣列成員的位置,如果所有成員都不符合條件,則返回-1。
[1, 4, -5, 10].find((n) => n < 0) // -5
[1, 5, 10, 15].findIndex(function(value, index, arr) {
return value > 9;
})
// 2
includes()
ES6新特性,Array.prototype.includes方法返回一個布林值,表示某個陣列是否包含給定的值。該方法的第二個參數列示搜尋的起始位置,預設為0。如果第二個引數為負數,則表示倒數的位置,如果這時它大於陣列長度(比如第二個引數為-4,但陣列長度為3),則會重置為從0開始。
[1, 2, 3].includes(2) // true
[1, 2, 3].includes(3, -1); // true
[1, 2, 3, 5, 1].includes(1, 2); // true
沒有該方法之前,我們通常使用陣列的indexOf方法,檢查是否包含某個值。indexOf方法有兩個缺點,一是不夠語義化,它的含義是找到引數值的第一個出現位置,所以要去比較是否不等於-1,表達起來不夠直觀。二是,它內部使用嚴格相等運算子(===)進行判斷,這會導致對NaN的誤判。
[NaN].indexOf(NaN) // -1
[NaN].includes(NaN) // true
reduce(),reduceRight()
reduce方法和reduceRight方法依次處理陣列的每個成員,最終累計為一個值。它們的差別是,reduce是從左到右處理(從第一個成員到最後一個成員),reduceRight則是從右到左(從最後一個成員到第一個成員),其他完全一樣。
[1, 2, 3, 4, 5].reduce(function (a, b) {
console.log(a, b);
return a + b;
})
// 1 2
// 3 3
// 6 4
// 10 5
//最後結果:15
陣列例項的 entries(),keys() 和 values()
ES6 提供entries(),keys()和values(),用於遍歷陣列。它們都返回一個遍歷器物件,可以用for…of迴圈進行遍歷,唯一的區別是keys()是對鍵名的遍歷、values()是對鍵值的遍歷,entries()是對鍵值對的遍歷。
for (let index of [`a`, `b`].keys()) {
console.log(index);
}
// 0
// 1
for (let elem of [`a`, `b`].values()) {
console.log(elem);
}
// `a`
// `b`
for (let [index, elem] of [`a`, `b`].entries()) {
console.log(index, elem);
}
// 0 "a"
// 1 "b"
總結
- push、pop、shift、unshift、reverse、splice、sort會改變原資料,其餘不會;
常見工具方法
字串、類陣列物件拼接
Array.prototype.join.call(`hello`, `-`)
// "h-e-l-l-o"
var obj = { 0: `a`, 1: `b`, length: 2 };
Array.prototype.join.call(obj, `-`)
// `a-b`
類陣列物件轉為陣列
Array.prototype.slice.call({ 0: `a`, 1: `b`, length: 2 })
// [`a`, `b`]
Array.prototype.slice.call(document.querySelectorAll("div"));
Array.prototype.slice.call(arguments);
找出字元長度最長的陣列成員
function findLongest(entries) {
return entries.reduce(function (longest, entry) {
return entry.length > longest.length ? entry : longest;
}, ``);
}
findLongest([`aaa`, `bb`, `c`]) // "aaa"