相比ES5,ES6 允許為函數(shù)的參數(shù)設(shè)置默認值蛋叼。
即直接寫在參數(shù)定義的后面晶密。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
ES6 引入 rest 參數(shù)(形式為 “... 變量名 ” )虐秋,用于獲取函數(shù)的多余參數(shù),這樣就不需要使用 arguments 對象了努潘。 rest 參數(shù)搭配的變量是一個數(shù)組诽偷,該變量將
多余的參數(shù)放入數(shù)組中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代碼的 add 函數(shù)是一個求和函數(shù)疯坤,利用 rest 參數(shù)报慕,可以向該函數(shù)傳入任意數(shù)目的參數(shù)。
下面是一個 rest 參數(shù)代替 arguments 變量的例子压怠。
// arguments 變量的寫法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest 參數(shù)的寫法
const sortNumbers = (...numbers) => numbers.sort();
上面代碼的兩種寫法眠冈,比較后可以發(fā)現(xiàn), rest 參數(shù)的寫法更自然也更簡潔菌瘫。
rest 參數(shù)中的變量代表一個數(shù)組蜗顽,所以數(shù)組特有的方法都可以用于這個變量。下面是一個利用 rest 參數(shù)改寫數(shù)組 push 方法的例子雨让。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
注意雇盖, rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個參數(shù)),否則會報錯栖忠。
// 報錯
function f(a, ...b, c) {
// ...
}
函數(shù)的 length 屬性崔挖,不包括 rest 參數(shù)贸街。
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
擴展運算符
擴展運算符( spread )是三個點(...)。它好比 rest 參數(shù)的逆運算狸相,將一個數(shù)組轉(zhuǎn)為用逗號分隔的參數(shù)序列薛匪。
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]
該運算符主要用于函數(shù)調(diào)用。
function push(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y;
}
var numbers = [4, 38];
add(...numbers) // 42
上面代碼中卷哩,array.push(...items)和add(...numbers)這兩行蛋辈,都是函數(shù)的調(diào)用,它們的都使用了擴展運算符将谊。該運算符將一個數(shù)組冷溶,變?yōu)閰?shù)序
列。
擴展運算符與正常的函數(shù)參數(shù)可以結(jié)合使用尊浓,非常靈活逞频。
function f(v, w, x, y, z) { }
var args = [0, 1];
f(-1, ...args, 2, ...[3]);
替代數(shù)組的 apply 方法
由于擴展運算符可以展開數(shù)組,所以不再需要apply方法栋齿,將數(shù)組轉(zhuǎn)為函數(shù)的參數(shù)了苗胀。
// ES5 的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
// ES6 的寫法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f(...args);
下面是擴展運算符取代apply方法的一個實際的例子,應(yīng)用Math.max方法瓦堵,簡化求出一個數(shù)組最大元素的寫法基协。
// ES5 的寫法
Math.max.apply(null, [14, 3, 77])
// ES6 的寫法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
上面代碼表示,由于 JavaScript 不提供求數(shù)組最大元素的函數(shù)菇用,所以只能套用Math.max函數(shù)澜驮,將數(shù)組轉(zhuǎn)為一個參數(shù)序列,然后求最大值惋鸥。有了擴展運算符以后杂穷,就可以直接用Math.max了。
另一個例子是通過push函數(shù)卦绣,將一個數(shù)組添加到另一個數(shù)組的尾部耐量。
// ES5 的寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
Array.prototype.push.apply(arr1, arr2);
// ES6 的寫法
var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);
上面代碼的 ES5 寫法中,push方法的參數(shù)不能是數(shù)組滤港,所以只好通過apply方法變通使用push方法廊蜒。有了擴展運算符,就可以直接將數(shù)組傳入push方法蜗搔。
下面是另外一個例子劲藐。
// ES5
new (Date.bind.apply(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1]);
擴展運算符的應(yīng)用
( 1 )合并數(shù)組
擴展運算符提供了數(shù)組合并的新寫法。
// ES5
[1, 2].concat(more)
// ES6
[1, 2, ...more]
var arr1 = ['a', 'b'];
var arr2 = ['c'];
var arr3 = ['d', 'e'];
// ES5 的合并數(shù)組
arr1.concat(arr2, arr3);
// [ 'a', 'b', 'c', 'd', 'e' ]
// ES6 的合并數(shù)組
[...arr1, ...arr2, ...arr3]
// [ 'a', 'b', 'c', 'd', 'e' ]
( 2 )與解構(gòu)賦值結(jié)合
擴展運算符可以與解構(gòu)賦值結(jié)合起來樟凄,用于生成數(shù)組聘芜。
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
下面是另外一些例子。
const [first, ...rest] = [1, 2, 3, 4, 5];
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = [];
first // undefined
rest // []:
const [first, ...rest] = ["foo"];
first // "foo"
rest // []
如果將擴展運算符用于數(shù)組賦值缝龄,只能放在參數(shù)的最后一位汰现,否則會報錯挂谍。
const [...butLast, last] = [1, 2, 3, 4, 5];
// 報錯
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 報錯
( 3 )函數(shù)的返回值
JavaScript 的函數(shù)只能返回一個值,如果需要返回多個值瞎饲,只能返回數(shù)組或?qū)ο罂谛稹U展運算符提供了解決這個問題的一種變通方法。
var dateFields = readDateFields(database);
var d = new Date(...dateFields);
上面代碼從數(shù)據(jù)庫取出一行數(shù)據(jù)嗅战,通過擴展運算符妄田,直接將其傳入構(gòu)造函數(shù)Date。
( 4 )字符串
擴展運算符還可以將字符串轉(zhuǎn)為真正的數(shù)組驮捍。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
上面的寫法疟呐,有一個重要的好處,那就是能夠正確識別 32 位的 Unicode 字符东且。
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
上面代碼的第一種寫法启具, JavaScript 會將 32 位 Unicode 字符,識別為 2 個字符珊泳,采用擴展運算符就沒有這個問題鲁冯。因此,正確返回字符串長度的函數(shù)色查,
可以像下面這樣寫薯演。
function length(str) {
return [...str].length;
}
length('x\uD83D\uDE80y') // 3
凡是涉及到操作 32 位 Unicode 字符的函數(shù),都有這個問題秧了。因此涣仿,最好都用擴展運算符改寫。
let str = 'x\uD83D\uDE80y';
str.split('').reverse().join('')
// 'y\uDE80\uD83Dx'
[...str].reverse().join('')
// 'y\uD83D\uDE80x'
上面代碼中示惊,如果不用擴展運算符,字符串的reverse操作就不正確愉镰。
( 5 )實現(xiàn)了 Iterator 接口的對象
任何 Iterator 接口的對象米罚,都可以用擴展運算符轉(zhuǎn)為真正的數(shù)組。
var nodeList = document.querySelectorAll('div');
var array = [...nodeList];
上面代碼中丈探,querySelectorAll方法返回的是一個nodeList對象录择。它不是數(shù)組,而是一個類似數(shù)組的對象碗降。這時隘竭,擴展運算符可以將其轉(zhuǎn)為真正的數(shù)
組,原因就在于NodeList對象實現(xiàn)了 Iterator 接口讼渊。
對于那些沒有部署 Iterator 接口的類似數(shù)組的對象动看,擴展運算符就無法將其轉(zhuǎn)為真正的數(shù)組。
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
上面代碼中爪幻,arrayLike是一個類似數(shù)組的對象菱皆,但是沒有部署 Iterator 接口须误,擴展運算符就會報錯。這時仇轻,可以改為使用Array.from方法
將arrayLike轉(zhuǎn)為真正的數(shù)組京痢。
( 6 ) Map 和 Set 結(jié)構(gòu), Generator 函數(shù)
擴展運算符內(nèi)部調(diào)用的是數(shù)據(jù)結(jié)構(gòu)的 Iterator 接口篷店,因此只要具有 Iterator 接口的對象祭椰,都可以使用擴展運算符,比如 Map 結(jié)構(gòu)疲陕。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
Generator 函數(shù)運行后方淤,返回一個遍歷器對象,因此也可以使用擴展運算符鸭轮。
var go = function*(){
yield 1;
yield 2;
yield 3;
};
[...go()] // [1, 2, 3]
上面代碼中臣淤,變量go是一個 Generator 函數(shù),執(zhí)行后返回的是一個遍歷器對象窃爷,對這個遍歷器對象執(zhí)行擴展運算符邑蒋,就會將內(nèi)部遍歷得到的值,轉(zhuǎn)為一
個數(shù)組按厘。
如果對沒有iterator接口的對象医吊,使用擴展運算符,將會報錯逮京。
var obj = {a: 1, b: 2};
let arr = [...obj]; // TypeError: Cannot spread non-iterable object