《ES6标准入门》阅读笔记——-数组的扩展

写在前面

本系列为在我读完阮一峰老师的《ES6标准入门》第二版之后,所做的阅读笔记的整理。

许多初学ES6, 同时和我一样初次阅读阮老师的这本书的时候, 读第一遍会越发的困惑, 因为阮老师上面说的很多的定义,方法,之前都是没有见过的, 读到后面才发现, 哦~原来是这样.

有一句话叫做”大神的世界我们不懂”, 所以我在初读第一遍《ES6标准入门》这本书的时候,也是踩了不少坑,读书的时候查阅了不少的资料.

所以,在这一系列的笔记教程中,我会从一个初学者的角度,向您讲述ES6的相关知识,在后面介绍的知识我会尽量不提前用,即使提前使用,也会同时做好标注,避免了阅读时各种查阅资料的烦恼.

系列博客将采用一个一个的样例,来说明书中的精华部分(当然,这只是我认为的),同时引导新手,快速入门ES6,并逐步将其投入到生产实践中。

同时,在阅读前也提醒您, 为了系统连贯性的学习ES6的基础知识,建议您从我的博客第一章开始阅读,当然,如果您对对应的知识已经有所了解,那么可以跳转到任意章节阅读,每一篇博客名中均有介绍该博客中涉及到的ES6的内容.

阮一峰老师的这本书是开源的,在其官方博客就可以下载到,但是我强烈建议大家去购买一本书, 一是方便自己查阅ES6中新增的众多API, 二也是表达一下对大神的敬仰.

新增的API–Array.from()

Array.from() 方法将两类对象转为真正的数组:类数组对象和可遍历对象

1
2
3
4
5
6
7
8
9
10
11
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
var arr1 = Array.prototype.slice.call(arrayLike)
console.log(arr1) // [1,2,3]

let arr2 = Array.from(arrayLike)
console.log(arr2) // [1,2,3]

可见,在将类数组转换为数组的过程中,使用ES6定义的新方法能够更好且更简单地将类数组转换为真正的数组.

在实际应用中,我们最常见的对象是DOM操作返回的NodeList集合,同样的,使用这个方法就可以实现将相应的类数组对象转换为数组

1
2
3
let nameSet = new Set(['a', 'b'])
var a = Array.from(nameSet)
console.log(a) // ['a', 'b']

注: Set是ES6中的新的数据结构, 类似于数组, 但是成员的值都是唯一的, 没有重复的值, 这是Set结构的最大特征.

如果对象是一个真的数组,那么使用Array.from() 方法会返回一个和原来一模一样的新数组

1
2
3
4
5
let a = [1, 2, 3]
let b = Array.from(a)
a = [3, 2, 1]
console.log(a) // [3,2,1]
console.log(b) // [1,2,3]

扩展运算符也可以将某些数据结构转为数组
那么,我们可以对函数的变量arguments进行相应的处理,让其变成真正的数组

1
2
3
function foo() {
var args = [...arguments]
}

那么同样的,我们也可以对HTML的元素对象进行相应的操作

1
[...document.querySelectorAll('div')]

扩展运算符背后调用的是遍历器接口Iterator, 如果一个对象没有部署该接口就无法进行转换, Iterator将在后面详细讲解.

但是,所有的转换,一个最大的前提条件是,必须要有一个length对象,保存一个数值,没有这个属性的话是没有办法转为数组的

对于没有部署Array.from() 方法的浏览器,我们可以使用对应的老方法和新方法进行结合模拟

1
2
3
const toArray = (() =>
Array.from ? Array.from : obj => [].slice.call(obj)
)

注: 箭头函数也是ES6中新定义的方法, 其实是一种语法糖,方便我们进行编程而已.
() => console.log(1) 等价于 function() {console.log(1)}

Array.from 方法还可以接受第二个参数,作用类似于数组的map方法,用来对每个元素进行处理,并将处理后的值放入返回的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
let arrayLike = {
'0': 'a',
'1': 'b',
'2': 'c',
length: 3
}
Array.from(arrayLike, x => x * x)
// 其实上面的方法就等同于下面的方法
Array.from(arrayLike).map(function(x) {
return x * x
})
// 上面是使用ES5的方法所写的方法,我们可以将函数转换为箭头函数的形式
Array.from(arrayLike).map(x => x * x)

利用这个特性,可以很容易地将数组的空位置零

1
2
let a = Array.from([1,,2,,5], (x) => x || 0)
console.log(a) // [1,0,2,0,5]

数组的Array.of()方法

Array.of() 方法用于将一组值转换为数组

1
2
3
console.log(Array.of(1, 8, 3))  // [1,8,3]
console.log(Array.of(3)) // [3]
console.log(Array.of()) // []

其实这个方法的主要目的,是弥补构造函数Array的不足, 因为参数的不同会导致Array的行为有所差异

1
2
3
console.log(Array())  // []
console.log(Array(3)) // [,,,]
console.log(1,2,3) // [1,2,3]

因此, 不同于直接使用Array方法, 使用Array.of()方法不论你传入的是一个数,还是几个数,其实最后都是返回的数组对象, 而不同于使用Array, 会产生不同的行为.

数组的copyWithin() 方法

数组的copyWithin()方法, 主要的作用是在当前数组内部将指定位置的成员复制到其他位置, 此方法接受三个参数

