floatmap 二维数组_第 2 章 数组

第2 章 数组

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

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

2.1 为什么用数组

假如有这样一个需求:保存所在城市每个月的平均温度。可以这么做:

var averageTempJan = 31.9;

var averageTempFeb = 35.3;

var averageTempMar = 42.4;

var averageTempApr = 52;

var averageTempMay = 60.8;

当然,这肯定不是最好的方案。按照这种方式,如果只存一年的数据,我们能管理12个变量。但要多存几年的平均温度呢?幸运的是,我们可以用数组来解决,更加简洁地呈现同样的信息:

var averageTemp = [];

averageTemp[0] = 31.9;

averageTemp[1] = 35.3;

averageTemp[2] = 42.4;

averageTemp[3] = 52;

averageTemp[4] = 60.8;

数组averageTemp里的内容如下图所示:

2.2 创建和初始化数组

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

var daysOfWeek = new Array(); //{1}

var daysOfWeek = new Array(7); //{2}

var daysOfWeek = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday',

'Thursday', 'Friday', 'Saturday'); //{3}

使用new关键字,就能简单地声明并初始化一个数组(行{1})。用这种方式,还可以创建一个指定长度的数组(行{2})。另外,也可以直接将数组元素作为参数传递给它的构造器(行{3})。

其实,用new创建数组并不是最好的方式。如果你想在JavaScript中创建一个数组,只用中括号([])的形式就行了,如下所示:

var daysOfWeek = [];

也可使用一些元素初始化数组,如下:

var daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',

'Thursday', 'Friday', 'Saturday'];

如果想知道数组里已经存了多少个元素,可以使用数组的length属性。以下代码的输出是7:

console.log(daysOfWeek.length);

访问元素和迭代数组

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

for (var i=0; i

console.log(daysOfWeek[i]);

}

我们来看另一个例子:求斐波那契数列的前20个数字。已知斐波那契数列中第一个数字是1,第二个是2,从第三项开始,每一项都等于前两项之和:

var fibonacci = []; //{1}

fibonacci[1] = 1; //{2}

fibonacci[2] = 2; //{3}

for(var i = 3; i < 20; i++){

fibonacci[i] = fibonacci[i-1] + fibonacci[i-2]; {4}

}

for(var i = 1; i

console.log(fibonacci[i]); //{6}

}

在行{1}处,我们声明并创建了一个数组。

在行{2}和行{3},把斐波那契数列中的前两个数字分别赋给了数组的第二和第三位(在JavaScript中,数组的第一位是0,这里我们略过,从第二位开始分别保存斐波那契数列中对应位置的元素)。

然后,我们需要做的就是想办法得到斐波那契数列的第三到第二十位的数字(前两个值我们已经初始化过了)。我们可以用循环来处理,把数组中前两位上的元素相加,结果赋给当前位置上的元素(行{4}——从数组中的索引3到索引19)。

最后,看看输出(行{6}),我们只需要循环遍历数组的各个元素(行{5})。

 示例代码里,我们用console.log来输出数组中对应索引位置的值(行{5}和行{6}),也可以直接用console.log(fibonacci)输出数组。大多数浏览器都可以用这种方式,清晰地输出数组。

现在如果想知道斐波那契数列其他位置上的值是多少,要怎么办呢?很简单,把之前循环条件中的终止变量从20改成你希望的值就可以了。

2.3 添加元素

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

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

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

numbers[numbers.length] = 10;

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

2.3.1 使用push方法

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

numbers.push(11);

numbers.push(12, 13);

如果输出numbers的话,就会看到从0到13的值。

2.3.2 插入元素到数组首位

现在,我们希望在数组中插入一个值,不像之前那样插入到最后,而是放到数组的首位。为了实现这个需求,首先我们要腾出数组里第一个元素的位置,把所有的元素向右移动一位。我们可以循环数组中的元素,从最后一位+1(长度)开始,将其对应的前一个元素的值赋给它,依次处理,最后把我们想要的值赋给第一个位置(-1)上。

for (var i=numbers.length; i>=0; i--){

numbers[i] = numbers[i-1];

}

numbers[0] = -1;

下面这张图描述了我们刚才的操作过程:

使用unshift方法

在JavaScript里,数组有一个方法叫unshift,可以直接把数值插入数组的首位:

numbers.unshift(-2);

numbers.unshift(-4, -3);

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

2.4 删除元素

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

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

