1. 扩展运算符
...表示,将一个数组转为用逗号分隔的参数序列,如下:
console.log(...[1,2,3]) // 1 2 3 console.log(1, ...[2,3,4], 5) // 1 2 3 4 5 [...document.querySelectorAll('div')] // [ , , ] function add(x, y){ return x + y } const n = [3, 5] add(...n) // 8
扩展运算符可以和正常函数结合使用,如下:
function f(a,b,c,d,e){ console.log(a,b,c,d,e) } const age = [0,1] f(-1,...age,2,...[3]) // -1 0 1 2 3
扩展运算符后面也可以是表达式,如下:
const x = 10 const arr = [ ...(x > 0 ? ['a'] : []), 'b', ] arr // ['a', 'b']
重要:如果扩展运算符后面是一个空数组,将不会有任何效果。另外只有在函数调用的时候扩展函数在放在圆括号之内,其他的则会报错。
替代函数的apply方法
function f(x, y, z){ console.log(x, y, z) } var a = [1,2,4] // ES5 f.apply(null, args) // ES6 f(...a) // Math.max方法 //ES5 Math.max.apply(null, [14, 3, 99]) //ES6 Math.max(...[12, 4, 55]) //等同于 Math.max(12, 4, 55) // push方法的应用 var a = [0,1,2] var b = [3,4,5] //ES5 Array.prototype.push.apply(a,b) // 0,1,2,3,4,5 //ES6 a.push(...b)
扩展运算符的应用
- 复制数组
因为数组是复合的数据类型,直接的复制只是复制数组在堆内存中的指针地址 const a1 = [1,2] const a2 = a1 a2[0] = 3 a1 // [3,2] // ES5 通过变通方法来复制 const a1 = [1,2] const a2 = a1.concat() a2[0] = 23 a1 // [1,2] // ES6写法 const b1 = [1,3] const b2 = [...b1] or [...b2] = b1
- 合并数组
const a1 = ['b', 'c'] const a2 = ['d'] const a3 = ['x', 'y'] // ES5中合并数组 a1.concat(a2, a3) // ES6中合并数组 [...a1, ...a2, ...a3] //以上两种都是浅拷贝,修改原数组和同步新数组
- 与解构赋值一起使用,扩展只能放在最后一位,不然会报错
// ES5 a = list[0], rest = list.slice(1) // ES6 [a,...rest] = list // 其他 const [a,...c] = [1,2,4,5,4,6] // a 1 c 2,4,5,4,6 const [a,...c] = [] // a undefined c [] const [a,...c] = ['a'] // a 'a' c []
- 字符串,将字符串转换为数组
[...'hello'] // [h,e,l,l,0]
- 实现了Iterator接口的对象
任何定义了遍历器接口对象,都可以用扩展运算符转为真正的数组 let nodelist = document.querySelectorAll('div') let array = [...nodelist] // querySelectorAll 返回的是一个类数组,通过扩展运算符 将其转换为一个真正的数组
- Map 和 Set 解构,Generator函数
扩展运算符调用的是数据解构的Iterator接口,只要有Iterator接口的 对象,都可以使用扩展运算符 // Map let map = new Map([ [1, 'a'], [2, 'b'], [3, 'c'], ]) let arr = [...map.keys()] // 1, 2, 3 let arr = [...map.values()] // 'a', 'b', 'c' //Generator函数 const go = function*(){ yield 1; yield 2; yield 3; } [...go()] // [1, 2, 3]
2. Array.from()
Array.from 方法用于将两类对象转为真正的数组。1、类似数组对象 和 可遍历的对象(包裹Set和Map),如下:
let arrLike = { '0': 'a', '1': 'b', '2': 'c', length: 3 } // ES5 var a1 = [].slice.call(arrLike) // ES6 var a2 = Array.from(arrLike)
在实际中,像获取dom后返回的Nodelist集合,以及函数内部的arguments对象就是类数组,通过 Array.from将它们转换为真正的数组。
// NodeList 对象 let ps = document.querySelectorAll('p') Array.from(ps).filter(p => { return p.textContent.length > 100 }) // arguments 对象 function foo(){ var arg = Array.from(arguments) } // 只要部署了Iterator接口的数据解构,Array.from都能将其转成数组 Array.from('hello') // ['h', 'e', 'l', 'l', 'o'] let nl = new Set([1, 2]) Array.from(nl) // [1, 2] // 如果是真数组则返回一样的 Array.from([1, 2, 3]) // [1, 2, 3]
... 扩展运算符也可以将某些类数组转换为数组,如arguments和NodeList集合
拥有lenght属性的对象都可以通过Array.from转换为数组,而扩展运算符则不行。
Array.from({lenght:3}) // [undefined, undefined, undefined]
对于低版本的浏览器,可以通过 Array.prototype.slice 方法替代
Array.from 还可以接受第二个参数如同map一样,用来对每个元素进行操作,并将处理后的值放入返回的数组中。
const arrlike = new Set([1,2,3]) Array.from(arrlike, x => x * x) // = Array.from(arrlike).map(x => x * x) // [1, 4, 9] //注意: 如果map中用到了this,可以传入Array.from 的第三个参数,用来绑定this
Array.from 可以将各种值转换为真正的数组,并且还提供map相关功能,这样代表如果有个原始数据结构,可以先将他转换为数组,然后使用数组相关的方法。
3. Array.of()
用于将一组值,转换为数组。主要用来弥补Array函数因为参数个数不同而导致的差异
Array.of(3,11,6) // [3, 11, 6] Array.of(3) // [3] Array.of(4).length // 1
4. 数组的实例 copyWithin()
将当前数组中指定位置的元素复制到另外一个位置,并且会覆盖那个位置的原有元素,会修改当前数组
// 有三个参数 1. target(必须):从该位置开始替换数据,如果是负值,则倒数 2. start(可选):从该位置读取数据,默认0,负值同上 3. end(可选):到这个位置停止读取数据,默认等于数组长度,负值同上 let p = [1,2,3,4,5,6,7] p.copyWithin(0,5,7) [6, 7, 3, 4, 5, 6, 7]
5. 数组实例的 find() 和 findIndex()
find 用来找出数组中符合条件的成员,它的参数是一个回调函数,找到一个返回值为true的返回,如果没有则返回undefined
let s = [1,2,3,4,5,6] s.find(x => x > 4) // 5 find 方法的回调函数有三个参数 value // 当前值 index // 当前的位置 arr // 原数组
findIndex 同find方法类似,只不过都不符合返回的是 -1,而且符合是返回符合条件值的位置而不是值。
let s = [1,2,3,4,5,6] s.find(x => x > 4) // 4
find 和 findIndex 都可以接受第二个参数
function o(p){ return p > this.age } const u = {name: 'cx', age: 11} const y = [8,11,22,2,4] y.find(o, u) // 22 返回的值 y.findIndex(o, u) // 2 返回值的位置
6. 数组实例的 fill()
通过给定值,填充一个数组
let sz = [1,2,3,4,5,6] sz.fill(1) // [1,1,1,1,1,1] sz.fill(1,0,3) // 接受三个参数,第一个为填充值,第二个为起始位置,第三个为截至位置 sz.fill(1,3) // 如果省略最后一个参数,则默认从起始位置到数组默认长度结束
7. 数组实例的 entries(), keys(), values()
三种方法主要用于遍历数组,可以用 for...of...进行遍历,keys()对应键名,values对应键值,entries()对键值对的遍历
let bo = ['a', 'c'] for(let r of bo.keys()){ console.log(r) // 0 1 } // 0 1 for(let n of bo.values()){ console.log(n) } // a c for(let s of bo.entries()){ console.log(s) } // [0, "a"] // [1, "c"]
8. 数组实例的 includes()
用来表示某个数组是否包含给定的值,返回一个布尔值
let i = ['a',1,2,3] i.includes() // false i.includes(1) // true i.includes(10) // false
indexOf 和includes 的区别
indexOf // 不够语义化,它的作用是找到参数出现的第一个位置, 所以要比较是否为 -1,另外由于 内部使用的是 === 则导致NaN 的误判。 // [NaN].indexOf(NaN) // -1 includes // 使用的是不一样的算法,则不会有这个问题 // [NaN].includes(NaN) // true
Map 和 Set 的has 方法和includes的区别
Map 的has 方法是用来查找键名的 Set 的has 方法是用来查找值的
9. 数组的实例 flat(), flatMap()
flat() 将嵌套的二维数组变成一维数组,如果需要拉平多维数组,则flat(多维数量) 或者使用 Infinity 直接转为一维数组
let rw = [1,2,3,[4,5,6],7] rw.flat() // [1, 2, 3, 4, 5, 6, 7] let dw = [1,2,3,[4,5,6,[7,8],[2,['a','b'],4,5]],[5,6,]] dw.flat(3) // [1, 2, 3, 4, 5, 6, 7, 8, 2, "a", "b", 4, 5, 5, 6] // 如果你不知道是多少层嵌套而都想转成一维,可以使用 Infinity dw.flat(Infinity) // [1, 2, 3, 4, 5, 6, 7, 8, 2, "a", "b", 4, 5, 5, 6]
flatMap() 对数组执行map,然后对返回值组成的数组 执行flat,不会改变原数组。flatMap只能展开一层数组。
let mp = [2,3,4,5] mp.flatMap((item) => [item, item* 2]) // [2, 4, 3, 6, 4, 8, 5, 10] ==== mp.map((item) => [item, item*2]) // [[2,4],[3,6],[4,8],[5,10]] mp.flat() // [2, 4, 3, 6, 4, 8, 5, 10]
10. 数组的空位(避免出现空位)
数组的空位指的是该数组中某一个位置没有任何值。另外空位不是undefined,如果一个位置的值是undefined,那么这个位置还是有值的。
Array(3) // [, , ,]
ES5中大多数情况中对待空位都是会忽略
- forEach(), filter(), reduce(), every() 和 some() 都会跳过空位 - map() 跳过但保留这个值 - join() 和 toString() 中 空位 === undefined,而 undefined和null会被处理成空字符串
ES6 中 空位则转换为undefined
- Array.from([1,,2]) // [1, undefined, 2] - [...['a',,'b']] // [ "a", undefined, "b" ] entries() keys() values() find() findIndex() // 都会将空位处理成undefined。