数组内容解析

355 阅读18分钟

一、数组常识

1、数组是个特殊的对象

# 数组属于“特殊的对象”。

为了验证数组属于特殊的对象。我们不妨通过打印数组的方式,看看调试器的显示结果。

首先我们应该搞清楚怎样才能知道数组的类型呢?

这个问题还是挺简单的,可以直接通过console.log(typeof 待测试的数组)的方式,打印在控制台上。

Snipaste_2020-08-27_13-40-39

我们在代码中运用起来看一下数组属于什么类型。

Snipaste_2020-08-27_13-42-45

最后,来到调试器里看一下输出的结果。

Snipaste_2020-08-27_13-47-49

# 从这一点就能够看出数组是一个对象。只是这个对象长的比较奇怪而已。

2、查看数组的数据结构

既然我们知道了数组是一个对象,那么自然可以使用console.dir()来进行检查。看看输出的效果。

Snipaste_2020-08-28_08-04-09

运行一下,看看结果。

Snipaste_2020-08-28_07-59-49

我们不妨点击展开一下

Snipaste_2020-08-28_08-06-59

我们要简单的说明一下数组的数据结构。

你会发现在在编写代码的时候,代码中的数值其实对应的是键值对的“值”。

Snipaste_2020-08-28_09-05-34

以数字从0开始逐级递增的部分叫做键值对的"值",也能够用来代表位置的数字索引

Snipaste_2020-08-28_08-26-5436

总结一下,你会是下面这个样子。

Snipaste_2020-08-28_08-26-5423

比较特殊的是,数组会默认有一个length属性名

Snipaste_2020-08-28_09-24-04

# 我们可以用“length属性”,编写很多复杂的逻辑代码。不容小视。

3、对比一下对象的数据结构

那么我们还记得对象的数据结构吗?我们不妨模拟一下。

Snipaste_2020-08-28_08-14-33

运行一下,看看结果。

Snipaste_2020-08-28_08-18-34

我们不妨点击展开一下

Snipaste_2020-08-28_08-20-12

# 可以看出数组和对象是非常相似的。

我们完全可以用对象的形式重新将数组重写一下。

Snipaste_2020-08-28_09-39-13

# 数组和对象对比的结论是:
1、数组和对象可以说非常的相似,本质上数组还是对象,只不过数组是一个特殊的对象。
2、数组中并不需要我们手动的设置"属性名",默认由"索引"替代。
3、数组自带length属性名,其中长度会自动的计算出来。编写代码的时候可以直接拿来使用。

4、数组小技巧

在使用数组的时候,有些小技巧需要我们看到数组的时候,脑海中就能够马上反应出来。

# 索引

所谓索引就是数组中默认生成的,从0开始逐级递增的属性名叫做索引。(如下图所示)

Snipaste_2020-08-28_08-26-5412

索引也叫做键值对中的“键”,也叫键值对的“key”,也叫属性名。但是官方的叫法叫做索引


# 获取指定项的内容

我们可以通过索引来获取指定项的内容。

# 比如我们现在要获取数组中的第一项(指定项)的内容。

Snipaste_2020-08-28_10-17-58

运行一下,你会发现输出第一项内容为 12

Snipaste_2020-08-29_08-57-35

从上面的现象中,也说明了一点,"位置1" 对应的是 "索引0",千万不要认为是"索引1"

# 获取数组中的长度

前面我们有提到,数组中默认会存在一个属性,叫做length属性。代表的是长度。

Snipaste_2020-08-28_09-24-04

那么我们可以通过ary.length的方式,可以获取数组的长度。

Snipaste_2020-08-28_10-31-48

运行之后,就能够看出数组中的长度。

Snipaste_2020-08-28_10-34-13

# 获取最后一项的索引

请问什么叫做获取最后一项索引呢?

其实直接通过图片的方式进行理解就方便很多了。

Snipaste_2020-08-28_08-26-54965

要想获取最后一项的索引,那么必须要我们理解索引是从0开始逐级递增的。

只有理解了这一点,那么我们编写起来就非常的简单了。其实你会发现最后一项索引数组长度存在关联性

Snipaste_2020-08-28_10-45-42

也就是可以通过ary.length-1的方式,就能够获取最后一项的索引

我们用代码的形式模拟一下。

Snipaste_2020-08-28_10-53-55

运行一下,看看效果。