numbers.pop();

 通过push和pop方法,就能用数组来模拟栈,你将会在下一章看到这部分内容。

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

从数组首位删除元素

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

for (var i = 0; i < numbers.length; i++){

numbers[i] = numbers[i+1];

}

下面这张图呈现了这段代码的执行过程:

我们把数组里所有的元素都左移了一位。但数组的长度依然是17,这意味着数组中有额外的一个元素(值是undefined)。在最后一次循环里,i + 1引用了一个数组里还未初始化的位置。在Java、C/C+或C#等一些语言里,这样写可能就会抛出异常了,因此不得不在 numbers.length - 1处停止循环。

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

使用shift方法

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

numbers.shift();

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

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

2.5 在任意位置添加或删除元素

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

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

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。

2.6 二维和多维数组

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

var averageTempDay1 = [72,75,79,79,81,81];

var averageTempDay2 = [81,79,75,75,73,72];

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

var averageTemp = [];

averageTemp[0] = [72,75,79,79,81,81];

averageTemp[1] = [81,79,75,75,73,72];

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

//day 1

averageTemp[0] = [];

averageTemp[0][0] = 72;

averageTemp[0][1] = 75;

averageTemp[0][2] = 79;

averageTemp[0][3] = 79;

averageTemp[0][4] = 81;

averageTemp[0][5] = 81;

//day 2

averageTemp[1] = [];

averageTemp[1][0] = 81;

averageTemp[1][1] = 79;

averageTemp[1][2] = 75;

averageTemp[1][3] = 75;

averageTemp[1][4] = 73;

averageTemp[1][5] = 72;

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

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

2.6.1 迭代二维数组的元素

如果想看这个矩阵的输出,我们可以创建一个通用函数,专门输出其中的值:

function printMatrix(myMatrix) {

for (var i=0; i

for (var j=0; j

console.log(myMatrix[i][j]);

}

}

}

需要遍历所有的行和列。因此,我们需要使用一个嵌套的for循环来处理,其中变量i为行,变量j为列。

使用以下代码查看矩阵averageTemp的输出:

printMatrix(averageTemp);

2.6.2 多维数组

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

var matrix3x3x3 = [];

for (var i=0; i<3; i++){

matrix3x3x3[i] = [];

for (var j=0; j<3; j++){

matrix3x3x3[i][j] = [];

for (var z=0; z<3; z++){

matrix3x3x3[i][j][z] = i+j+z;

}

}

}

数据结构中有几个维度都没关系,我们都可以用循环遍历每个维度来访问所有格子。3×3×3的矩阵也可用立体图表示如下:

可以用以下代码输出这个矩阵的内容:

for (var i=0; i

for (var j=0; j

for (var z=0; z

console.log(matrix3x3x3[i][j][z]);

}

}

}

如果是一个3×3×3×3的矩阵,代码中就会用四层嵌套的for语句,以此类推。

2.7 JavaScript的数组方法参考

在JavaScript里,数组是可修改的对象,这意味着创建的每个数组都有一些可用的方法。数组很有趣,因为它们十分强大,并且相比其他语言中的数组,JavaScript中的数组有许多很好用的方法。这样就不用再为它开发一些基本功能了,例如在数据结构的中间添加或删除元素。

下面的表格中详述了数组的一些核心方法,其中的一些我们已经学习过了。

方法名

描述

concat

连接2个或更多数组,并返回结果

every

对数组中的每一项运行给定函数,如果该函数对每一项都返回true,则返回true

filter

对数组中的每一项运行给定函数,返回该函数会返回true的项组成的数组

forEach

对数组中的每一项运行给定函数。这个方法没有返回值

join

将所有的数组元素连接成一个字符串

indexOf

返回第一个与给定参数相等的数组元素的索引,没有找到则返回

1

lastIndexOf

返回在数组中搜索到的与给定参数相等的元素的索引里最大的值

map

对数组中的每一项运行给定函数,返回每次函数调用的结果组成的数组

reverse

颠倒数组中元素的顺序,原先第一个元素现在变成最后一个,同样原先的最后一个元素变成了现在的第一个

slice

传入索引值,将数组里对应索引范围内的元素作为新数组返回

some

对数组中的每一项运行给定函数,如果任一项返回true,则返回true

sort

按照字母顺序对数组排序,支持传入指定排序方法的函数作为参数

toString

将数组作为字符串返回

