ECMAScript 6引入三個點“...”語法用來分別代表一個數組參數列表。
rest操作符主要用於獲得傳遞給函數的參數列表,案例代碼:
function countArguments(...args) {
return args.length;
}
// 獲得參數的數量
countArguments('welcome', 'to', 'Earth'); // => 3
下面是spread操作符主要用於數組構造和解構,在調用時將數組填入函數參數:
let cold = ['autumn', 'winter'];
let warm = ['spring', 'summer'];
// 構造一個數組
[...cold, ...warm] // 真正數組值是 ['autumn', 'winter', 'spring', 'summer']
// 解構一個數組
let otherSeasons, autumn;
[autumn, ...otherSeasons] = cold;
otherSeasons // 值是 ['winter']
// 代表一個數組的函數參數
cold.push(...warm);
cold // 值是 ['autumn', 'winter', 'spring', 'summer']
下面我們看看爲什麼要使用這種數組參數?
Rest參數
如果不使用這種三個點的數組參數新語法,過去我們是使用一個對象作爲函數參數,但是會遭遇函數外和函數內訪問不一致情況,如下面filterNumbers()是一個內部函數,它要訪問外部的函數sumOnlyNumbers()的arguments 對象:
function sumOnlyNumbers() {
var args = arguments;
var numbers = filterNumbers();
return numbers.reduce((sum, element) => sum + element);
function filterNumbers() {
return Array.prototype.filter.call(args,
element => typeof element === 'number'
);
}
}
sumOnlyNumbers(1, 'Hello', 5, false); // => 6
首先我們要將arguments分配給給一個臨時新變量args,這樣才能在內部函數filterNumbers中可以訪問args新變量,因爲 filterNumbers()定義了它自己的arguments 會覆蓋外部的arguments 。這種做法太冗餘了。
使用rest操作符可以靈活解決這個問題,允許在函數中定義一個rest參數 ...args:
function sumOnlyNumbers(...args) {
var numbers = filterNumbers();
return numbers.reduce((sum, element) => sum + element);
function filterNumbers() {
return args.filter(element => typeof element === 'number');
}
}
sumOnlyNumbers(1, 'Hello', 5, false); // => 6
function sumOnlyNumbers(...args) 函數定義了args接受以一個數組作爲參數,因爲名稱衝突解決了,因此args可以使用在 filterNumbers()內部。而且引入args後,可以直接使用 args.filter()方法,這也是rest參數獨特之處。
如果不是所有值都包括在rest參數中,你能在開始定義一個逗號,明確定義這些參數是不包括在rest參數中:
function filter(type, ...items) {
return items.filter(item => typeof item === type);
}
filter('boolean', true, 0, false); // => [true, false]
filter('number', false, 4, 'Welcome', 7); // => [4, 7]
而arguments 對象則沒有這種可選擇的屬性,而是包涵了所有值。
下面再看看在箭頭函數的情況,一個箭頭函數並不能在其內容體內定義arguments 對象,而是隻能訪問其閉包作用域下的那個arguments,如果你想獲得所有的參數只能使用rest參數:
(function() {
let outerArguments = arguments;
const concat = (...items) => {
console.log(arguments === outerArguments); // => true
return items.reduce((result, item) => result + item, '');
};
concat(1, 5, 'nine'); // => '15nine'
})();
items rest參數包含了函數所有參數在一個數組中,而arguments對象是來自閉包enclosing作用域獲得的,因此肯定等於outerArguments ,也就沒有多大意義。
spread參數
spread操作符能夠用一個數組配置構造器參數:
class King {
constructor(name, country) {
this.name = name;
this.country = country;
}
getDescription() {
return `${this.name} leads ${this.country}`;
}
}
var details = ['Alexander the Great', 'Greece'];
var Alexander = new King(...details);
Alexander.getDescription();
King的構造器參數是name和country,而我們可以使用三個點"..."將一個數組賦值到這個構造器,數組的數值分別對應於name和country。
此外spread操作符能夠使用遍歷協議接口對一個其中元素進行遍歷然後收集結果。
function iterator() {
var index = 0;
return {
next: () => ({ // 遵循遍歷協議Iterator protocol
done : index >= this.length,
value: this[index++]
})
};
}
var arrayLike = {
0: 'Cat',
1: 'Bird',
length: 2
};
arrayLike[Symbol.iterator] = iterator; //遵循可被遍歷協議
var array = [...arrayLike];
console.log(array); // => ['Cat', 'Bird']
arrayLike[Symbol.iterator]會在包含遍歷函數iterator()對象中創建一個屬性,使得這個對象遵循可被遍歷協議(itarable protocol. iterator()),返回的是一個帶有next屬性作爲函數的對象(遵循 iteration protocol)。
因此arrayLike 現在變得可遍歷了,spread操作符用於釋放arrayLike中元素到一個數組中,也就是釋放到數組[...arrayLike],這就是三個點語法spread參數可作爲一個枚舉元素的結果列表。