Snipaste_2020-08-28_10-56-53


# 上面这些数组中的小技巧,其实通过分析数组的数据结构,都是可以分析出来的。不需要我们可以的去死记,只要你能够理解什么是索引?什么是数组的长度?那么问题就解决了。

二、数组有什么常用的方法(摘要)

遍历数组方法的简单介绍

说到遍历数组的方法,那么到底可以有多少个方法呢?

​ 有forEachmapfilterfindreducesomeevery及其他方法。

​ 需要知道的是操作数组的方法实在是太太太重要了。只要你操作数据那么上面的这些方法你都要学会。更何况后面我们在学习vuereact这种以数据来影响视图的前端框架,那么数组这一块的内容可以说是天天在操作。因此上面的这些方法是我们一定要学会的,甚至能背出来的方法。

首先我们需要知道的是:我们在学习数组的常用方法时,我们需要按照四个维度来记忆。

# 1、方法的作用和含义
	 需要知道这个方法到底是干啥的?
# 2、方法的实参(包括类型和含义)
	 执行方法时,可以给方法传递参数。那么我们到底给方法传递什么参数呢?是需要我们搞清楚的。
# 3、方法的返回值
	 执行方法后,会返回一个什么值?
# 4、原来的数组是否会发生改变
	 有些方法会将原始数组的数据进行修改。需要我们进行区分。

​ 其实,对于这些方法来说,没有什么可以用逻辑进行思考的,主要还是在于背。背的越熟越好。在使用的时候,能够快速的反应出来,那么再稍微查阅一下,然后使用起来,那么这就够了。

1、新增内容:push方法

需要注意的是,增删改的这一部分方法都会修改原有的数组。

# push方法

# 1、方法的作用和含义
	 push方法的作用是向数组末尾增加内容。
# 2、方法的实参(包括类型和含义)
	 可以传入“多个任意类型”的参数,可以增加一个,也可以增加很多个。
# 3、方法的返回值
	 返回新增后的数组长度。
# 4、原来的数组是否会发生改变
	 会修改原有的数据。

我们不妨举个栗子。我们试图将数字、字符串、对象。添加到ary数组中。

Snipaste_2020-08-28_12-41-17

重点看一下返回的结果。这一点非常的重要。

Snipaste_2020-08-28_13-40-45

# 原生JS方法

我们不妨思考一下,除了使用push方法以外,还有其他方法能够在数组的最后面添加内容吗?

Snipaste_2020-08-29_06-50-21

我们不妨测试一下,代码如下所示:

Snipaste_2020-08-29_06-56-44

然后运行一下,你会发现,既然成功了。

Snipaste_2020-08-29_07-15-13

你想过为什么可以通过这种方式添加数据吗?

之所以能够这样简单粗暴的方式直接添加,是因为JavaScript是动态语言特性决定的。它不像Java和C#这一类的静态语言。JavaScript可以在不提前定义的情况下直接添加不同类型的数据。

完善一下代码,避免代码被写死了。

Snipaste_2020-08-29_07-07-10

我们可以用数组的长度,替代掉最后一个索引+1的代码,最终的代码如下。

Snipaste_2020-08-29_07-21-25

运行一下,看看是不是和前面的运行结果一样。

Snipaste_2020-08-29_07-25-05

# 这种方式是,基于原生JS操作键值对的方式,也可以向末尾添加一项新的内容。

你也会发现,其实使用push方法来添加。要比原生的JS来添加内容要好用很多。毕竟push方法是封装好的方法。编写代码的时候,优先选择push方法。特殊情况下,这个方法也不是不能用。

2、数组遍历 forEach方法

# forEach方法的理解技巧

要想理解forEach方法,还是要从四个维度上进行分析

# 1、方法的作用和含义
	forEach方法:是用来遍历数组中的每一项内容
# 2、方法的实参(包括类型和含义)
	参数是:回调函数
# 3、方法的返回值
	返回值:没有
# 4、原来的数组是否会发生改变
	原来数组不会发生变化

# 基于原生JS中的循环

如果我们在没有讲解forEach方法之前,想要遍历数组中的每一项内容的时候,可以怎么做呢?

在没有forEach方法之前,我们可以基于原生JS中的循环就可以实现遍历数组中每一项内容的功能。

let ary = [12, 15, 9, 28, 10, 22];

