ES6(三)—— 数组

JavaScriptES6

# 数据的重要功能【遍历、转换、生成、查找】

# 数组遍历

# ES5中遍历有多少种方法?

const arr = [1,2,3,4,5]
1

# for循环

for (let i = 0;i < arr.length; i++) {
    console.log(arr[i]) // 1 2 3 4 5
}
1
2
3

# forEach

数组遍历方法

arr.forEach(function(item){
    console.log(item) //1 2 3 4 5
})
1
2
3

代码简洁上看,forEachfor循环写法要简洁。

但是forEach不支持reakcontinue,每个元素必须遍历到

// for循环可以使用break控制遍历停止
for (let i = 0;i < arr.length; i++) {
    if(arr[i] === 2){
        break
    }
    console.log(arr[i]) // 1
}

// for循环可以使用continue控制遍历跳过
for (let i = 0;i < arr.length; i++) {
    if(arr[i] === 2){
        continue
    }
    console.log(arr[i]) // 1 3 4 5
}

//forEach不支持break和continue
arr.forEach(function(item){
    if(item === 2) {
        break // or continue 都会报错 Unsyntactic break 语法不支持
    }
    console.log(item)
})

arr.forEach(function(item){
    if(item === 2) {
        return false //跳过本此循环 类似continue
    }
    console.log(item) // 1 3 4 5
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# every

数组遍历方法

arr.every(function(item){
    console.log(item) // 1
})
// every继不继续遍历取决于返回值,为true则继续遍历,false退出。默认是false
// 如果要全部遍历完成 需要返回true
arr.every(function(item){
    console.log(item)  // 1 2 3 4 5
    return true
})

// 可以通过返回的true or false 来控制循环
arr.every(function(item){
    if(item === 2) {
        return false //相当于for循环中的break
    }
    console.log(item)  // 1
    return true
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# for-in

for-in本身是未object遍历的,而不是为数组。虽然可以遍历数组,但是有瑕疵。

for-in中可以使用continuebreak,但是不支持写return,会报错Illegal return statement

// 之所以数组可以用for-in取遍历
// 是因为1.数组是对象的一种 2.数组是可遍历的
for (let index in arr) {
    console.log(index, arr[index])
}
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5


//瑕疵一:
// 因为arr是一个对象,所以可以定义属性并赋值
arr.a = 8
for (let index in arr) {
    console.log(index, arr[index])
}
// 再次进行遍历
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
// a 8  这个时候a不是索引,而是字符串,如果这里我们还是以为遍历之后只是返回索引就容易出现bug


//瑕疵二:
for(let index in arr) {
    if(index === 2) {
        continue
    }
    console.log(index, arr[index])
}
// 0 1
// 1 2
// 2 3
// 3 4
// 4 5
//为什么可以使用continue但是continue却没有起作用
//因为这个时候index是字符串而不是索引

//解决方法一:只判断值,不判断类型
for(let index in arr) {
    if(index == 2) {
        continue
    }
    console.log(index, arr[index])
}
// 0 1
// 1 2
// 3 4
// 4 5
//解决方法二:将index隐式转化成数字类型
for(let index in arr) {
    if(index * 1 === 2) {
        continue
    }
    console.log(index, arr[index])
}
// 0 1
// 1 2
// 3 4
// 4 5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

# for-of(ES6新增)

// item不是下标,直接是值,且可以使用break终止循环
for(let item of arr) {
    console.log(item) // 1 2 3 4 5
}
1
2
3
4

# 为什么es6要新增一个for-of?

要判断一个对象是不是可遍历的,不能说其是不是一个数组 or Object。

ES6允许用户自定义数据结构,这种数据结构既不是数组,也不是Object,但是都是可遍历的。这种数据结构进行遍历,不能用数组的也不能用for-in,就需要新增一种for-of去遍历

举例子:

// 定义一个数据结构,想要遍历拿到类别中的最低值
const Price = {
    A: [1.5, 2.3, 4.5],
    B: [3, 4, 5],
    C: [0.5, 0.8, 1.2]
}

for (let key in Price) {
    console.log(key, Price[key])
    // [1.5, 2.3, 4.5]
    // [3, 4, 5]
    // [0.5, 0.8, 1.2]
}
// 使用for-in只能返回数组,无法直接把三个最低值遍历出来
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# for-of不能直接遍历对象? —— 关于可迭代接口

for-of可以用来遍历Set结构和Map结构,但是不可以直接遍历object,因为其不是可遍历的对象,Iterable接口没有实现。

下面看一下:数组、Set和Map的原型对象上有迭代器

数组.png

Set对象.png

Map对象.png Object.png 对象方法上面没有

Object.png

那么我们调用一下数组上面的iterator方法

const arr = ['foo', 'bar', 'baz']
console.log(arr[Symbol.iterator]())
// 返回一个iterator的对象,其原型对象上面有next方法
// Array Iterator {}  
//  __proto__: Array Iterator
//    next: ƒ next()   
//    Symbol(Symbol.toStringTag): "Array Iterator"
//    __proto__: Object
const iterator = arr[Symbol.iterator]()

console.log(iterator.next()) // { value: 'foo', done: false }
console.log(iterator.next()) // { value: 'bar', done: false }
console.log(iterator.next()) // { value: 'baz', done: false }
console.log(iterator.next()) // { value: undefined, done: true }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

可以看到for-of内部的循环规则,里面有一个迭代器。只要对象可以实现Iterable接口就可以使用for-of进行循环。下面对一个对象进行可迭代改造。

# 优势和缺点

循环方式 优势 缺点 特点
for循环 支持break和continue 不支持return
forEach 写法比for简洁明了 全部遍历,不支持break和continue return false相当于continue
every 可以支持类似for的break和continue
for-in 可以遍历object 遍历数组的时候存在瑕疵 不支持return
for-of(ES6新增) 可以遍历除数组和object之外可遍历的数据结构,支持break和continue 不支持return
  • return只能用在函数体内,出现在代码的任何地方都会报错

# 将伪数组转换成数组

# 什么是伪数组?

简单理解: 具备一些数组的特性:可遍历、有长度。但是不能直接调用数组的API,类似于 argumentsDOM nodeList

严格理解:

  1. 按照索引方式存储数据
  2. 具备length属性

{0:'2',1:'b',length:2} 这种类型都可以称之为伪数组

为什么要将伪数组转换成数组?

如果想要使用数组的API,就需要将伪数组转换为数组

# ES5的转换

let args = [].slice.call(arguments) //collection
// arguments 只能在函数体内使用
// ES6已经废弃arguments的使用
let imgs = [].slice.call(document.querySelectorAll('img')) // NodeList
1
2
3
4

# ES6的转换

Array.from

let args = Array.from(arguments)
let imgs = Array.from(document.querySelectorAll('img'))
1
2

大致说一下Array.from这个函数

Array.from(arrayLike,mapFn,thisArg)
第一个参数:伪数组,必须
第二个参数:遍历函数,非必须
第三个函数:this对象,非必须

举一个例子: 初始化一个长度为5的数组

//ES5 
let array = Array(5)

array.forEach(function (item) {
    item = 1
})
console.log(array) // [empty × 5] forEach只会遍历存在的元素

//使用for循环可以遍历,但是依旧是先声明,后赋值
for (let i = 0, len = array.length; i < len; i++) {
    array[i] = 1
}
console.log(array) // [1,1,1,1,1]

// 先将数组转化为5个空字符串数组,然后每个遍历赋值
let arr = Array(6).join(' ').split('').map(item => 1)
console.log(array) // [1,1,1,1,1]

// ES6
//使用Array.from可以达到初始化并填充的效果
let array = Array.from({ length: 5 }, function () { return 1 })
console.log(array) // [1,1,1,1,1]
// 上面之所以第一个参数可以传{ length: 5 },是因为第一个参数应该传伪数组

//使用Array.fill可以快速实现初始化并填充
let array = Array(5).fill(1)
console.log(array) //[1,1,1,1,1]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 创建新数组

# ES5

let array = Array(5)
let array = ['', ''] //无法定义长度,只能每个都初始化为空字符串
1
2

# ES6

# Array.from

let array = Array.from({ length: 5 })
1

# Array.of

let array = Array.of(1,2,3,4,5) //参数是依此放进去的元素,可以一个参数可以多个
console.log(array) //[1,2,3,4,5]
1
2

# Array.prototype.fill

let array = Array(5).fill(1) //参数是依此放进去的元素,可以一个参数可以多个
console.log(array) //[1,1,1,1,1]
1
2

Array.fill(value,start,end)
第一个参数:填充值
第二个参数:起始位置,默认为数组的开始
第三个函数:结束位置,默认为数组的结束
[start,end)

let array = [1, 2, 3, 4, 5]
console.log(array.fill(8, 2, 4)) // [1,2,8,8,5]
1
2

# 数组中查找元素

# ES5

查找元素包括看元素在不在数组中,也包括根据某个条件去筛选。

// 一
let array = [1, 2, 3, 4, 5]
let find = array.filter(function (item) {
    return item === 3
})
console.log(find) // [3]
// 能找到没找到都返回一个数组,根据数组的长度判断数组中有或者没有元素

let find = array.filter(function (item) {
    return item % 2 === 0
})
console.log(find) // [2,4]
// 如果数组中数据特别大,只是要有还是没有,并不要所有的数据

// 二
// 使用every也可以进行判断但是我们需要额外定义变量
let isExt
array.every(function (item) {
    if (item === 3) {
        isExt = item
        return false
    }
    return true
})
console.log(isExt) // 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# ES6

# Array.prototype.find

找到查找元素返回其值,找不到返回undefined

let array = [1, 2, 3, 4, 5]
let find = array.find(function(item){
    return item === 3
})
console.log(find)  // 3---当前值

let find1 = array.find(function(item){
    return item === 6
})
console.log(find1)  // undefined---没有找到

let find2 = array.find(function(item){
    return item % 2 === 0
})
console.log(find2)  // 2---找到2就不再继续找了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Array.prototype.findIndex

找到查找元素返回其索引位置,找不到返回-1

let find = array.findIndex(function(item){
    return item % 2 === 0
})
console.log(find)  // 1---索引位置
1
2
3
4

# 函数参数

# Array.from(arrayLike,mapFn,thisArg)

第一个参数:伪数组,必须
第二个参数:遍历函数,非必须
第三个函数:this对象,非必须

# Array.of(element0[,element1...])

参数:任意个参数,依此成为数组的元素

# Array.fill(value,start,end)

第一个参数:填充值
第二个参数:起始位置,默认为0
第三个函数:结束位置,默认为this.length
[start,end)

# Array.find(callback[, thisArg])

第一个参数:回调函数,接收三个参数,element、index、array
第二个参数:this对象

# Array.findIndex(callback[, thisArg])

第一个参数:回调函数,接收三个参数,element、index、array
第二个参数:this对象

更新时间: 2021-09-15 12:03