valueOf

和toString类似,将数组作为字符串返回

我们已经学过了push、pop、shift、unshift和splice方法。下面来看表格中提到的方法。在本书接下来的章节里,编写数据结构和算法时会大量用到这些方法。

2.7.1 数组合并

考虑如下场景:有多个数组,需要合并起来成为一个数组。我们可以迭代各个数组,然后把每个元素加入最终的数组。幸运的是,JavaScript已经给我们提供了解决方法,叫作concat方法:

var zero = 0;

var positiveNumbers = [1,2,3];

var negativeNumbers = [-3,-2,-1];

var numbers = negativeNumbers.concat(zero, positiveNumbers);

concat方法可以向一个数组传递数组、对象或是元素。数组会按照该方法传入的参数顺序连接指定数组。在这个例子里,zero将被合并到nagativeNumbers中,然后positiveNumbers继续被合并。最后输出的结果是-3、-2、-1、0、1、2、3。

2.7.2 迭代器函数

有时我们需要迭代数组中的元素。前面我们已经学过,可以用循环语句来处理,例如for语句。

JavaScript内置了许多数组可用的迭代方法。对于本节的例子,我们需要数组和函数。假如有一个数组,它值是从1到15,如果数组里的元素可以被2整除(偶数),函数就返回true,否则返回false:

var isEven = function (x) {

// 如果x是2的倍数,就返回true

console.log(x);

return (x % 2 == 0) ? true : false;

};

var numbers = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];

 return (x % 2 == 0) ? true : false也可以写成return (x % 2== 0)。

用every方法迭代

我们要尝试的第一个方法是every。every方法会迭代数组中的每个元素,直到返回false。

numbers.every(isEven);

在这个例子里,数组numbers的第一个元素是1,它不是2的倍数(1是奇数), 因此isEven 函数返回false,然后every执行结束。

用some方法迭代

下一步,我们来看some方法。它和every的行为类似,不过some方法会迭代数组的每个元素,直到函数返回true:

numbers.some(isEven);

在我们的例子里,numbers数组中第一个偶数是2(第二个元素)。第一个被迭代的元素是1,isEven会返回false。第二个被迭代的元素是2,isEven返回true——迭代结束。

用forEach方法迭代

如果要迭代整个数组,可以用forEach方法。它和使用for循环的结果相同:

numbers.forEach(function(x){

console.log((x % 2 == 0));

});

使用map和filter方法

JavaScript还有两个会返回新数组的遍历方法。第一个是map:

var myMap = numbers.map(isEven);

数组myMap里的值是:[false, true, false, true, false, true, false, true, false, true, false, true, false, true, false]。它保存了传入map方法的isEven函数的运行结果。这样就很容易知道一个元素是否是偶数。比如,myMap[0]是false,因为1不是偶数;而myMap[1]是true,因为2是偶数。

还有一个filter方法。它返回的新数组由使函数返回true的元素组成:

var evenNumbers = numbers.filter(isEven);

在我们的例子里,evenNumbers数组中的元素都是偶数:[2, 4, 6, 8, 10, 12, 14]。

使用reduce方法

最后是reduce方法。reduce方法接收一个函数作为参数,这个函数有四个参数:previousValue、currentValue、index和array。这个函数会返回一个将被叠加到累加器的值,reduce方法停止执行后会返回这个累加器。如果要对一个数组中的所有元素求和,这就很有用,比如:

numbers.reduce(function(previous, current, index){

return previous + current;

});

输出将会是120。

 JavaScript的Array类还有另外两个重要方法:map和reduce。这两个方法名是自解释的,这意味着map方法会依照给定函数对值进行映射,而reduce方法会依照函数规约数组包含的值。这三个方法(map、filter和reduce)是我们要在第11章学习的JavaScript函数式编程的基础。

2.7.3 ECMAScript 6和数组的新功能

第1章提到过,ECMAScript 6(ES6或ES2015)和ECMAScript 7(ES7或ES2016)规范给JavaScript语言带来了新的功能。

下表列出了ES6和ES7新增的数组方法。

方法

描述

@@iterator

返回一个包含数组键值对的迭代器对象,可以通过同步调用得到数组元素的键值对

copyWithin

复制数组中一系列元素到同一数组指定的起始位置

entries

返回包含数组所有键值对的@@iterator

includes

如果数组中存在某个元素则返回true,否则返回false。ES7新增

find

