手把手5分钟入门JavaScript数组,万字文超详细手绘操作执行过程②JavaScript数据结构与算法系列

Posted 狼丶宇先森

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手5分钟入门JavaScript数组,万字文超详细手绘操作执行过程②JavaScript数据结构与算法系列相关的知识,希望对你有一定的参考价值。

本文目录


千万别觉得javascript的数组就是一个[]中括号,虽然常用,可能你觉得也简单,但是真的要深入,真的还是有点难度的。

一、数组的简介

几乎所有的编程语言都原生支持数组类型,因为数组是最简单的内存数据结构。JavaScript里也有数组类型,尽管它的第一个版本并没有支持数组。本章将深入学习数组数据结构和它的能力。

数组存储一系列同一种数据类型的值。虽然在JavaScript里,也可以在数组中保存不同类型的值,但我们还是遵守最佳实践,避免这么做(大多数语言都没这个能力)。

  • 为什么使用数组?
    假如有这样一个需求:保存所在城市每个月的平均温度。可以这么做:

const m1 = 31.1;
const m2 = 34.2;
const m3 = 28.8;
const m4 = 32.2;
const m5 = 30.1;
const m6 = 19.9;

当然,这肯定不是最好的方案。按照这种方式,如果只存一年的数据,我们能管理12个变量。

若要多存几年的平均温度呢?幸运的是,我们可以用数组来解决,更加简洁地呈现同样的信息。


 const ms = [];
 ms[0] = 31.1;
 ms[1] = 34.2;
 ms[2] = 28.8;
 ms[3] = 32.2;
 ms[4] = 30.1;
 ms[5] = 19.9;

这个时候数组ms里的内容就如下图所示了:

二、JavaScript的数组

  • 创建和初始化数组

用JavaScript声明、创建和初始化数组很简单,就像下面这样。


let arr = new Array(); //方法1
arr = new Array(7);	//方法2
arr = new Array('a','b','c','d'); //方法3
arr = ['a','b','c','d']; //方法4

  1. 使用new关键字,就能简单地声明并初始化一个数组(方法1)
  2. 用这种方式,还可以创建一个指定长度的数组(方法2)
  3. 另外,我们也可以直接将数组元素作为参数传递给它的构造器(方法3)
  4. 最后也可以简单粗暴的使用中括号[] (方法4)

简单粗暴灵活,推荐方法为第四种,不推荐使用new Array创建。

  • 访问元素和迭代数组

要访问数组里特定位置的元素,可以用中括号传递数值位置,得到想知道的值或者赋新的值。假如我们想输出数组daysofweek里的所有元素,可以通过循环选代数组、打印元素,如下所示。

for (let i = 0; 1 < arr.length; i++)
	console.log(arr[i]);


  • 添加元素

在数组中添加和删除元素也很容易,但是有时候也会很棘手。假如我们有这样的一个数组 numbers,初始化成了 0 到 9 。

let numbers = [0,1,2,3,4,5,6,7,8,9];

在数组末尾插入一个元素

如果想要给数组添加一个元素,例如添加一个10,只要把值赋值给数组中最后一个空位上的元素即可。

numbers[numbers.length] = 10;

需要注意的是:

在JavaScript中,数组是一个可以修改的对象。如果添加元素,它就会动态增长。在C和Java等其他语言里,我们要决定数组的大小,想添加元素就要创建一个全新的数组,不能简单地往其中添加所需的元素。

使用push方法

另外,还有一个push方法,能把元素添加到数组的末尾。通过push方法,我们能添加任意个元素。


numbers.push(11);
numbers.push(12,13);
console.log(numbers);

在数组的开头插入元素

现在,我们希望在数组中插入一个新元素(数-1),不像之前那样插人到最后,而是放到数组的开头。为了实现这个需求,首先要腾出数组里第一个元素的位置,把所有的元素向右移动一位。

我们可以循环数组中的元素,从最后一位(长度值就是数组的末尾位置)开始,将对应的前一个元素(i-1)的值赋给它(i),依次处理,最后把我们想要的值赋给第一个位置(索引0)上。我们可以将这段逻辑写成一个函数,甚至将该方法直接添加在Array的原型上,使所有数组的实例都可以访问到该方法。

下面的代码表现了这段逻辑。


Array.prototype.insertFirstPosition = function(value)
	for(let i = this.length; i >=0; i++)
		this[i] = this[i - 1];
	
	this[0] = value;


numbers.insertFirstPosition(-1);

下图描述一下操作过程:

使用unshift方法

在JavaScripr里,数组有一个方法叫 unshift,可以直接把数值插入数组的开头(此方法背后的逻辑和insertFirstPosition方法的行为是一样的)。


numbers.unshift(-2);
numbers.unshift(-4,-3);

那么,用unshift方法,我们就可以在数组的开始处添加值-2,然后添加-3、-4等。这样数组就会输出数-4到13。

  • 删除元素

