扩展运算符
扩展运算符是(…),它好比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>]
函数调用中使用扩展运算符
let arr = [10];
function foo (array, ...items) {
array.push(...items);
}
foo(arr, ...[15, 30, 50]);
// [10, 15, 30, 50]
function bar (x, y) {
return x + y;
}
bar(...[6, 6])
// 12
上面两个方法都使用了扩展运算符,扩展运算符会将数组转换为参数序列
扩展运算符与正常函数的参数结合使用
function test (v, b, n, m, c) {
return [v, b, n, m, c]
}
let arr = [10, 5];
test(2, ...arr, 9, ...[7]);
// [2, 10, 5, 9, 7]
扩展运算符的特殊写法
let x = 1;
let arr = ...[*x > 0 ? ['foo'] : []), ['bar']]
// ['foo'], ['bar']
let arr1 = [...[], 1]
// [1]
只有函数调用时,扩展运算符才可以放在括号中
console.log(...[1, 2]);
// 1, 2
(...[1, 2, 3])
// error
console.log((...[1, 2, 3, 4, 5]))
扩展运算符可以把数组转为函数参数的缘故,使得apply方法不是首要选择了
function foo (x, y, z) {
return [x, y, z]
}
let args = [1, 3, 5];
foo.apply(null, args);
// [1, 3, 5]
function bar (x, y, z) {
return [x, y, z]
}
let args = [1, 3, 5];
bar(...args);
// [1, 3, 5]
扩展运算符的应用
(1)复制数组
数组是符合的数据结构,直接复制的话,只是复制了指向数据结构的指针,而不是克隆一个全新的数组
let arr = [1, 2];
let arr1;
arr1 = arr;
arr1[0] = 2;
arr;
// [2, 2]
上面的方式,arr1拿的只是arr的引用。
arr1和arr指向同一堆,所以arr1变,arr也会变。
let arr = [1, 2];
let arr1 = arr.concat();
arr1[0] = 2'
arr
// [1, 2]
上面是使用克隆方法来创建新数组
使用扩展克隆数组
let arr = [1, 2];
// one
let arr1 = [...arr];
// [1, 2]
arr1[0] = 2;
arr
// [1, 2]
// two
let [...arr1] = arr;
第二种显得不那么语义化,但以上两种arr1都克隆了arr
(2)合并数组
// es5
let arr = [1, 2];
let arr1 = [3, 4];
let arr2 = [5, 7];
arr.concat(arr1, arr2);
// [1, 2, 3, 4, 5, 7]
// es6
[...arr, ... arr1, ...arr2]
// [1, 2, 3, 4, 5, 7]
let a1 = [2, 6];
let a2 = [3, 9];
let a3 = a1.concat(a2);
// [2, 6, 3, 9]
let a4 = [...a1, ...a2];
// [2, 6, 3, 9]
以上都是浅拷贝,指向的全部为引入地址,如果原地址内容发生改变,新变量也会跟着一起变动
(3)扩展运算符与解构赋值结合
let [foo, ...bar] = [1, 3, 5, 6, 7];
foo // 1
bar // [3, 5, 6, 7]
let [foo, ...bar] = []
foo // undefined
bar // []
let [foo, ...bar] = ['foo'];
foo // 'foo'
bar // []
const [first = 'first', ...rest] = [];
first // 'first'
rest // []
const [first = 'first', ...rest = [1, 3, 5, 7]] = [];
// error
// 扩展运算符不能使用默认值
扩展运算符用于数组赋值,必须在末尾,否则报错。
let [bar, ...foo, baz] = [1, 3, 5, 6, 7];
// error
let [...bar, foo] = [1, 3, 4, 5 ,6];
// error
仔细想一下,扩展会把所有剩余数组项都包括在内,所以后面不能有变量了
(4)字符串
...['hello'];
// ['h', 'e', 'l', 'l', 'o']
其实就是使用的Iterator遍历器.
(5)实现了Iterator接口的对象
任何定义了遍历器(Iterator)接口的对象,都可以使用扩展运算符转换为真正的数组
let nodeList = document.querySelectorAll('div')
let arr = [...nodeList];
// ['div', 'div' ....];
上面nodeList是一个类数组,此时使用扩展运算符们就可以把它转换为真正的数组
对于数值,字符串,数组这些都部署了Iterator遍历器,自然使用扩展运算符可以实现遍历。
但是对象就无法实现了,因为对象身上没有部署Iterator遍历器
let obj = {
0: 'BMW',
1: 'male',
2: 'fanfan',
length: 3
}
let newObj = [...obj];
// error
但你可以使用Array.from 或者 手动为改实例添加Iterator遍历器,后续再说
(6)Map,Set解构, Generator函数
只要内部部署了Iterator遍历器,就可以使用扩展运算符把参数转为数组,换句话说扩展运算符就是调用的实例本身的Iterator遍历器
let map = new Map();
map.set([1, 'first']);
map.set([2, 'last']);
let newMap = [...map.keys()];
// [1, 2, 3]
Generatror 函数在运行结束的时候会返回一个遍历器对象,因此你可以使用扩展运算符
let g = function * () {
yield 1;
yield 2;
yield 3;
}
[...g()]
// [1, 2, 3]
let obj = {car: 'BMW', wife: 'fanfan'};
[...obj];
// error
由于对象没有部署Iterator所以报错了
Array.from
Array.from方法用于将类数组对象和可遍历对象转为真正的数组
类数组转为真正数组
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
// slice
let arr1 = [].slice.call(arrayLike) // [a, b, c]
// Array.from
let arr2 = Array.from(arrayLike) // [a, b, c]
只要部署了遍历器对象的数据类型,都能转为数组
Array.from('hello');
// [h, e, l, l, o]
Array.from(new Map([1, 'first'], [2, 'last']));
// [[1, 'first'], [2, 'last']]
扩展运算符其调用的就是Iterator遍历器对象,如果一个对象没有部署这个接口,那么就转换不了。
Array.from方法还支持类似数组的对象。所谓类似数组的对象,即必须拥有length属性
Array.from({length: 3});
// [undefined, undefined, undefined]
对于不兼容Array.from的浏览器可以使用slice兼容
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)();
toArray({length : 3})
Array.from参数的第二个参数类似于过滤,可以把转换成功的数组每一个位拿来处理
Array.from([1, 3, 5], (x) => x * x);
// 1, 9, 25
Array.from([1, 3, 5], x => x * x);
// 等同于
Array.from([1, 3, 5].map(x => x * x));
例子
let spanText = docuemnt.querySelector('span');
// map
Array.prototype.map.call(spanText, x => x.textContent)
// from
Array.from(spanText, x => x.textContent)
数组中boolean为false转为0
Array.from([1, 3, 5, 7, , 10], x => x || 0);
返回多个数据类型
function typeOf () {
Array.from(arguments, x => typeof x)
}
typeOf(null, undefined, false, '', 123, [])
Array.from第三个参数用来绑定this
Array.from可以将任意值转为数组,并且提供map方法,这样一来,可以先转换数组,再对每一个值进行赋值或者替换
let arr = Array.from({length : 3}, x => 'super');
// [super, super, super]
Array.from 还可以用于字符串。
主要用来判断当前unicode字符,是否大于\uFFFF的Unicode字符,算作两个字符的bug
function countString (string) {
return Array.from(string).length
}