本篇是关于 JavaScript 中 数组的有关用法 所编写的
目录
在数组中添加和删除元素也很容易,但有时也会很棘手。假如我们有一个数组 numbers,
7.3 数组的 entries、keys 和 values 方法
1. 为什么要使用数组
//假如有这样一个需求:保存所在城市每个月的平均温度。可以这么做:
const averageTempJan = 31.9;
const averageTempFeb = 35.3;
const averageTempMar = 42.4;
const averageTempApr = 52;
const averageTempMay = 60.8;
当然,这肯定不是最好的方案。按照这种方式,如果只存一年的数据,我们能管理
12
个变量。
若要多存几年的平均温度呢?幸运的是,我们可以用数组来解决,更加简洁地呈现同样的信息。
const averageTemp = [];
averageTemp[0] = 31.9;
averageTemp[1] = 35.3;
averageTemp[2] = 42.4;
averageTemp[3] = 52;
averageTemp[4] = 60.8;
2. 如何声明一个数组
let arr = new Array()
let arr1 = new Array(7)
let arr2 = new Array(1,2,3,4,5)
//使用new 关键字声明
console.log(arr2)
// 使用字面量直接声明 这两个 的结果是一样的
let arr3= []
//如果想知道一个数组的长度 , 每一个数组中都有一个 length属性
console.log(arr1.length)//5
访问元素和迭代数组(数组其实就是一个容器 , 就是能存贮 我们一个任意的 数据类型 , 然后方便我们以后去使用)
既然我们创建了一个数组 ,那么我们怎么才能访问到数组里面的变量呢
let daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday'];
// 可以使用for 循环来 遍历 数组中的每一个元素
for (let i = 0 ;i < daysOfWeek.length; i++ ){
console.log(daysOfWeek[i])
}
//我们来看另一个例子:求斐波那契数列的前 20 个数。已知斐波那契数列中的前两项是 1,
从第三项开始,每一项都等于前两项之和。
let fibonacci = []
for (let i = 0 ;i<20;i++ ){
if (i===0||i===1){
fibonacci[i]=1
}else {
fibonacci[i]=fibonacci[i-1]+fibonacci[i-2]
}
}
console.log(fibonacci)
console.log(fibonacci.length)
// [
// 1, 1, 2, 3, 5,
// 8, 13, 21, 34, 55,
// 89, 144, 233, 377, 610,
// 987, 1597, 2584, 4181, 6765
// ]
// 20
3.向数组中添加元素
在数组中添加和删除元素也很容易,但有时也会很棘手。假如我们有一个数组 numbers,
初始化成了
0
到
9
。
//在下面的数组开头中添加 -1
let numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
for (let i=numbers.length; i>=0;i--){
numbers[i]=numbers[i-1]
}
numbers[0]=-1
console.log( numbers)
3.1 数组末尾添加一个元素
如果想要给数组添加一个元素(比如
10
),只要把值赋给数组中最后一个空位上的元素即可。
numbers[numbers.length] = 10;
使用
push
方法
另外,还有一个
push
方法,能把元素添加到数组的末尾。通过
push 方法,我们能添加任
意个元素。
numbers.push(11);
numbers.push(12, 13);
如果输出
numbers
的话,就会看到从
0
到
13
的值。
3.2 在数组开头添加一个元素
如果想要再数组开头添加一个元素 我们只能 是吧每一个元素 向后挪一位 然后把把添加的元素赋值给开头的位置
使用
unshift
方法
在
JavaScript
里,数组有一个方法叫
unshift
,可以直接把数值插入数组的开头(此方法背
后的逻辑和
insertFirstPosition
方法的行为是一样的)。
numbers.unshift(-2);
numbers.unshift(-4, -3);
4 删除一个元素
目前为止,我们已经学习了如何给数组的开始和结尾位置添加元素。下面来看一下怎样从数
组中删除元素。
4.1 从数组末尾删除元素
要删除数组里最靠后的元素,可以用
pop
方法。
numbers.pop();
4.2 从数组开头删除元素
如果要移除数组里的第一个元素,可以用下面的代码。
for (let i = 0; i < numbers.length; i++) {
numbers[i] = numbers[i + 1];
}
我们把数组里所有的元素都左移了一位,但数组的长度依然是
17,这意味着数组中有额外
的一个元素(值是
undefined)。在最后一次循环里
里,
i+1
引用了数组里还未初始化的一个位置。
在
Java
、
C/C+
或
C#
等一些语言里,这样写可能会抛出异常,因此不得不在
numbers.length- 1
处停止循环
要从数组中移除这个值,还可以创建一个包含刚才所讨论逻辑的方法,叫作
removeFirst
Position
。但是,要真正从数组中移除这个元素,我们需要创建一个新的数组,将所有不是
undefined
的值从原来的数组复制到新的数组中,并且将这个新的数组赋值给我们的数组。要
完成这项工作,也可以像下面这样创建一个
reIndex
方法。
Array.prototype.reIndex = function(myArray) {
const newArray = [];
for(let i = 0; i < myArray.length; i++ ) {
if (myArray[i] !== undefined) {
// console.log(myArray[i]);
newArray.push(myArray[i]);
}
}
return newArray;
}
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
。
6.JavaScript 的数组方法参考
6.1数组合并
const zero = 0;
const positiveNumbers = [1, 2, 3];
const negativeNumbers = [-3, -2, -1];
let numbers = negativeNumbers.concat(zero, positiveNumbers);
concat
方法可以向一个数组传递数组、对象或是元素。数组会按照该方法传入的参数顺序
连接指定数组。在这个例子里,
zero
将被合并到
nagativeNumbers
中,然后
positiveNumbers
继续被合并。最后输出的结果是
3
、
2
、
1
、
0
、
1
、
2
和
3
。
6.2 迭代器函数
有时,我们需要迭代数组中的元素。前面已经学过,可以用循环语句来处理,例如
for
语句。
JavaScript
内置了许多数组可用的迭代方法。对于本节的例子,我们需要一个数组和一个函
数:假设数组中的值是从
1
到
15
;如果数组里的元素可以被
2
整除(偶数),函数就返回
true
,
否则返回
false
。
function isEven(x) {
//
如果
x
是
2
的倍数,就返回
true
console.log(x);
return x % 2 === 0 ? true : false;
}
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
简化代码
const isEven = x => x % 2 === 0;
1.
用
every
方法迭代
我们要尝试的第一个方法是
every
。
every
方法会迭代数组中的每个元素,直到返回
false
。
numbers.every(isEven);
在这个例子里,数组
numbers
的第一个元素是
1
,它不是
2
的倍数(
1
是奇数),因此
isEven
函数返回
false
,然后
every
执行结束。
2.用
some
方法迭代
下一步,我们来看
some
方法。它和
every
的行为相反,会迭代数组的每个元素,直到函
数返回
true
。
numbers.some(isEven);
在我们的例子里,
numbers
数组中第一个偶数是
2
(第二个元素)。第一个被迭代的元素
是
1
,
isEven
会返回
false
。第二个被迭代的元素是
2
,
isEven
返回
true
——
迭代结束。
3.
用
forEach
方法迭代
如果要迭代整个数组,可以用
forEach
方法。它和使用
for
循环的结果相同。
numbers.forEach(x => console.log(x % 2 === 0));
4.JavaScript
还有两个会返回新数组的迭代方法。第一个是
map
。
const 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
是偶数。
5. filter ( 返沪所有执行函数为true 的元素)
还有一个
filter
方法,它返回的新数组由使函数返回
true
的元素组成。
const evenNumbers = numbers.filter(isEven);
在我们的例子里,
evenNumbers
数组中的元素都是偶数:
[2, 4, 6, 8, 10, 12, 14]
。
6.
使用
reduce 方法(
-
- 减少;降低:例如,减少浪费(reduce waste)、降低亮度(reduce luminance)、减少成本(Reduce costs)。
- 使处于:使某人/某事物陷入某种(通常指更坏的)状态或状况中。
- 把...分解:将某事物概括或简化成某种形式,或将化合物还原。
)
最后是
reduce
方法。
reduce
方法接收一个有如下四个参数的函数:
previousValue
、
currentValue
、
index
和
array
。因为
index
和
array
是可选的参数,所以如果用不到它们
的话,可以不传。这个函数会返回一个将被叠加到累加器的值,
reduce
方法停止执行后会返回
这个累加器。如果要对一个数组中的所有元素求和,这就很有用。下面是一个例子。
numbers.reduce((previous, current) => previous + current);
输出将是
120
。
let numbers = [1,2,3,4,5,6,7,8,9]
let res = numbers.reduce((sum,next)=>{
sum=sum+next
return sum
},0)
console.log(res)
7. ECMAScript 6 和数组的新功能
7.1. 使用 for...of 循环迭代
你已经学过用
for
循环和
forEach
方法迭代数组。
ES2015
还引入了迭代数组值的
for...of
循环,下面来看看它的用法。
for (const n of numbers) {
console.log(n % 2 === 0 ? 'even' : 'odd');
}
let numbers = [1,2,3,4,5,6,7,8,9]
for(let i in numbers){
console.log(i)
}
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 8
7.2 使用@@iterator 对象
ES2015
还为
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
个
我们可以用下面的代码来输出
numbers
数组中的
15
个值。
iterator = numbers[Symbol.iterator]();
for (const n of iterator) {
console.log(n);}
7.3 数组的 entries、keys 和 values 方法
ES2015
还增加了三种从数组中得到迭代器的方法。我们首先要学习的是
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
是保存在数组索引的值。
我们也可以使用下面的代码。
aEntries = numbers.entries();
for (const n of aEntries) {
console.log(n);
}
使用集合、字典、散列表等数据结构时,能够取出键值对是很有用的。这个功能会在本书后
面的章节中大显身手。
keys
方法返回包含数组索引的
@@iterator
,下面是使用该方法的代码示例。
const 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
则包含数组的值。使用这个方法的代码示例如下。
const 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 }
7.4 使用 from 方法
Array.from
方法根据已有的数组创建一个新数组。比如,要复制
numbers
数组,可以如
下这样做。
let numbers2 = Array.from(numbers);
还可以传入一个用来过滤值的函数,例子如下。
let evens = Array.from(numbers, x => (x % 2 == 0));
上面的代码会创建一个
evens
数组,以及值
true
(如果在原数组中为偶数)或
false
(如
果在原数组中为奇数)。
7.5. 使用 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
数组里的值都展开成参数。
7.6 使用 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]
)。我们还可以指定
开始填充的索引,如下所示。
60
第
3
章 数组
numbersCopy.fill(2, 1);
上面的例子里,数组中从
1
开始的所有位置上的值都是
2
(
[0, 2, 2, 2, 2, 2]
)。
同样,我们也可以指定结束填充的索引。
numbersCopy.fill(1, 3, 5);
在上面的例子里,我们会把
1
填充到数组索引
3
到
5
的位置(不包括
3
和
5
),得到的数组为
[0, 2, 2, 1, 1, 2]
。
7.7 使用 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
结束(不包括
3
和
5
)的元素复制到位置
1
,结果
是得到数组
[1, 4, 5, 4, 5, 6]
copyWithin
是 JavaScript 数组的一个方法,用于在当前数组内部,将指定位置的成员复制到其他位置,并返回当前数组。这个方法会修改原数组。
copyWithin
方法接受三个参数,每个参数的含义如下:
- target (必需)
- 定义从哪个位置开始替换数组的元素。
- 如果该值为负数,则表示从末尾开始计算的索引。例如,-1 表示最后一个元素,-2 表示倒数第二个元素,依此类推。
- 如果 target 大于等于数组的长度,将不会进行任何拷贝操作。
- start (可选)
- 定义从哪个位置开始读取元素进行复制。
- 如果省略此参数,默认为 0。
- 如果该值为负数,则表示从末尾开始计算的索引。
- 如果 start 大于等于数组的长度,则 start 会被当作数组长度处理。
- 如果 start 大于 end,则不会进行任何拷贝操作。
- end (可选)
- 定义到哪个位置前停止读取元素进行复制(不包含该位置的元素)。
- 如果省略此参数,默认为数组的长度。
- 如果该值为负数,则表示从末尾开始计算的索引。
- 如果 end 大于数组的长度,则 end 会被当作数组长度处理。
示例:
假设我们有一个数组 arr = [1, 2, 3, 4, 5]
arr.copyWithin(0, 3)
:这将从索引 3 开始(即元素 4)到数组末尾的元素复制到索引 0 的位置开始,结果是[4, 5, 3, 4, 5]
。arr.copyWithin(1, -2, -1)
:这将从倒数第二个元素(即元素 4)复制到索引 1 的位置开始,但不包括倒数第一个元素(即元素 5),结果是[1, 4, 3, 4, 5]
。
注意:
- 这三个参数都应该是数值,如果不是,它们会被自动转换为数值。
- 如果 start 大于 end,则不会进行任何拷贝操作。
copyWithin
方法会直接修改原数组,并返回修改后的数组。
7.8 数组元素的排序
通过本书,我们能学到如何编写最常用的搜索和排序算法。其实,
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
方法在对数组做排序时,把元素默认成字符串进行相
互比较。
7.9 搜索
find 静态方法 (传入的参数是一个函数)
1. ECMAScript 2015——
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);
}
console.log(numbers.find(multipleOf13));
console.log(numbers.findIndex(multipleOf13));
find
和
findIndex
方法接收一个回调函数,搜索一个满足回调函数条件的值。上面的例子
里,我们要从数组里找一个
13
的倍数。
find
和
findIndex
的不同之处在于,
find
方法返回第一个满足条件的值,
findIndex
方法则返回这个值在数组里的索引。如果没有满足条件的值,
find
会返回
undefined
,而
findIndex
返回
-1
。
2. 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
。
64
第
3
章 数组
如果给
includes
方法传入一个起始索引,搜索会从索引指定的位置开始。
let numbers2 = [7,6,5,4,3,2,1];
console.log(numbers2.includes(4,5));
上面的例子输出为
false
,因为数组索引
5
之后的元素不包含
4 (第一个参数 是查找的元素 第二个值是 查找的起始索引)
7.10 列表元素字符传拼接
现在,我们学习最后两个方法:
toString
和
join
。
如果想把数组里所有元素输出为一个字符串,可以用
toString
方法。
console.log(numbers.toString());
1
、
2
、
3
、
4
、
5
、
6
、
7
、
8
、
9
、
10
、
11
、
12
、
13
、
14
、
15
和
10
这些值都会在控制台中输出。
如果想用一个不同的分隔符(比如
-
)把元素隔开,可以用
join
方法。
const numbersString = numbers.join('-');
console.log(numbersString);
输出将如下所示。
1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-10
如果要把数组内容发送到服务器,或进行编码(知道了分隔符,解码也很容易),这会很
有用。