目前为止,我们已经学习了如何给数组的开始和结尾位置添加元素。下面来看一下怎样从数组中删除元素。

从数组末尾删除元素

要删除数组里最靠后的元素,可以用pop方法。

numbers.pop();

小技巧:

通过push和pop方法,就能用数组来模拟栈。剧透:你将在下一章看到这部分内容(栈结构)。

现在,数组输出的数是-4到12,数组的长度是17。

从数组开头删除元素

如果要移除数组里的第一个元素,可以用下面的代码。


for(let i = 0; i < numbers.length; i++)
	numbers[i] = numbers[i + 1];


下图呈现了这段代码的执行过程。

我们把数组里所有的元素都左移了一位,但数组的长度依然是17,这意味着数组中有额外的一个元素(值是 undefined)。

在最后一次循环里,i+1引用了数组里还未初始化的一个位置。

在Java、C/C+或C#等一些语言里,这样写可能会抛出异常,因此不得不在nunbers.length-1处停止循环。

可以看到,我们只是把数组第一位的值用第二位覆盖了,并没有删除元素(因为数组的长度和之前还是一样的,并且多了一个未定义元素)。

要从数组中移除这个值,还可以创建一个包含刚才所讨论逻辑的方法,叫作removeFirstPosition。但是,要真正从数组中移除这个元素,我们需要创建一个新的数组,将所有不是undefined 的值从原来的数组复制到新的数组中,并且将这个新的数组赋值给我们的数组。

要完成这项工作,也可以像下面这样创建一个reIndex方法。


Array.prototype.reIndex = function(array)
	const newArr = [];
	for(let i = 0; i < array.length; i++)
		if(array[i] !== undefined)
			newArr.push(array[i]);
		
	
	return newArr;


// 手动移除第一个元素并排序
Array.prototype.removeFirstPosition = function()
	const newArr = [];
	for(let i = 0; i < this.length; i++)
		this[i] = this[i + 1]
	
	return this.reIndex(this);


numbers = numbers.removeFirstPosition();

注意:

上面的代码只应该用作示范,不应该在真实项目中使用。要从数组开头删除元素,我们应该始终使用shift方法,这将在下一节中展示。

使用shift方法

要删除数组的第一个元素,可以用shift方法实现。

numbers.shift();

假如本来数组中的值是从-4到12,长度为17。执行了上述代码后,数组就只有-3到12了也会减小到16。

通过shift和unshift方法,就能用数组模拟基本的队列数据结构,后面会讲到。

  • 任意位置添加或删除元素

目前为止,我们已经学习了如何添加元素到数组的开头或末尾,以及怎样删除数组开头和结尾位置上的元素。那么如何在数组中的任意位置上删除或添加元素呢?

我们可以使用splice方法,简单地通过指定位置/索引,就可以删除相应位置上指定数量3的元素。

numbers.splice(5,3);

这行代码删除了从数组索引5开始的3个元素。这就意味着numbers[5]、numbers[6]和numbers[7]从数组中删除了。现在数组里的值变成了-3、-2、-1、0、1、5、6、7、8、9、10、11和12(2、3、4已经被移除)。

对于JavaScript 数组和对象,我们还可以用delete 运算符删除数组中的元素,例如 delete numbers [0]。然而,数组位置0的值会变成 undefined,也就是说,以上操作等同于numbers[0] = undefined。因此,我们应该始终使用splice、pop或 shift方法来删除数组元素。

现在,我们想把数2、3、4插人数组里,放到之前删除元素的位置上,可以再次使用splice方法。

numbers,splice(5,0,2,3,4);

splice方法接收的第一个参数,表示想要删除或插入的元素的索引值。

第二个参数是删除元素的个数(这个例子里,我们的目的不是删除元素,所以传入0)。

第三个参数往后,就是要添加到数组里的值(元素2、3、4)。

输出会发现值又变成了从-3到12。

最后,执行以下这行代码。

numbers.splice(5,3,2,3,4);

输出的值是从-3到12。原因在于,我们从索引5开始删除了3个元素,但也从索引引5开始添加了元素2、3、4。

  • 二维数组和多维数组

还记得本章开头平均气温测量的例子吗?现在我打算再用一下这个例子,不过把记录的数据改成数天内每小时的气温。现在我们已经知道可以用数组来保存这些数据,那么要保存两天内每小时的气温数据就可以这样做。


let ms1 = [72,75,79,79,81,81];
let ms2 = [81,79,75,75,73,72];