for(let i=0;i<ary.length;i++){
    // i:代表的是当前循环的索引。
    // ary[i]:根据索引获取循环的某一项值。
    console.log('索引:'+i+'内容:'+ary[i]);
}

我们把上面这段代码编写在编辑器中。

Snipaste_2020-08-27_08-24-43

然后运行一下看看,显示的结果。

Snipaste_2020-08-27_08-26-30

​ 我们可以看出,通过普通的for循环,就能够实现遍历数组中每一项内容的功能。

​ 除了通过原生JS中的循环方式以外,我们还可以通过调用方法的方式来实现相同的效果。也就是下面我们要用到的forEach方法。

# forEach方法的使用技巧

还是原来的数组,我们看看如何用forEach方法来遍历出每一项内容的功能

let ary = [12, 15, 9, 28, 10, 22];

首先,我们需要用“需要遍历的数组ary”来开头,再调用forEach方法。这种方式看起来会比较怪。

Snipaste_2020-08-27_08-59-03

然后,我们将回调函数作为参数传递进去。

Snipaste_2020-08-27_09-07-16

需要注意的是,数组中有多少项,那么里面的回调函数就会默认执行多少次。

Snipaste_2020-08-27_09-20-14

你会发现。回调函数里面有了两个参数,分别是itemindex

那么我们看一下这两个参数分别代表着什么含义。

当每一次执行回调函数的时候,那么item和index表示的意思如下:
# item:是数组中当前要操作的这一项。
# index:是数组中当前项的索引。

换句话说,其实item对应着for循环中的ary[i],而index对应着for循环中的i

Snipaste_2020-08-27_09-47-45

为了证实这一点,我们也用for循环中的打印方法,改一下参数。

Snipaste_2020-08-27_10-01-20

打印出来,看看效果。

Snipaste_2020-08-27_08-26-30

你会发现,"for循环""forEach方法"打印出来的效果是一模一样的。

​ 其实通过forEach方法写出来的代码会更加简单和易于维护,后面我们如果需要用到遍历数组,那么我们可以首先考虑去使用forEach方法来进行编辑。

​ 需要注意的是,类数组是不能使用forEach方法来遍历的。只有数组才能使用forEach方法来进行遍历。虽然说for循环也可以遍历数组,但是在编程中,大多还是采用forEach方法的。

​ 后续会学习到另一种特殊的方法,这个方法是可以通过JS封装一个比官方给的forEach方法还要好用的Each方法Each方法不仅仅是可以遍历数组,还可以遍历对象,还可以遍历类数组

最后,我们我们格式化一下代码的话,会出现一定程度的变化。需要我们能够识别出来。

Snipaste_2020-08-27_10-17-47

如果你在开发的时候,没有用到index索引的时候,其实还可以对代码进行优化。

Snipaste_2020-08-27_11-31-00

如果是index索引被省略掉了,其实按照箭头函数的规则,只有一个参数的话,可以省略小括号。

Snipaste_2020-08-27_11-38-10

知道这里面的小细节,可以在编程中省略很多的代码,提升代码美观度。熟悉以后,可以提升开发效率。

# myforEach的封装

在遍历数组的时候,有没有想过有了for循环的情况下,为什么官方还提供forEach方法?

​ 要知道在forEach方法没有出来之前,我们可以使用基于原生JS中的循环(就是使用for循环)就可以实现遍历数组中每一项内容。如下所示:

let ary = [12, 15, 9, 28, 10, 22];

for(let i=0;i<ary.length;i++){
    // i:代表的是当前循环的索引。
    // ary[i]:根据索引获取循环的某一项值。
    console.log('索引:'+i+'内容:'+ary[i]);
}

我们把上面这段代码编写在编辑器中。

Snipaste_2020-08-27_08-24-43

然后运行一下看看,显示的结果。

Snipaste_2020-08-27_08-26-30

# 我们可以看出,通过普通的for循环,就能够实现遍历数组中每一项内容的功能。

​ 但是除了使用for循环以外,我们还可以通过另一种方式来编写。也就是使用forEach方法来进行遍历数组中的元素。代码如下所示:

let ary = [12,15,9,28,10,22];
ary.forEach(
    (item,index)=>{
		console.log('索引:'+index+'内容:'+item);
    }
)

Snipaste_2020-08-27_10-01-20

