1.扩展运算符
含义
扩展运算符是三个点“…”,如图rest参数的逆运算,能将数组转换为用逗号分隔的参数序列;该运算符主要用于函数调用:
// 用于函数调用
function fn1(x,y) {
console.log(x + y);
}
const numbers = [1,2];
fn1(...numbers);// 3
可以和正常参数结合使用:
// 可以和正常的函数参数结合使用
function fn2(v,w,x,y,z){
console.log(v,w,x,y,z);
}
const array = [1,2,3,4,5];
fn2(...array); // 1 2 3 4 5
扩展运算符后面可以放置表达式:
// 扩展运算符后面也可以放置表达式
let x = 1;
const arr = [...(x > 0 ? ['a'] : ['b'])];
console.log(arr); // ['a'];
如果扩展运算符后面是一个空数组,此时不产生任何效果;
可以替代数组的apply方法
由于扩展运算符可以展开数组,所以不再需要apply方法将数组转化为函数的参数:
// ES5写法
function f1(x,y,z) {};
const aa = [1,2,3];
f1.apply(null,aa); // 需要通过apply方法调用函数
// ES6写法
function f2(x,y,z) {};
const bb = [4,5,6];
f2(...bb);
扩展运算符的应用
1.合并数组
// 1.合并数组
const arr1 = [1,2,3];
const arr2 = [4,5,6];
const arr3 = [7,8,9];
// ES5写法
let res1 = arr1.concat(arr2,arr3); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
// ES6
let res2 = [...arr1,...arr2,...arr3];
2.复制数组:数组属于复杂数据类型,直接复制的话只是复制了指向底层数据结构的指针,而不是复制产生全新的数组;
const arr4 = [1,2];
const arr5 = arr4;
arr5[0] = 2;
console.log(arr4); // [2,2] 直接复制不产生新数组
// 扩展运算符实现复制
// 方法1
const arr6 = [...arr4];
arr6[0] = 2;
console.log(arr4); // [1,2] arr6 = [1,2]
// 方法2
const [...arr7] = arr4;
console.log(arr7); // [1,2]
3.与解构赋值结合:可以和解构赋值结合,用于生成数组;
将扩展运算符用于解构赋值时,只能将其放在参数的最后一位,否则会报错;
const [first,...rest] = [1,2,3,4,5,6];
console.log(first); // 1
console.log(rest); // [2,3,4,5,6];
4.字符串
扩展运算符还可以将字符串转化为真正的数组:
const str1 = [...'hello'];
console.log(str1); // ["h", "e", "l", "l", "o"]
使用扩展字符转换字符串还有一个好处就是能够正确识别32位的Unicode字符:
'x\uD83D\uDE80y'.length // 4
[...'x\uD83D\uDE80y'].length // 3
凡是涉及32位unicode字符的操作都会存在这样的问题,最好使用扩展运算符改写:
5.将实现了Iterator接口的对象转换为数组
可以将实现了Iterator接口的伪数组转换为真数组:
<div></div>
<div></div>
<div></div>
<script>
let nodeList = document.querySelectorAll("div");
let nodeArr = [...nodeList];
console.log(nodeArr);
</script>
对于没有实现Iterator接口的伪数组,无法使用扩展运算符将其转换为真数组,但是可以使用Array.from()方法转换:
// 自定义一个伪数组
const arrayLike = {
0 : "peanut",
1 : 33,
length : 2
}
let r = [...arrayLike]; // object is not iterable
// 此时可以使用Array.from方法进行转换
let rr = Array.from(arrayLike);
console.log(rr); // ["peanut", 33]
6.小结
扩展运算符内部调用的是数据结构的Iterator接口,因此只要是具有Iterator接口的对象,都可以使用扩展运算符,比如Map、Set结构,Generator函数…对于没有Iterator接口的对象,使用扩展运算符会报错;
2.Array.from()
作用:用于将两类对象转换为真正的数组:伪数组、可遍历的对象(包括Map和Set结构);语法:
/*
obj:要转换的对象
callback:对每一个元素进行处理的回调函数,类似于map方法的回调函数,将处理后的值放入返回的数组中
*/
Array.from(obj[,callback]);
只要是部署了Iterator接口的对象,该方法都能将其转换为数组;扩展运算符将对象转换为数组的前提是对象需要由Iterator接口;Array.from()将对象转换为数组的前提是对象具有length属性且属性名为数值(仅仅具有length属性而不包含其他属性的对象除外);
// Array.from方法可以将具有length属性的对象转换为数组
const arrLike = {
0 : "peanut",
1 : "study",
length : 2,
}
// 传入第二个参数,返回函数处理后的新数组
let res1 = Array.from(arrLike,(value,index) => {
return (index + " " + value);
})
console.log(res1); // ["0 peanut", "1 study"]
如果map函数中使用到this关键字,这时可以传入第三个参数用于绑定this指向;
应用
Array.from({length : n},()=>{}):只要有一个原始的数据结构,就可以对它的值进行处理,转成规范的数组结构,就可以使用数组的所有方法;这里n是回调函数执行的次数,回调函数的返回值会变成数组中的元素;
let res3 = Array.from({length : 3},()=> 'peanut');
console.log(res3); // ["peanut", "peanut", "peanut"]
将字符串转换为数组,然后返回字符串长度;
因为该方法能识别Unicode字符,可以避免大于0xFFFF的字符被识别为2个字符的bug;
function countStrLength(string){
return Array.from(string).length;
}
3.Array.of()
作用:将一组数值转换为数组;Array对象由于参数原因可能会出现如下的情况:
// Array.of()可以弥补Array对象以下的几个缺陷
// Array对象创建数组:
const arr1 = new Array(); // []
const arr2 = new Array(3); // [空 × 3] 一个参数时其实也只能说是指定了数组的长度
const arr3 = new Array(1,2); // [1, 2]
使用Array.of()能解决该问题:
// 使用Array.of不会出现这样的问题
const arr4 = Array.of(); // []
const arr5 = Array.of(3); // [3]
const arr6 = Array.of(1,2); // [1,2]
Array.of()基本上可以代替Array和new Array,它的行为非常统一,不穿传入多少数值参数都会转换为数组返回;Array.of()方法的代码模拟实现:
function ArrayOf(){
return [].splice.call(arguments); // slice:返回从指定位置到指定结尾截取的数组,不指定参数时返回原数组
}
4.Array.prototype.copyWithin():内部元素复制并替换方法
作用:在当前数组内部将指定位置的成员复制到数组的其他位置(覆盖原有元素),并返回当前的数组;语法:
/*
target(必选):从该位置开始替换元素
start:从该位置开始读取要复制的元素,默认0,负数表示倒着数
end:到该位置停止读取元素,默认等于数组长度,负数表示倒数
*/
Array.prototype.copyWithin(target[,start[,end]]);
// Array.prototype.copyWithin(target[,start[,end]]);
const arr1 = [1,2,3,4,5,6]
// 将第4位开始的元素复制到从0开始的位置
let res1 = arr1.copyWithin(0,4); // [5, 6, 3, 4, 5, 6]
// 参数也可以是负数
let res2 = arr1.copyWithin(0,-1,-2); // [5, 6, 3, 4, 5, 6]
5.Array.prototype.find()和Array.prototype.findIndex():查找元素
Array.prototype.find()方法
作用:找出第一个符合条件(符合回调函数中的条件)的数组成员,回调函数返回true时返回传入回调函数中的那个元素;找到则返回该元素,未找到返回undefined;语法:
/*
callback:在数组每一项上执行的函数,接受三个参数
value:当前的值
index:当前的位置
arr:原数组
thisArg:指定执行回调函数时的this对象
*/
array.find(callback[,thisArg]);
Array.prototype.findIndex()方法
作用:返回满足条件的第一个元素的位置;找到则返回该元素的位置,未找到返回-1;语法:
/*
callback:在数组每一项上执行的函数,接受三个参数
value:当前的值
index:当前的位置
arr:原数组
thisArg:指定执行回调函数时的this对象
*/
array.findIndex(callback[,thisArg]);
代码示例:
// 1.find方法,找出第一个符合条件的数组元素
const arr1 = [1,0,2,5,-5,-6];
// 找出小于0的元素
let res1 = arr1.find(value => {
return value < 0;
})
// 2.findIndex() 与find类似,所有元素都不符合条件时返回-1
const arr2 = [1,2,3];
// 找到等于2的元素
let res2 = arr2.findIndex(value => value === 2); // 1 返回符合条件的元素的位置
// 找出数组中的第一个负数
let res3 = arr2.findIndex(value => value < 0); // -1 没找到返回-1
与indexOf()比较:
find():返回的是元素本身,而不是元素位置,未找到返回undefined,而不是-1;findIndex():与indexOf()类似,但是findIndex()能识别数组的NaN元素;
相比于indexOf()的优点
这两个方法能识别数组中的NaN元素,indexOf()无法做到这一点:
在查找NaN元素时,需要借助 Object.is(arg1,arg2)方法(该方法用于判断两个参数是否相等);
// 相比于indexOf的优点:能识别NaN元素
const arr3 = [NaN];
// 数组的indexOf方法
let res4 = arr3.indexOf(NaN); // -1 未找到 但是数组中存在NaN元素
// find和findIndex方法都能找到NaN元素
let res5 = arr3.find(value => Object.is(NaN,value)); // NaN
let res6 = arr3.findIndex(value => Object.is(NaN,value)); // 0
Array.prototype.fill()
作用:用指定的值填充一个数组;数组被填充位置的原先的元素都会被抹去;返回的是修改后的数组;语法:
/*
value:用于填充用的值
start:填充开始的位置,默认值为0
end:填充结束的位置(不包括该位置的元素),默认值为数组长度
*/
arr.fill(value[,start[,end]]);
fill()方法对于初始化空数组十分方便;代码示例:
// 只有一个参数 不指定填充位置时 数组中所有元素都被替换为指定的元素
const arr1 = [1,2,3];
let res1 = arr1.fill(7); // [7, 7, 7]
// 指定填充的位置
let res2 = arr1.fill(6,1,2); // [7, 6, 7]
7.遍历数组的新方法:entries()、keys()、values()
作用:都用于遍历数组,返回一个遍历器对象,可用for…of遍历;唯一的不同:entries()==>遍历键值对、keys()==>遍历键、values()==>遍历值,对应保存的内容分别为键值对、键、值;代码示例:
// 1.entries方法:遍历键值对
const arr1 = ["peamut","coder"];
for(let [key,value] of arr1.entri
console.log(key,value);
}; // 0 "peamut" 1 "coder"
// 2.keys()方法 遍历键
for(let key of arr1.keys()){
console.log(key);
} // 0 1
// 3.values()遍历值
for(let value of arr1.values()){
console.log(value);
} // peamut coder
如果不使用循环,也可以手动调用遍历器提供的next方法手动遍历元素;
// 使用遍历器提供的next方法遍历元素
let person = arr1.entries(); // 将返回的遍历器赋值给person
let res1 = person.next().value; // [0, "peamut"]
8.Array.prototype.includes()
作用:检测某个给定的值是否包含在数组中,返回布尔值,与字符串的includes()方法类似;语法:
/*
value:要查找的值
formIndex:查找开始的位置,可以是负值,如果负数表示的长度大于数组长度,则从0开始查找
*/
arr.prototype.includes(value[,fromIndex]);
代码示例:
// includes方法 检测给定值是否包含于数组中 返回布尔值
const arr1 = [1,2,NaN,4];
let res1 = arr1.includes(NaN); // true
// 可以指定开始查找的位置
let res2 = arr1.includes(NaN,3); // false 从第三位开始 所以找不到啦啦啦
在该方法之前,使用indexOf()检查数组是否包含某个值,但是indexOf()方法有两个缺点:
不够语义化,indexOf的返回值需要判断是否等于-1才能确定其是否包含;
indexOf()方法内部使用"==="进行判断,对NaN判断结果不准确;
Map和Set数据结构还有一个has()方法,需要与includes()方法区分:
Map的has方法用于检查是否包含指定的键;Set的has方法用于检查是否包含指定的值;
数组的空位
数组的空位是指数组的某一个位置没有任何值;空位是没有任何值的,undefined也算有值;
let arr1 = new Array(3); // [,,] 3个空位
let arr2 = [undefined,undefined,undefined]; // undefined不算空位
ES5数组方法对空位的处理方式:
foreach()、filter()、every()和some()都会跳过空位;map()会跳过空位,但会保留这个值;jion()和toString()会将空位视为undefined,将undefined和null视为空字符串;
ES6:明确将空位转为undefined;
Array.from():将数组的空位转为undefined;扩展运算符:转为undefined;copyWithin():连同空位一起复制;fill():将空位视为正常的数组位置;for…of:会遍历空位;