然而,这不是最好的方法,还可以做得更好。我们可以使用矩阵(二维数组,或数组的数组来存储这些信息。矩阵的行保存每天的数据,列对应小时级别的数据。


let msTemp = [];
msTemp[0] = [72,75,79,79,81,81];
msTemp[1 = [81,79,75,75,73,72];

JavaScript只支持一维数组,并不支持矩阵。但是,我们可以像上面的代码一样,用数组套数组,实现矩阵或任一多维数组。代码也可以写成如下这样。

// day 1
msTemp[0] = [];
msTemp[0][0] = 72;
msTemp[0][1] = 75;
msTemp[0][2] = 79
msTemp[0][3] = 79;
msTemp[0][4] = 81;
msTemp[0][5] = 81;
// day 2
msTemp[1] = [];
msTemp[1][0] = 81;
msTemp[1][1] = 79;
msTemp[1][2] = 75
msTemp[1][3] = 75;
msTemp[1][4] = 73;
msTemp[1][5] = 72;

上面的代码里,我们分别指定了每天和每小时的数据。数组中的内容如下图所示。


每行就是每天的数据,每列是当天不同时段的气温。

要在浏览器控制台中打印二维数组,还可以使用 console.table(msTemp) 语句。它会显示一个更加友好的输出结果。

多维数组

我们也可以用这种方式来处理多维数组。假设我们要创建一个3×3×3的矩阵,每一格里包含矩阵的i(行)、j(列)及z(深度)之和。

const matrix3x3x3 = [];

for(let i=0;i<3;i++)
	matrix3x3x3[i] = [];
	for(let j=0;j<3;j++)
		matrix3x3x3[i][j] = [];
		for(let z=0;z<3;z++)
			matrix3x3x3[i][j][z] = [];
		
	


数据结构中有几个维度都没关系,我们可以用循环迭代每个维度来访问所有格子。

3×3×3的矩阵立体图如下所示。

手绘功底有限,我已经尽力了。。。 这立体图实在画不正。。。【/捂脸】

三、JavaScript数组方法参考

方法描述
concat连接2个或更多数组,并返回结果
every对数组中的每个元素运行给定函数,如果该函数对每个元素都返回true,则返回true
filter对数组中的每个元素运行给定函数,返回该函数会返回true的元素组成的数组
forEach对数组中的每个元素运行给定函数。这个方法没有返回值
join将所有的数组元素连接成一个字符串返回
indexOf第一个与给定参数相等的数组元素的索引,没有找到则返回-1
lastIndexOf返回在数组中搜索到的与给定参数相等的元素的索引里最大的值
map对数组中的每个元素运行给定函数,返回每次函数调用的结果组成的数组
reverse颠倒数组中元素的顺序,原先第一个元素现在变成最后一个,同样原先的最后一个元素变成了现在的第一个
slice传入索引值,将数组里对应索引范围内的元素作为新数组返回
some对数组中的每个元素运行给定函数,如果任一元素返回true,则返回true
sort按照字母顺序对数组排序,支持传入指定排序方法的函数作为参数
toString将数组作为字符串返回
valueOf和tostring类似,将数组作为字符串返回

下表列出了ES2015和ES2016新增的数组方法。

方法描述
@@iterator返回一个包含数组键值对的选代器对象,可以通过同步调用得到数组元素的键值对
copyWithin复制数组中一系列元素到同一数组指定的起始位置
entries返回包含数组所有键值对的@@iterator
includes如果数组中存在某个元素则返回true,否则返回false
find根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素
findIndex根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素在数组中的索引
fill用静态值填充数组
from根据已有数组创建一个新数组
keys返回包含数组所有索引的@@iterator
of根据传入的参数创建一个新数组
values返回包含数组中所有值的@@iterator

本章的方法仅供参考,万一本文年代久远了,可能就会缺失新增的方法。

四、本章小结

本章,我们学习了最常用的数据结构:数组。不仅学习了如何声明和初始化数组、给数组赋值,以及添加和删除数组元素,还学习了二维、多维数组以及数组的主要方法。这对我们在后面章节中编写自己的算法很有用。

由于时间问题,就没有对每个数组的方法进行演示,若有不懂的地方,需要的话可以去“菜鸟教程网”看一下每种数组方法具体的用法。

五、写在后面

这作为一个JavaScript数据结构与算法的数组篇文章,主要目的是让学习JavaScrip的数组结构,以及常用的数组操作方法,把本文读懂,在后面的“栈”数据结构中会有很大的益处,而且在项目开发中,数组也是常用的数据结构之一,祝您生活学习愉快,本系列也会持续进行更新的。

有问题请留言或者@博主,谢谢支持o( ̄︶ ̄)o~

感谢您的阅读,如果此文章或项目对您有帮助,若可以的话请给个一键三连吧!

GitHub有开源项目,需要的小伙伴可以顺手star一下!

GitHub: https://github.com/langyuxiansheng

以上是关于手把手5分钟入门JavaScript数组,万字文超详细手绘操作执行过程②JavaScript数据结构与算法系列的主要内容,如果未能解决你的问题,请参考以下文章

手把手5分钟入门JavaScript数组,万字文超详细手绘操作执行过程②JavaScript数据结构与算法系列

万字文肝Java基础知识

万字文肝Python基础知识

万字文肝Python基础知识

肝了4.5万字,手把手带你玩转JavaScript(建议收藏)

肝了4.5万字,手把手带你玩转JavaScript(建议收藏)