# 运行之后,你就可以发现使用forEach的方法就是实现相同的效果。

那么这个时候,你有没有想过,forEach方法里面是如何实现数组的遍历的呢?

​ 其实答案很简单,其实forEach方法的本质还是for循环。只不过for循环被封装成了回调函数for循环forEach方法本质上是一样的,只是在外界使用的方式上看上去显的不一样。

那么为什么还要多次一举的将for循环封装成forEach方法呢?

Snipaste_2020-09-09_12-47-52

​ 其实我们单纯从代码上进行比较的话,就能看出来,在使用forEach方法的时候,要比for循环要好用很多。在使用forEach方法的时候,我们并不需要考虑“i++”这类的递增关系。只需要关注itemindex。就是完成遍历的操作。因此通过封装的代码,会让代码变得更加的方便的使用。日后的代码维护也变得方便很多。这就是为什么需要大费周章过的对for循环进行封装的原因。

如何证明forEach方法的本质就是for循环呢?

​ 最直接的方法,自然是直接查看源码。这里我就不介绍了,我们来个更好玩的,我们试试用回调函数的方法来封装一个属于自己的forEach方法。实现和forEach方法相同的效果。

具体封装过程:

我们可以看出这个通过for循环遍历的话,代码是这样的。

Snipaste_2020-09-11_09-35-14

而forEach方法,本质上就是对for循环的封装。而封装的方式是通过回调函数进行封装的。

# 回调函数:编写在方法中的函数。

因此首先我们需要编写一个方法用来管理for循环。

Snipaste_2020-09-11_09-44-39

然后,我们需要在方法中添加fn参数。其中fn代指的是方法。

Snipaste_2020-09-11_10-06-28

接下来,我们就要考虑fn函数的编写情况了。我们需要考虑的是,需要遍历的数据是什么?

Snipaste_2020-09-11_10-26-39

但是这会有个问题是,我们不能直接使用ary,因为这样的话会限制我们封装的代码。

Snipaste_2020-09-11_10-44-41

我们应该将数组的名称决定权交给调用者。我们可以用this来指代需要遍历的数组名称。

Snipaste_2020-09-11_11-19-44

接下来我们重点看一下如何编写fn函数。其实就是对打印出来的结果进行改造。

Snipaste_2020-09-11_11-31-57

# 其实这样就算封装完毕了,是不是很简单。

如果是在前端使用的话,我们还需要将自己定义的myForEach方法定义到Array.peototype中。在微信小程序中使用自己定义的myForEach方法的话。只需要引入就可以使用。没有这么麻烦。

Snipaste_2020-09-11_11-51-38

打印出来看下结果。

Snipaste_2020-09-11_11-59-24

当传递进去之后,那么我们在后续遍历数组的时候,myForEach方法才会起到作用。这个时候我们试着使用起来。

Snipaste_2020-09-11_12-10-13

为什么需要使用这么奇怪的调用方法结构。ary.myForEach()

其实原因在于我们在编写myForEach方法的时候,为了能够封装一个通用性较强的方法,我们用了this来替代数组名称。(前面已经说过)。既然在封装的时候没有规定是哪一个特定的数据,那么我们在使用的时候,就需要先告诉一下myForEach方法需要使用的具体数组名,然后再使用方法。也就是这样的ary.myForEach()结构。

那么这个时候,我们需要如何使用myForEach方法呢?

Snipaste_2020-09-11_12-34-20

这个你需要知道的是,index参数内部存储的就是for循环中的索引i,item参数内部存储的是for循环中的

Snipaste_2020-09-11_12-41-29

然后我们就可以执行遍历数组了。

Snipaste_2020-09-11_13-11-50

打印出来看一下效果

Snipaste_2020-09-11_13-09-42

# 这就把forEach方法的效果给模拟出来了。可以看出forEach方法本质就是for循环。只不过我们通过回调函数的方式将for循环进行了封装。

细心的你会发现,其实myforEach方法的使用方式和数组内置的forEach方法的使用方法是一样的。

Snipaste_2020-09-11_13-19-01

我们还可以完善一下代码,如下所示:

Snipaste_2020-09-08_13-49-18

最终打印出来的结果如下:

Snipaste_2020-09-08_13-51-58

可以看出,本质上还是通过回调函数的形式进行来封装。


上篇的案例来源于:www.jianshu.com/p/a07975ae2…