本文只是做为ES6入门第九章学习笔记,在整理知识点的同时,会加入部分我的思考与解答,若想知道更详细的介绍,还请阅读阮一峰大神的ES6入门html
1、拓展运算符node
ES6中新增了拓展运算(...)三个点,它的做用是将一个数组或实现了Iterator接口的对象/类数组(nodeList,arguments)转为分隔的参数序列。es6
console.log(...['echo', '听风是风', '时间跳跃']); //echo 听风是风 时间跳跃
类数组arguments使用拓展运算符:数组
let fn = function () { console.log(...arguments); }; fn(1, 2, 3, 4); //1,2,3,4
数组拓展运算符与函数rest参数的区别:app
比较巧的是,其实在ES6入门第八章函数拓展中,也有用三个小点且用于取代arguments的rest参数的rest参数。为了不混淆二者,咱们作个简单区分。函数
let fn = function (...rest) { rest.forEach(ele => console.log(ele)); }; fn('听风是风', ...[1, 2, 3]); //听风是风 1 2 3 //等同于 fn('听风是风', 1, 2, 3);
rest参数做用是取代arguments,用于函数参数上,在函数内部使用rest参数时不须要带上...;数组拓展运算符更多用于函数调用上,且...必须带上。学习
//rest参数 let fn = function (...rest) { //使用时...不须要带上 rest.forEach(ele => console.log(ele)); }; //数组拓展运算符 let arr = [1,2,3]; //使用时...永不离身 Math.max(...arr);//3
rest参数必须写在函数参数末尾,不然标错;数组拓展运算符在函数调用时能够出如今任意位置,但须要注意的是只有函数调用时才能放在圆括号中,不然报错。this
//错误用法,...rest只能出如今参数末尾 let fn = function (name, ...rest, age) {}; //错误用法,除非是函数调用,不然不能写在花括号中 (...[1,2,3]);
2、拓展运算符的做用spa
1.取代apply方法prototype
实际开发中咱们经常须要经过apply或者call方法来调用某方法,但若是调用时参数是一个数组,就不得不使用apply,由于apply能将数组解析给单个参数传递给函数。
而拓展运算符恰好就具备将数组,类数组转为单个元素序列的功能,所以能很好的取代apply。好比函数应用:
let fn = (x, y, z) => console.log(x + y + z), arr = [1, 2, 3]; //函数应用 //ES5 fn.apply(null, arr); //6 // ES6 fn(...arr); //6
还记得经常使用的数组取最大,最小值的方法吗。Math.max一样可以简写:
let arr = [1, 2, 3, 4, 5]; //ES5 Math.max.apply(null, arr); //5 Math.min.apply(null, arr); //1 //ES6 Math.max(...arr); //5 Math.min(...arr); //1
将一个数组的元素压入到另外一个数组尾部(我以前都没想过用这个作法...):
let arr1 = [1, 2, 3], arr2 = [4, 5, 6]; //ES5 Array.prototype.push.apply(arr1, arr2); //ES6 arr1.push(...arr2);
2.合并/复制数组:
let arr1 = [1, 2, 3], arr2 = [4, 5, 6], arr3 = [7, 8, 9]; //ES5 let arr = arr1.concat(arr2, arr3); //ES6 let arr = [...arr1, ...arr2, ...arr3];
顺带一提,concat方法不会修改原数组,而是复制数组中的元素组成并返回一个新数组,concat和拓展运算符合并数组的操做都是浅拷贝。
你确定会想,不是说concat是浅拷贝吗,为啥我下面的修改合并的新数组没跟这变化:
let arr1 = [1, 2, 3], arr2 = [4, 5, 6]; //ES5 let arr = arr1.concat(arr2); arr1.push(10); console.log(arr); //[1,2,3,4,5,6]
这是由于concat虽然操做的是数组,可是复制添加的不是整个数组,而是数组中的单个元素们,上述数组中的元素均为number类型,属于基础类型。
let arr1 = [{a:1}, {a:2}], arr2 = [{c:3}, {d:4}]; //ES5 let arr = arr1.concat(arr2); arr1[0]['a'] = 5; console.log(arr); //[{a:5}, {a:2},{c:3}, {d:4}]
那咱们将数组中的单个元素改成对象试试,能够看到修改arr1时会对arr数组形成影响,拓展运算符合并数组同为浅拷贝也是这个道理。
ES6入门这本书中也提到使用拓展运算符复制数组的操做,一样须要注意当元素是复杂数组类型会存在浅拷贝的问题。
let arr = [1,2,3,4]; //如下两种写法复制数组等效 arr1 = [...arr]; [...arr1] = arr;
3.结合解构赋值实现快速赋值
我在ES6入门笔记第二篇中有记录解构赋值的使用,解构赋值一样能与数组拓展运算符结合使用,达到快速拆分数组分开赋值的目的。
let arr = [1, 2, 3, 4]; [a, ...b] = arr; a//1 b//[2,3,4]
对应位置去看,其实就是将数组第一位给了a,剩下的元素打包给了b,不难理解。
下面是数组元素不足,或者为空时使用解构赋值的状况:
let arr = []; [a, ...b] = arr; a //undefined b //[] let arr1 = ['听风是风']; [a1, ...b1] = arr1; a1 //'听风是风' b1 //[]
最后须要注意的一点是,若是结合解构赋值使用,拓展运算符必须写在参数最后一位,不然会报错。还记得吗,...rest参数也是必须写在函数参数最后一位。
let arr = []; [...a, b] = arr; //报错
4.类数组转数组
在前面已经有提过,类数组(例如nodeList,arguments)结合拓展运算符能快速转为数组:
let P = document.querySelectorAll('p'); //获取页面全部p元素并修改文本内容 [...P].forEach(ele => ele.innerHTML = '听风是风');
5.将字符串快速转为数组
拓展运算符能够将字符串转为字符串使用
let name = 'echo'; //ES5 let name1 = name.split(''); // ES6 let name1 = [...name]; name1 //['e','c','h','o']
6.实现了Iterator接口的对象都能使用拓展运算符
拓展运算符解析原理其实就是内部调用了目标对象的遍历器接口(Symbol.iterator),只要具有Iterator接口的对象,均可以使用拓展运算符:
let map = new Map([ ['听风是风', 1], ['echo', 2], ['时间跳跃', 3] ]); [...map.keys()] //['听风是风','echo','时间跳跃']
2、Array.from()方法
1.基本用法
Array.from方法主要用途是将类数组(具备length属性)对象,或可遍历的对象转为真正的数组。
咱们在前面已经使用拓展运算符将类数组nodeList和arguments转为了真正的数组,这里Array.from方法一样能作到。你确定会想,既然拓展运算符能够作到,为何还要新增这个方法,由于二者仍是有区别的。
类数组对象是具备length属性的对象,nodeList,和arguments具备length属性的同时,其实还具备Iterator接口,因此拓展运算符才能调用Iterator接口转换。
好比下面两个类数组对象,都具备length属性但不具有iterator接口,Array.from方法能够转为数组,但拓展运算符不能作到:
let arrayLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 }; // ES5 let arr = Array.prototype.slice.call(arrayLike); // ES6 let arr1 = Array.from(arrayLike); arr; //['a','b','c']; arr1; //['a','b','c']; //拓展运算符没法转换不具备iterable接口的对象 [...arrayLike] //报错arrayLike is not iterable
再如只有length属性的类数组对象:
let obj = { length: 3 }; Array.from(obj); //[undefined,undefined,undefined] //一样拓展运算符没法转换 [...obj]; //报错
2.Array.from()方法的第二个参数
Array.from()方法的第二个参数相似与数组的map方法,用于对每一个元素进行处理,并将处理后的结果放入返回的数组。
let arrayLike = { '0': 1, '1': 2, '2': 3, length: 3 }; // ES6 let arr1 = Array.from(arrayLike, ele => ele + 1); //[2,3,4] // ES5 let arr2 = Array.prototype.map.call(arrayLike, ele => ele + 1); //[2,3,4] // 等同于 let arr3 = Array.from(arrayLike).map(ele => ele + 1); //[2,3,4];
好比快速生成包含10个数字1的数组:
let arrayLike = {length: 10};
let arr = Array.from(arrayLike, () => 1);
3、Arrar.of()方法
Array.of()用于将一组值转为为数组,主要目的是弥补构造函数Array()的不足:
对于建立数组,咱们常推荐数组直接量,而非使用数组构造函数建立,由于对于特殊状况会获得你不想要的值:
let arr = [3]; //[3] //构造函数的坑 let arr1 = Array(3); //[undefined,undefined,undefined] //Array.of能够弥补这点 let arr3 = Array.of(3) //[3]
若是不支持此方法,咱们也能够模拟实现:
let ArrayOf = function () { if (Array.of) { return Array.of(...arguments); } else { return [].slice.call(arguments); }; }; let arr = ArrayOf(1, 2, 3); //[1,2,3]
4、数组实例新增API
此方法用于数组复制指定位置开始到指定位置结束的成员到其它位置,修改原数组并返回。
Array.prototype.copyWithin(target, start = 0, end = this.length)
·target(必需):从该位置开始替换数据。若是为负值,表示倒数。·start(可选):从该位置开始读取数据,默认为 0。若是为负值,表示倒数。·end(可选):到该位置前中止读取数据,默认等于数组长度。若是为负值,表示倒数(也就是说复制不包含这一位)
看几个例子:
let arr = [1, 2, 3, 4, 5]; //正数下标 0 1 2 3 4 //负数下标 -5 -4 -3 -2 -1 //从下标2开始到下标3结束,但不包含下标3的元素,因此只有元素3,并复制到下标0处替换原有的元素 [1, 2, 3, 4, 5].copyWithin(0, 2, 3); //[3,2,3,4,5] //从下标-3开始到下标-1结束,但不包含下标-1的元素,因此只有元素3,4,并复制到下标0处替换原有的元素 [1, 2, 3, 4, 5].copyWithin(0, -3, -1); //[3,4,3,4,5]
须要注意的是,在使用copyWidthin方法时,你的start元素位置必须在end元素位置左边,不然此方法将无效:
let arr = [1, 2, 3, 4, 5]; //起始元素均在结束元素右边,不会起做用 arr.copyWithin(0, 3, 2); //[1, 2, 3, 4, 5] arr.copyWithin(0, -1, -3); //[1, 2, 3, 4, 5]
2.find()与findIndex()
find方法在开发中使用频率很高,它能帮你找到第一个符合条件的对象并返回,而且跳出循环,若是未找到则返回undefined。
let arr = [1, 2, 3, 4, 5]; var ele = arr.find(ele => ele > 3); ele //4
find方法的回调函数接受三个参数:
arr.find(function (ele, index, self) {});
·ele表明循环的当前项·index表明当前项的下标·self表明被循环的原数组对象
findIndex用法与find同样,只是它会返回第一个符合条件的元素下标位置,回调函数一样拥有三个参数,用法与find相同,这里就不作介绍了。
3.fill()方法
数组的一个填充方法,接受三个参数,分别表明填充所用元素,填充起始位置,填充结束位置(填充时不包含此位置)。
[1, 2, 3].fill('听风是风', 1, 2); //[1,'听风是风',3] new Array(3).fill('echo'); //['echo','echo','echo']
前面咱们有使用Array.from方法快速生成包含10个1的数组,那么到这里,你又知道了可使用fill快速生成相同元素的数组。
须要注意的是,当咱们使用fill填充的元素是对象时,会存在浅拷贝的问题,也就是说,当你填充后修改数组任意下标的对象,其它下标的元素都会改变
let arr = new Array(3).fill({ name: 'echo' }); arr[0].name = '听风是风'; arr //[{name:'听风是风'},{name:'听风是风'},{name:'听风是风'}]
4.keys(),value()与entries()方法
这三个方法均用于遍历数组,这三个都返回一个遍历器对象,能够结合for...of循环分别对数组键名(keys()),数组键值(value()),数组键值对(entries())进行遍历。
let arr = [{name:'听风是风'},{age:26},{address:'深圳'}]; for(let index of arr.keys()){ console.log(index);//0,1,2 }; for(let ele of arr.values()){ console.log(ele);//{name:'听风是风'},{age:26},{address:'深圳'} }; for(let [index,value] of arr.entries()){ console.log(index,value);//0 {name:'听风是风'},1 {age:26},2 {address:'深圳'} };
请仔细看这个例子,我原觉得keys遍历的是数组元素的name,age,adress,values遍历出来的是听风是风,26与深圳,其实并非。
不要被方法名骗了,keys遍历仅仅是数组的下标,values遍历出来的是单个元素,entries遍历出来的是下标与元素的对。
若是不使用for...of循环,可使用遍历器对象next方法进行遍历,因为我还没看这一章对next不太了解,这里先作个记录。
let arr = ['a','b','c']; let index = arr.keys(); console.log(index.next().value);//0 console.log(index.next().value);//1 console.log(index.next().value);//2
5.includes方法
此方法用于判断数组是否包含某个值,返回一个Boolean值,包含返回true,不包含则返回false。
let arr = [1,2,3]; //ES6 arr.includes(1);//true //ES5 使用indexOf,并判断返回的索引是否大于-1 arr.indexOf(1)//0
在这个方法以前咱们习惯使用indexOf查找,找到指定元素返回该元素索引,找不到返回-1。但须要注意的是,indexOf没法查找NaN这个特例:
let arr = [NaN]; arr.includes(NaN);//true arr.indexOf(NaN)//-1
咱们也可使用some方法本身模拟查找方法,
let contains = (() => Array.prototype.includes ? (arr, value) => arr.includes(value) : (arr, value) => arr.some(el => el === value) )();
6.数组的flat()与flatMap()方法
flat方法能够将多维数组转变为一维数组并返回一个新数组,不会修改原数组,也是很是的骚操做了:
let arr = [1, 2, 3, [4]]; arr.flat(); //[1,2,3,4]
那么要是数组维度超过2层怎么办呢,flat方法接受一个参数,定义该方法要降维的层数:
let arr = [1, 2, 3, [4,[5]]]; arr.flat(2); //[1,2,3,4,5]
若是你不想知道数组嵌套了多少层,不知道这个参数定义多少,你能够直接使用infinite代替,这样无论几层都会被降为一维数组:
let arr = [1,[2,[3,[4,[5]]]]]; arr.flat(Infinity); //[1,2,3,4,5]
flatMap()方法对数组中每一个元素执行一个回调函数(相似map方法),而后使用返回值组成一个新数组,不会修改原数组,最后再对这个新数组执行flat方法。
须要注意的是flatMap方法最多只能展开二维数组:
let arr = [1, [2, [3, [4, [5]]]]]; let arr1 = [1, 2, 3, [4]]; //超过二维就没法解析了 let arr_ = arr.flatMap(ele => ele * 2); //[1,2,3,4,5] //二维数组仍是能够正常计算 let arr1_ = arr1.flatMap(ele => ele * 2); //[1,2,3,4,5] console.log(arr_); //[2,NaN] console.log(arr1_); //[2,4,6,8] //返回的新数组确实还会被flat方法降维一次 let arr3 = [1, 2, 3]; let arr3_ = arr3.flatMap(ele => [ele * 2]); console.log(arr3_); //[2,4,6]
flatMap回调函数一样接受三个参数,与前面find方法参数意义相同。