target 必需值,从该位置开始替换数据, 也就是说, 从这个地方开始要更改数组了
atart 可选值,从该位置开始读取数据,默认为0 , 若为负值,则表示倒数, 也就是说, 我要更改数组,总得找个东西替换这些个之前的数组呀. 那么我就从这个位置开始截取数组的值,把它放到要更改的地方
end 到该位置前停止读取数据,默认等于数组长度,如果为负值,表示倒数, 也就是说,截取也不能截取个没完嘛,所以需要在这个地方停止截取, 然后就把截取的值丢到target的那个位置了.

1
2
3
let a = [1, 2, 3, 4, 5]
a.copyWithin(3, 1, 2)
console.log(a) // [1, 2, 3, 2, 5]

由上可见,其实copyWithin()最终更改了, 第四个位置, 也就是a[3] 的值

数组的find() 方法和findIndex() 方法

数组的find() 方法,用于找出第一个符合条件的数组成员.数组成员依次执行其后的回调函数,直到找出第一个匹配的成员,否则返回undefined

下面的代码找出了数组中小于0 的成员, 并将其打印出来

1
2
let a = [1, 2, 3, -4, 3]
console.log(a.find((x) => x < 0)) // -4

find方法可以接受三个参数, 依次为当前值, 当前位置和原数组.

1
2
let b = [1, 5, 10, 15].find((value, index, arr) => value > 9)
console.log(a) // 10

数组的findeIndex() 方法返回第一个符合条件的数组成员的位置, 如果所有成员都不符合条件,则返回-1

这两个方法还可以接受第二个参数,用来绑定回调函数的this对象

还有重要的一点是,这两个方法都可以发现NaN,弥补了之前NaN支持的不足

1
2
3
4
5
let a = [NaN].indexOf(NaN)
console.log(a) // -1

let b = [NaN].findIndex(y => Object.is(NaN, y))
console.log(b) // 0

注: Object.is() 用来比较两个值是否严格相等. 它与全等运算符表现基本一致, 但增加了NaN的支持.

数组的fill() 方法

数组实例的fill() 该方法使用给定值填充数组

1
2
console.log(['a', 'b', 'c'].fill(1)) // [1, 1, 1]
console.log(['a', 'b', 'c'].fill(7, 1, 2)) // ["a", 7, "c"]

这样对数组进行初始化非常的方便,该方法还可接受另外两个可选参数,规定填充的起始和结束位置

数组遍历

ES6提供了三个新方法用于遍历数组

keys() 对键名的遍历
values() 对键值的遍历
entries() 对键值对的遍历

1
2
3
4
5
6
7
8
9
for (let index of ['a', 'b'].keys()) {
console.log(index) // 0 // 1
}
for (let index of ['a', 'b'].values()) {
console.log(index) // 'a' // 'b'
}
for (let index of ['a', 'b'].entries()) {
console.log(index) // 0 'a' // 1 'b'
}

注: for…of 遍历也是对循环的一种补充,与for…in类似,但不会枚举出对象上的可枚举属性

数组的includes()

includes() 方法返回一个实例,表示某数组是否包含给定的值

1
2
3
let a = [1, 2, 3]
console.log(a.includes(2)) // true
console.log(a.includes(4)) // false

当浏览器不兼容的时候,可以使用indexOf方法进行替代
但是indexOf有一个弊端,原因是indexOf内部使用的是全等操作符进行匹配,所以这个时候当我们判定NaN的时候,是无法判定成功的, 因为NaN!==NaN

1
console.log([NaN].indexOf(NaN)) // -1

数组的空位

ES5在很多情况下,对于数组的空位是直接忽略的,例如forEach filter every some 等方法会跳过空位, map 会跳过空位,但会保留这个值, join 和 toString 会将空位视为undefined 而undefined和null会被处理为字符串

下面是ES5下的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[, 'a'].forEach((x, i) => console.log(i)); 
// 1 因为第零个是空位,被忽略了
console.log([, 'a'].filter((x, i) => true));
// ["a"] 小红书有讲过的方法,使用true将数组的空位去除
console.log([, 'a'].every((x, i) => x !== undefined))
// true
console.log([, 'a'].some((x, i) => x === undefined))
// false
console.log([, 'a'].map((x, i) => 1));
// [empty × 1, 1]
console.log([, 'a', undefined, null].join('#'))
// #a##

`

但在ES6中,空位会被明确转为undefined

1
2
console.log([...['a', , 'b']]) 
//["a", undefined, "b"]

copyWithin()会连空位一起复制

1
[, 'a', 'b', ,].copyWithin(2, 0) //[empty × 1, "a", empty × 1, "a"]

fill()会将空位视为正常的数组位置

1
new Array(3).fill('a') // ['a', 'a', 'a']

for…of循环也会遍历空位, 而map却不会遍历空位

1
2
3
4
let arr = [, ,]
for(let i of arr) {
console.log(1) // 1 // 1
}

数组推导

数组推导是ES7中的新方法,允许直接通过现有数组生成新数组.

本来TC39是计划将其放入ES6的,但TC39仍然想完善它,让其支持所有数组结构. 所以推迟到了ES7.

1
2
3
4
5
let a1 = [1, 2, 3, 4]
var a2 = [
for (i of a1) i * 2
];
console.log(a2) // [2, 4, 6, 8]

(完)

文章目录
  1. 1. 写在前面
  2. 2. 新增的API–Array.from()
  3. 3. 数组的Array.of()方法
  4. 4. 数组的copyWithin() 方法
  5. 5. 数组的find() 方法和findIndex() 方法
  6. 6. 数组的fill() 方法
  7. 7. 数组遍历
  8. 8. 数组的includes()
  9. 9. 数组的空位
  10. 10. 数组推导
|