根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素

findIndex

根据回调函数给定的条件从数组中查找元素,如果找到则返回该元素在数组中的索引

fill

用静态值填充数组

from

根据已有数组创建一个新数组

keys

返回包含数组所有索引的@@iterator

of

根据传入的参数创建一个新数组

values

返回包含数组中所有值的@@iterator

除了这些新的方法,还有一种用for...of循环来迭代数组的新做法,以及可以从数组实例得到的迭代器对象。

在后面的主题中,我们会演示所有的新功能。

使用forEach和箭头函数迭代

箭头函数可以简化使用forEach迭代数组元素的做法。代码例子如下:

numbers.forEach(function (x) {

console.log(x % 2 == 0);

});

这段代码可以简化如下:

numbers.forEach(x => {

console.log((x % 2 == 0));

});

使用for...of循环迭代

你已经学过用for循环和forEach方法迭代数组。ES6还引入了迭代数组值的for...of循环,来看看它的用法:

for (let n of numbers) {

console.log((n % 2 == 0) ? 'even' : 'odd');

}

使用ES6新的迭代器(@@iterator)

ES6还为Array类增加了一个@@iterator属性,需要通过Symbol.iterator来访问。代码如下:

let iterator = numbers[Symbol.iterator]();

console.log(iterator.next().value); // 1

console.log(iterator.next().value); // 2

console.log(iterator.next().value); // 3

console.log(iterator.next().value); // 4

console.log(iterator.next().value); // 5

然后,不断调用迭代器的next方法,就能依次得到数组中的值。numbers数组中有15个值,因此需要调用15次iterator.next().value。

数组中所有值都迭代完之后,iterator.next().value会返回undefined。

以上代码的输出和我们接下来要讲的numbers.value()是一样的。

数组的entries、keys和values方法

ES6还增加了三种从数组中得到迭代器的方法。我们首先要学习的是entries方法。

entries方法返回包含键值对的@@iterator,下面是使用这个方法的代码示例:

let aEntries = numbers.entries(); // 得到键值对的迭代器

console.log(aEntries.next().value); // [0, 1] - 位置0的值为1

console.log(aEntries.next().value); // [1, 2] - 位置1的值为2

console.log(aEntries.next().value); // [2, 3] - 位置2的值为3

numbers数组中都是数字,key是数组中的位置,value是保存在数组索引的值。

使用集合、字典、散列表等数据结构时,能够取出键值对是很有用的。这个功能会在本书后面的章节中大显身手。

keys方法返回包含数组索引的@@iterator,下面是使用这个方法的代码示例:

let aKeys = numbers.keys(); // 得到数组索引的迭代器

console.log(aKeys.next()); // {value: 0, done: false }

console.log(aKeys.next()); // {value: 1, done: false }

console.log(aKeys.next()); // {value: 2, done: false }

keys方法会返回numbers数组的索引。一旦没有可迭代的值,aKeys.next()就会返回一个value属性为undefined,done属性为true的对象。如果done属性的值为false,就意味着还有可迭代的值。

values方法返回的@@iterator则包含数组的值。使用这个方法的代码示例如下:

let aValues = numbers.values();

console.log(aValues.next()); // {value: 1, done: false }

console.log(aValues.next()); // {value: 2, done: false }

console.log(aValues.next()); // {value: 3, done: false }

 记住,当前的浏览器还没有完全支持ES6所有的新功能,因此,测试这些代码最好的办法是使用Babel。访问https://goo.gl/eojEGk查看和运行示例。

使用from方法

Array.from方法根据已有的数组创建一个新数组。比如,要复制numbers数组,可以这样做:

et numbers2 = Array.from(numbers);

还可以传入一个用来过滤值的函数,例子如下:

let evens = Array.from(numbers, x => (x % 2 == 0));

上面的代码会创建一个evens数组,其中只包含numbers数组中的偶数。

使用Array.of方法

Array.of方法根据传入的参数创建一个新数组。以下面的代码为例:

let numbers3 = Array.of(1);

let numbers4 = Array.of(1, 2, 3, 4, 5, 6);

它和下面这段代码的效果一样:

let numbers3 = [1];

let numbers4 = [1, 2, 3, 4, 5, 6];

我们也可以用这个方法复制已有的数组,比如:

let numbersCopy = Array.of(...numbers4);

