拓展运算符
1.由三个点组成(...),将一个数组转为用逗号分隔的参数序列
2.扩展运算符后面还可以放置表达式,貌似仅限数组中使用
const arr = [
...(x > 0 ? ['a'] : []),
'b',
];
3.后面是空数组,不产生任何效果。
4.扩展运算符如果单独放在括号中,JavaScript 引擎就会认为这是函数调用。如果这时不是函数调用,就会报错。
//这种情况:
(...[1])
替代函数的 apply 方法
1.不再需要apply()将数组转化为函数参数:
function f(a,b){}
let arr = [0,1];
//ES5
f.apply(null,arr)
//ES6
f(...arr)
2.数组Math.max方法:
Math.max(...[1,2,3])
3.数组push数组:
let arr1 = [1,2];
let arr2 = [3,4];
arr1.push(...arr2); //[1,2,3,4]
4.用数组创建Date实例:
new Date(...[2015, 1, 1]); //Sun Feb 01 2015 00:00:00 GMT+0800 (中国标准时间)
应用
1.复制数组:(数组时复合数据类型,直接复制只是复制了指针 -- 修改克隆数组会导致原数组的改变)
在ES5中使用concat()方法,什么是concat方法?
示例:
let a = [1,2];
let cloneA = a.concat(); //返回a的副本
cloneA[0] = 2;
a; //[1,2]
ES6拓展写法:
let a = [1,2];
//写法1
let cloneA = [...a];
//写法2
let [...a] = cloneA;
2.合并数组:
[...arr1 , ...arr2 , ...arr3]
它属于浅拷贝(如果修改了原数组的成员,会同步反映到新数组)
let a1 = [{name:"jack"}];
let a2 = [{age:20}];
let ES6 = [...a1 , ...a2];
let ES5 = a1.concat(a2);
ES6[0].name; //jack
ES5[0].name; //jack
a1[0].name = 'tom';
console.log(ES5[0].name) //tom
console.log(ES6[0].name) //tom
3.与解构赋值相结合(用于生成数组)
var list = [1,2,3,45]
ES5:
let a= list[0],
rest = list.slice(1);
console.log(a,rest); // 1 , [2,3,45]
ES6:
let [b,...restb] = list;
console.log(b ,restb) // 1 , [2,3,45]
拓展运算符,只能放在参数的最后一位,否则会报错
let [...arr , last ] = [ 1, 2 , 3 , 4 ]; //error
4.可以将字符串转为数组
[..."hello"] //['h','e','l','l','o']
能够正确识别四个字节的 Unicode 字符,涉及到操作四个字节的 Unicode 字符的函数,最好都用扩展运算符改写。
5.实现Iterator接口的对象,只要定义了遍历器接口的对象都可以转为真正数组:
let arr = [...nodeList]
对于没有部署遍历器的类数组对象,可以使用Array.from转换为数组:
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
};
6.Map,Set解构,Generator函数同上
如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。
Array.from() - 将两类对象转为真正的数组(类似数组的对象,可遍历对象)
0.类数组对象的本质特征:具有length属性。
1.如果参数是一个真正的数组,Array.from
会返回一个一模一样的新数组。
2.Array.from
可以接受第二个参数,作用类似于数组的map
方法,用来对每个元素进行处理,将处理后的值放入返回的数组。
Array.from([1,2,3], (x) => x * x); //[1,4,9]
技巧:
1、提取DOM结点内容:
let spans = document.querySelectorAll('span.name');
// Array.from()
let names2 = Array.from(spans, s => s.textContent)
2、将数组中空位填成0:
Array.from([ 1, ,2 , , 3] , (n) => n || 0 );
//[1,0,2,0,3]
3.若map
函数里面用到了this
关键字,还可以传入Array.from
的第三个参数,用来绑定this
4.只要有一个原始的数据结构,你就可以先对它的值进行处理,然后转成规范的数组结构,进而就可以使用数量众多的数组方法。
Array.from( { length : 2 } , () => 'jack' ); //['jack' , 'jack']
5.字符串转为数组,然后返回字符串的长度。因为它能正确处理各种 Unicode 字符,可以避免 JavaScript 将大于\uFFFF
的 Unicode 字符,算作两个字符的 bug。
function countSymbols(string) {
return Array.from(string).length;
}
Array.of() - 将一组值,转换为数组
1.可以用来替代Array()
或new Array()
,并且不存在由于参数不同而导致的重载。它的行为非常统一。
Array.of() // []
Array.of(undefined) // [undefined]
Array.of(1) // [1]
Array.of(1, 2) // [1, 2]
2.Array.of
总是返回参数值组成的数组。如果没有参数,就返回一个空数组。
数组实例的coplyWithin() - 使用这个方法,会修改当前数组
1.用法:
Array.prototype.copyWithin(target, start = 0, end = this.length)
它接受三个参数。
- target(必需):从该位置开始替换数据。如果为负值,表示倒数。
- start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
- end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。
这三个参数都应该是数值,如果不是,会自动转为数值。
let arr = [1, 2, 3, 4, 5, 6, 7];
let arr1 = arr.copyWithin(0, 3)
log(arr1) //[4,5,6,7,5,6,7]
//表示:将 从3号位开始 到 数组结束的成员 [4,5,6,7] 复制到 从零开始的位置
// 将3号位复制到0号位
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
// 将3号位复制到0号位
[].copyWithin.call({length: 5, 3: 1}, 0, 3)
// {0: 1, 3: 1, length: 5}
// 将2号位到数组结束,复制到0号位
let i32a = new Int32Array([1, 2, 3, 4, 5]);
i32a.copyWithin(0, 2);
// Int32Array [3, 4, 5, 4, 5]
// 对于没有部署 TypedArray 的 copyWithin 方法的平台
// 需要采用下面的写法
[].copyWithin.call(new Int32Array([1, 2, 3, 4, 5]), 0, 3, 4);
// Int32Array [4, 2, 3, 4, 5]
数组实例的find()方法和findIndex()
find():它的参数是一个回调函数,所有数组成员依次执行该回调函数,直到找出第一个返回值为true
的成员,然后返回该成员,未成立返回undeifned。
//找出第一个大于9的成员
let arr = [1, 2, 3, 4, 5, 6, 7,10,11];
let arr1 = arr.find((value,index,arr)=>{
return value > 9
})
l(arr1) //10
findIndex,符合条件返回数组成员位置,不符合返回-1
let arr = [1, 2, 3, 4, 5, 6, 7,10,11];
let arr1 = arr.findIndex((value,index,arr)=>{
return value > 9
})
l(arr1) //7
这两个方法都可以接受第二个参数,用来绑定回调函数的this
对象:
let arr = [1, 2, 30];
function f(v) {
return v > this.age;
}
let person = {
name: "Job",
age: 20
};
let a = arr.find(f, person);
a //30
find
函数接收了第二个参数person
对象,回调函数中的this
对象指向person
对象。
这两个方法都可以发现NaN
,弥补了数组的indexOf
方法的不足。
[NaN].indexOf(NaN)
// -1
[NaN].findIndex(y => Object.is(NaN, y))
// 0
fill()方法 - 使用给定值,填充一个数组(修改原数组)
['a','b'].fill(1)
//[ 1 , 1 ]
new Array(3).fill(1);
//[1,1,1]
1.数组中已有的元素,会被全部抹去。
2.可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置。
3.如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。
let arr = new Array(2).fill({name:"jack"});
arr //[{name:"jack"},{name:"jack"}]
arr[0].name = 'tom'
arr //[{name:"tom"},{name:"tom"}]
let arr1 = new Array(2).fill([]);
arr1[0].push(5)
arr1 //[ [5] , [5] , [5] ]
entries(),keys() 和 values()
1.用于遍历数组,都返回一个遍历器对象。
2.可以用for...of
循环进行遍历,唯一的区别是keys()
是对键名的遍历、values()
是对键值的遍历,entries()
是对键值对的遍历。
let arr = [ "a" , "b" ];
for (let i of arr.keys()){
console.log(i); //0 1
}
for(let e of arr.values()){
console.log(e); //a b
}
for(let [i,e] of arr.entries()){
console.log(i,e); //0 "a" 1"b"
}
3.不使用for...of
循环,可以手动调用遍历器对象的next
方法,进行遍历
let arr2 = ['a','b','c'];
let entries = arr2.entries();
entries.next().value; //[0,"a"]
entries.next().value; //[1,"b"]
entries.next().value; //[2,"c"]
let values = arr2.values();
values.next().value; //a
values.next().value; //b
values.next().value; //c
let keys = arr2.keys();
keys.next().value; //0
keys.next().value; //1
keys.next().value; //2
includes() - 返回一个布尔值,表示某个数组是否包含给定的值
0.对象数组无效。
let arr3 = [{name:'jack'},{name:"tom"}];
let obj = {name:'jack'};
let bool = arr3.includes(obj); //false
1.该方法的第二个参数表示搜索的起始位置,默认为0
。
2.如果第二个参数为负数,则表示倒数的位置,如果这时它大于数组长度,则会重置为从0
开始。
3.indexOf使用===,会造成对NaN的误判,includes不会
4.Map 和 Set 数据结构有一个has
方法,需要注意与includes
区分。
- Map 结构的
has
方法,是用来查找键名的,比如Map.prototype.has(key)
、WeakMap.prototype.has(key)
、Reflect.has(target, propertyKey)
。 - Set 结构的
has
方法,是用来查找值的,比如Set.prototype.has(value)
、WeakSet.prototype.has(value)
。
数组实例的 flat(),flatMap()
1.flat()
用于将嵌套的数组变成一维的数组,该方法返回一个新数组,对原数据没有影响。
2.flat()
默认只会将二维数组转换,如果想更深层,在()中写入层数
let arr = [1,2,[3,4]];
arr = arr.flat(); //[1,2,3,4];
let arr1 = [1,2,3,[4,5,[6,7]]];
arr1 = arr1.flat(2); //[1,2,3,4,5,6,7]
3.如果不管有多少层嵌套,都要转成一维数组,可以用Infinity
关键字作为参数。
4.flat()
方法会跳过空位。
5.flatMap()
方法对原数组的每个成员执行一个函数.
// 相当于 [[2, 4], [3, 6], [4, 8]].flat()
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
6.flatMap()
只能展开一层数组。
// 相当于 [[[2]], [[4]], [[6]], [[8]]].flat()
[1, 2, 3, 4].flatMap(x => [[x * 2]])
// [[2], [4], [6], [8]]
7.flatMap()
方法首先使用映射函数映射每个元素,然后将结果压缩成一个新数组。它与 map 和 深度值1的 flat 几乎相同,但 flatMap
通常在合并成一种方法的效率稍微高一些。
8.flatMap()
方法的参数是一个遍历函数,该函数可以接受三个参数,分别是当前数组成员、当前数组成员的位置(从零开始)、原数组。
9.flatMap()
方法还可以有第二个参数,用来绑定遍历函数里面的this
。
注意:
数组的空位
1.数组的某一个位置没有任何值。
var arr = new Array(3);
arr //[, , ,]
2.空位不是undefined
,一个位置的值等于undefined
,依然是有值的,可以用in运算符求证:
in:如果指定的属性在指定的对象或其原型链中,则in
运算符返回true
。
0 in [undefined, undefined, undefined] // true
0 in [, , ,] // false
3.ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。
forEach()
,filter()
,reduce()
,every()
和some()
都会跳过空位。map()
会跳过空位,但会保留这个值join()
和toString()
会将空位视为undefined
,而undefined
和null
会被处理成空字符串。
4.Array.from
方法会将数组的空位,转为undefined。
5.扩展运算符(...
)也会将空位转为undefined
。
6.copyWithin()
会连空位一起拷贝。
7.fill()
会将空位视为正常的数组位置。
8.for...of
循环也会遍历空位。
9.entries()
、keys()
、values()
、find()
和findIndex()
会将空位处理成undefined
。