天天看點

ES6 數組

參考:https://es6.ruanyifeng.com/#docs/array

一、擴充運算符

1、含義

擴充運算符(spread)是三個點(

...

)。它好比 rest 參數的逆運算,将一個數組轉為用逗号分隔的參數序列。

console.log(...[1, 2, 3])
// 1 2 3

console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5

[...document.querySelectorAll('div')]
// [<div>, <div>, <div>]
           
  • 該運算符主要用于函數調用。
  • 擴充運算符與正常的函數參數可以結合使用
  • 擴充運算符後面還可以放置表達式。
  • 如果擴充運算符後面是一個空數組,則不産生任何效果。
  • 隻有函數調用時,擴充運算符才可以放在圓括号中,否則會報錯。 

2、替代函數的 apply 方法 

由于擴充運算符可以展開數組,是以不再需要

apply

方法,将數組轉為函數的參數了。

// ES5 的寫法
function f(x, y, z) {
  // ...
}
var args = [0, 1, 2];
f.apply(null, args);

// ES6的寫法
function f(x, y, z) {
  // ...
}
let args = [0, 1, 2];
f(...args);
           

面是擴充運算符取代

apply

方法的一個實際的例子,應用

Math.max

方法,簡化求出一個數組最大元素的寫法。

// ES5 的寫法
Math.max.apply(null, [14, 3, 77])

// ES6 的寫法
Math.max(...[14, 3, 77])

// 等同于
Math.max(14, 3, 77);
           

上面代碼中,由于 JavaScript 不提供求數組最大元素的函數,是以隻能套用

Math.max

函數,将數組轉為一個參數序列,然後求最大值。有了擴充運算符以後,就可以直接用

Math.max

了。

另一個例子是通過

push

函數,将一個數組添加到另一個數組的尾部。

// ES5的 寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);

// ES6 的寫法
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1.push(...arr2);
           

上面代碼的 ES5 寫法中,

push

方法的參數不能是數組,是以隻好通過

apply

方法變通使用

push

方法。有了擴充運算符,就可以直接将數組傳入

push

方法。

// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);
           

3、擴充運算符的應用

1)複制數組

數組是複合的資料類型,直接複制的話,隻是複制了指向底層資料結構的指針,而不是克隆一個全新的數組。

const a1 = [1, 2];
const a2 = a1;

a2[0] = 2;
a1 // [2, 2]
           

上面代碼中,

a2

并不是

a1

的克隆,而是指向同一份資料的另一個指針。修改

a2

,會直接導緻

a1

的變化。

ES5 隻能用變通方法來複制數組。

const a1 = [1, 2];
const a2 = a1.concat();

a2[0] = 2;
a1 // [1, 2]
           

上面代碼中,

a1

會傳回原數組的克隆,再修改

a2

就不會對

a1

産生影響。

擴充運算符提供了複制數組的簡便寫法。

const a1 = [1, 2];
// 寫法一
const a2 = [...a1];
// 寫法二
const [...a2] = a1;

//a2都是a1的克隆。
           

2)合并數組

擴充運算符提供了數組合并的新寫法。

const arr1 = ['a', 'b'];
const arr2 = ['c'];
const arr3 = ['d', 'e'];

// ES5 的合并數組
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]

// ES6 的合并數組
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
           

不過,這兩種方法都是淺拷貝,使用的時候需要注意。

const a1 = [{ foo: 1 }];
const a2 = [{ bar: 2 }];

const a3 = a1.concat(a2);
const a4 = [...a1, ...a2];

a3[0] === a1[0] // true
a4[0] === a1[0] // true
           

上面代碼中,

a3

a4

是用兩種不同方法合并而成的新數組,但是它們的成員都是對原數組成員的引用,這就是淺拷貝。如果修改了引用指向的值,會同步反映到新數組。 

3)與解構指派結合

擴充運算符可以與解構指派結合起來,用于生成數組。

// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
           

注意:如果将擴充運算符用于數組指派,隻能放在參數的最後一位,否則會報錯。 

const [...butLast, last] = [1, 2, 3, 4, 5];
// 報錯

const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 報錯
           

 4)字元串

擴充運算符還可以将字元串轉為真正的數組。

[...'hello']
// [ "h", "e", "l", "l", "o" ]
           

5)實作了 Iterator 接口的對象 

任何定義了周遊器(Iterator)接口的對象(參閱 Iterator 一章),都可以用擴充運算符轉為真正的數組。

let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
           

上面代碼中,

querySelectorAll

方法傳回的是一個

NodeList

對象。它不是數組,而是一個類似數組的對象。這時,擴充運算符可以将其轉為真正的數組,原因就在于

NodeList

對象實作了 Iterator 。 

對于那些沒有部署 Iterator 接口的類似數組的對象,擴充運算符就無法将其轉為真正的數組。擴充運算符就會報錯。

6)Map 和 Set 結構,Generator 函數 

擴充運算符内部調用的是資料結構的 Iterator 接口,是以隻要具有 Iterator 接口的對象,都可以使用擴充運算符,比如 Map 結構。

let map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

let arr = [...map.keys()]; // [1, 2, 3]
           

Generator 函數運作後,傳回一個周遊器對象,是以也可以使用擴充運算符。

const go = function*(){
  yield 1;
  yield 2;
  yield 3;
};

[...go()] // [1, 2, 3]
           

上面代碼中,變量

go

是一個 Generator 函數,執行後傳回的是一個周遊器對象,對這個周遊器對象執行擴充運算符,就會将内部周遊得到的值,轉為一個數組。

如果對沒有 Iterator 接口的對象,使用擴充運算符,将會報錯。

二、Array.from()

Array.from

方法用于将兩類對象轉為真正的數組:類似數組的對象(array-like object)和可周遊(iterable)的對象(包括 ES6 新增的資料結構 Set 和 Map)

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
           

實際應用中,常見的類似數組的對象是 DOM 操作傳回的 NodeList 集合,以及函數内部的

arguments

對象。

Array.from

都可以将它們轉為真正的數組。

// NodeList對象
let ps = document.querySelectorAll('p');
Array.from(ps).filter(p => {
  return p.textContent.length > 100;
});

// arguments對象
function foo() {
  var args = Array.from(arguments);
  // ...
}
           

上面代碼中,

querySelectorAll

方法傳回的是一個類似數組的對象,可以将這個對象轉為真正的數組,再使用

filter

方法。

隻要是部署了 Iterator 接口的資料結構,

Array.from

都能将其轉為數組。

數組建立

1、Array.of()

将參數中所有值作為元素形成數組。

console.log(Array.of(1, 2, 3, 4)); // [1, 2, 3, 4]
 
// 參數值可為不同類型
console.log(Array.of(1, '2', true)); // [1, '2', true]
 
// 參數為空時傳回空數組
console.log(Array.of()); // []
           

2、Array.from()

方法用于将兩類對象轉為真正的數組:類似數組的對象(array-like object)和可周遊(iterable)的對象(包括 ES6 新增的資料結構 Set 和 Map)。

下面是一個類似數組的對象,

Array.from

将它轉為真正的數組。

let arrayLike = {
    '0': 'a',
    '1': 'b',
    '2': 'c',
    length: 3
};

// ES5的寫法
var arr1 = [].slice.call(arrayLike); // ['a', 'b', 'c']

// ES6的寫法
let arr2 = Array.from(arrayLike); // ['a', 'b', 'c']
           
es6