上面的代码和Array.from(numbers4)的效果是一样的,区别只是用到了第1章讲过的展开操作符。展开操作符(...)会把numbers4数组里的值都展开成参数。

使用fill方法

fill方法用静态值填充数组。以下面的代码为例:

let numbersCopy = Array.of(1, 2, 3, 4, 5, 6);

numbersCopy数组的length是6,也就是有6个位置。再看下面的代码:

numbersCopy.fill(0);

numbersCopy数组所有位置的值都会变成0([0, 0, 0, 0, 0, 0])。

我们还可以指定开始填充的索引,如下:

numbersCopy.fill(2, 1);

上面的例子里,数组中从1开始的所有位置,值都是2([0, 2, 2, 2, 2, 2])。

同样,也可以指定结束填充的索引:

numbersCopy.fill(1, 3, 5);

上面的例子里,我们会把1填充到数组索引3到5的位置(不包括5),得到的数组为[0, 2, 2, 1, 1, 2]。

创建数组并初始化值的时候,fill方法非常好用,就像下面这样:

let ones = Array(6).fill(1);

上面的代码创建了一个长度为6,所有的值都是1的数组([1, 1, 1, 1, 1, 1])。

使用copyWithin方法

copyWithin方法复制数组中的一系列元素到同一数组指定的起始位置。看看下面这个例子:

let copyArray = [1, 2, 3, 4, 5, 6];

假如我们想把4、5、6三个值复制到数组前三个位置,得到[4, 5, 6, 4, 5, 6]这个数组。可以用下面的代码达到目的:

copyArray.copyWithin(0, 3);

假如我们想把4、5两个值(位置3和4)复制到位置1和2,可以这样做:

copyArray = [1, 2, 3, 4, 5, 6];

copyArray.copyWithin(1, 3, 5);

这种情况下,会把从位置3开始到位置5结束(不包括5)的元素复制到位置1,结果是得到数组[1, 4, 5, 4, 5, 6]。

2.7.4 排序元素

通过本书,我们能学到如何编写最常用的搜索和排序算法。其实,JavaScript里也提供了一个排序方法和一组搜索方法。让我们来看看。

首先,我们想反序输出数组numbers(它本来的排序是1, 2, 3, 4,…15)。要实现这样的功能,可以用reverse方法,然后数组内元素就会反序。

numbers.reverse();

现在,输出numbers的话就会看到[15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]。然后,我们用sort方法:

numbers.sort();

然而,如果输出数组,结果会是[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]。看起来不大对,是吧?这是因为sort方法在对数组做排序时,把元素默认成字符串进行相互比较。

我们可以传入自己写的比较函数。因为数组里都是数字,所以可以这样写:

numbers.sort(function(a, b){

return a-b;

});

这段代码,在b大于a时,会返回负数,反之则返回正数。如果相等的话,就会返回0。也就是说返回的是负数,就说明a比b小,这样sort就根据返回值的情况给数组做排序。

之前的代码也可以被表示成这样,会更清晰一些:

function compare(a, b) {

if (a < b) {

return -1;

}

if (a > b) {

return 1;

}

// a必须等于b

return 0;

}

numbers.sort(compare);

这是因为JavaScript的sort方法接受compareFunction作为参数,然后sort会用它排序数组。在例子里,我们声明了一个用来比较数组元素的函数,使数组按升序排序。

自定义排序

我们可以对任何对象类型的数组排序,也可以创建compareFunction来比较元素。例如,对象Person有名字和年龄属性,我们希望根据年龄排序,就可以这么写:

var friends = [

{name: 'John', age: 30},

{name: 'Ana', age: 20},

{name: 'Chris', age: 25}

];

function comparePerson(a, b){

if (a.age < b.age){

return -1

}

if (a.age > b.age){

return 1

}

return 0;

}

console.log(friends.sort(comparePerson));

在这个例子里,最后会输出Ana(20), Chris(25), John(30)。

字符串排序

假如有这样一个数组:

var names =['Ana', 'ana', 'john', 'John'];

console.log(names.sort());

你猜会输出什么?答案是这样的:

["Ana", "John", "ana", "john"]

既然a在字母表里排第一位,为何ana却排在了John之后呢?这是因为JavaScript在做字符比较的时候,是根据字符对应的ASCII值来比较的。例如,A、J、a、j对应的ASCII值分别是65、75、97、106。

虽然在字母表里a是最靠前的,但J的ASCII值比a的小,所以排在a前面。

