ES6 引入 rest 参数(...变量名),用于获取函数的多余参数。rest 参数对应的是一个数组,成员依次是函数的多余参数。
function f (...args) {
console.log(args instanceof Array);
console.log(args);
}
f(1,2,3);
// true
// [ 1, 2, 3 ]
它用于接收剩余参数。因此,它只能是函数最后一个参数,且一个函数中最多只能出现一个。
function f(...args, a){}
// SyntaxError: Rest parameter must be last formal parameter
function f(...args, ...args1){}
// SyntaxError: Rest parameter must be last formal parameter
已知,函数体内部有arguments对象,可以在函数体内部读取所有参数。为什么还需要rest参数?
因为arguments对象不是数组,而是一个类似数组的对象。
rest 参数是一个真正的数组,数组特有的方法都可以使用。
如果一个对象的所有键名都是正整数或零,并且有length属性,那么这个对象就很像数组,语法上称为“类似数组的对象”。类数组并不是数组,它们不具备数组特有的方法。
“类数组的对象”的根本特征,就是具有length属性。只要有length属性,就可以认为这个对象类似于数组。但这种length属性不是动态值,不会随着成员的变化而变化。
let obj = {
length: 0
};
obj[3] = 'd';
obj.length // 0
典型的类数组对象:
类数组对象想要使用数组方法,一般是两种方式
方法一:for循环
// arguments对象
function f () {
let args = [];
for (var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
return args
}
f(1,2,3); // [1, 2, 3]
方法二:数组的slice方法
以下两种方式都可行
// arrayLike 对象
let arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Array.prototype.slice.call(arrayLike) // [ 'a', 'b', 'c' ]
[].slice.call(arrayLike) // [ 'a', 'b', 'c' ]
// String
[].slice.call('abc') // [ 'a', 'b', 'c' ]
方法三:Array.from(arrayLike)
// arrayLike 对象
let arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Array.from(arrayLike) // ['a', 'b', 'c']
// String
Array.from('abc') // ['a', 'b', 'c']
方法四:扩展运算符...(对象必须具备 Iterator 接口,即具有Symbol.iterator属性)
// arguments
function f () {
console.log(Symbol.iterator in arguments)
return [...arguments]
}
f(1, 2, 3)
// true
// [1, 2, 3]
// String
Symbol.iterator in String.prototype // true
[...'abc'] // ['a', 'b', 'c']
// DOM NodeList 对象
Symbol.iterator in NodeList.prototype // true
[...document.querySelectorAll('div')] // 数组
// arrayLike 对象不支持
let arrayLike = {
0: 'a',
1: 'b',
2: 'c',
length: 3
};
Symbol.iterator in arrayLike // false
[...arrayLike] // TypeError: arrayLike is not iterable
// call()
// forEach 方法
function logArgs() {
Array.prototype.forEach.call(arguments, function (elem, i) {
console.log(i + '. ' + elem);
});
}
logArgs('a', 'b', 'c')
// 0. a
// 1. b
// 2. c
// map方法
Array.prototype.map.call('abc', function (item) {
console.log(item);
});
// a
// b
// c
// apply()
// forEach 方法
function logArgs() {
Array.prototype.forEach.apply(arguments, [function (elem, i) {
console.log(i + '. ' + elem);
}]);
}
logArgs('a', 'b', 'c')
// 0. a
// 1. b
// 2. c
// map方法
Array.prototype.map.apply('abc', [function (item) {
console.log(item);
}]);
// a
// b
// c
通过call/apply借用数组方法的方式,比直接使用数组原生的forEach要慢。
所以更优解是把类数组对象变成真正的数组。
思考:
var obj = {
2: 'a',
3: 'd',
length: 2,
push: Array.prototype.push
};
obj.push('b');
obj.push('c');
obj // 结果是什么?
The end.
参考链接: