Array物件

tr0313發表於2019-02-16

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 元素集,還有字串。
109ca7cacdfef8b8bc90681a7e3d0343.png

Array.of()

ES6特性,
4f43e3c9c023167ba4928bcb33c3a535.png

例項方法

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

f1af205ad410dda05d122e5b06ca5ecc.png

陣列例項的 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"

總結

  1. 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"

相關文章