现在,如果给sort传入一个忽略大小写的比较函数,将会输出["Ana", "ana", "John", "john"]:

names.sort(function(a, b){

if (a.toLowerCase() < b.toLowerCase()){

return -1

}

if (a.toLowerCase() > b.toLowerCase()){

return 1

}

return 0;

});

假如对带有重音符号的字符做排序的话,我们可以用localCompare来实现:

var names2 = ['Maève', 'Maeve'];

console.log(names2.sort(function(a, b){

return a.localCompare(b);

}));

最后输出的结果将是["Maeve", "Maève"]。

2.7.5 搜索

搜索有两个方法:indexOf方法返回与参数匹配的第一个元素的索引,lastIndexOf返回与参数匹配的最后一个元素的索引。我们来看看之前用过的numbers数组:

console.log(numbers.indexOf(10));

console.log(numbers.indexOf(100));

在这个示例中,第一行的输出是9,第二行的输出是-1(因为100不在数组里)。

下面的代码会返回同样的结果:

numbers.push(10);

console.log(numbers.lastIndexOf(10));

console.log(numbers.lastIndexOf(100));

我们往数组里加入了一个新的元素10,因此第二行会输出15(数组中的元素是1到15,还有10),第三行会输出-1(因为100不在数组里)。

ECMAScript 6——find和findIndex方法

看看下面这个例子:

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];

function multipleOf13(element, index, array) {

return (element % 13 == 0) ? true : false;

}

console.log(numbers.find(multipleOf13));

console.log(numbers.findIndex(multipleOf13));

find和findIndex方法接收一个回调函数,搜索一个满足回调函数条件的值。上面的例子里,我们要从数组里找一个13的倍数。

find和findIndex的不同之处在于,find方法返回第一个满足条件的值,findIndex方法则返回这个值在数组里的索引。如果没有满足条件的值,find会返回undefined,而findIndex返回-1。

ECMAScript 7——使用includes方法

如果数组里存在某个元素,includes方法会返回true,否则返回false。使用includes方法的例子如下:

console.log(numbers.includes(15));

console.log(numbers.includes(20));

例子里的includes(15)返回true,includes(20)返回false,因为numbers数组里没有20。

如果给includes方法传入一个起始索引,搜索会从索引指定的位置开始:

let numbers2 = [7, 6, 5, 4, 3, 2, 1];

console.log(numbers2.includes(4, 5));

上面的例子输出为false,因为数组索引5之后的元素不包含4。

2.7.6 输出数组为字符串

现在,我们学习最后两个方法:toString和join。

如果想把数组里所有元素输出为一个字符串,可以用toString方法:

console.log(numbers.toString());

1、2、3、4、5、6、7、8、9、10、11、12、13、14、15和10这些值都会在控制台中输出。

如果想用一个不同的分隔符(比如-)把元素隔开,可以用join方法:

var numbersString = numbers.join('-');

console.log(numbersString);

这将输出:

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

如果要把数组内容发送到服务器,或进行编码(知道了分隔符,解码也很容易),这会很有用。

2.8 类型数组

与C和Java等其他语言不同,JavaScript数组不是强类型的,因此它可以存储任意类型的数据。

而类型数组则用于存储单一类型的数据。它的语法是let myArray = new TypedArray (length),其中TypedArray需替换为下表所列之一。

类型数组

数据类型

Int8Array

8位二进制补码整数

Uint8Array

8位无符号整数

Uint8ClampedArray

8位无符号整数

Int16Array

16位二进制补码整数

Uint16Array

16位无符号整数

Int32Array

32位二进制补码整数

Uint32Array

32位无符号整数

Float32Array

32位IEEE浮点数

Float64Array

64位IEEE浮点数

代码示例如下:

let length = 5;

let int16 = new Int16Array(length);

let array16 = [];

array16.length = length;

for (let i=0; i

int16[i] = i+1;

}

console.log(int16);

使用WebGL API、进行位操作、处理文件和图像时,类型数组都可以大展拳脚。它用起来和普通数组也毫无二致,本章所学的数组方法和功能都可以用于类型数组。

http://goo.gl/kZBsGx是一个很好的教程,讲解了如何使用类型数组处理二进制数据,以及它在实际项目中的应用。

2.9 小结

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

我们还学习了ES2015和ES2016规范新增的Array方法和功能。

下一章,我们将学习栈,一种具有特殊行为的数组。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值