共计 9200 个字符,预计需要花费 23 分钟才能阅读完成。
本文首发于 刘星的集体网站 liuxing.io
简介
在 JavaScript 中,除了 Object 之外,数组 (Array) 应该是最罕用的类型了。数组是一组有序的数据,应用方括号示意[1, 2, 3]
,可通过索引来拜访每个元素(索引从 0 开始)。JavaScript 中的数组的长度和元素类型都是非固定的。
数组的创立
首先咱们来看看数组的几种创立形式:字面量形式、Array 构造函数、扩大运算符 (...
)、ES6 新增的用于创立数组的静态方法from()
和of()
数组字面量
数组字面量 (array literal) 应该是 JavaScript 中最为罕用的创立形式了,在初始化数组的时候相当不便。数组字面量是在中括号中蕴含以逗号分隔的元素列表。
const users = ['LiuXing', 'liuixng.io'];
console.log(users.length); // 2
下面这个例子中创立了一个蕴含两个字符串的数组。
构造函数
还能够应用 Array 构造函数来创立数组。能够给 Array()
构造函数传入一个数字,创立一个长度为传入值得数组;也能够给 Array 构造函数传入要保留的元素,创立蕴含传入值的数组。
// 传入要保留的元素
const users = Array('LiuXing', 'liuxing.io'); ['LiuXing', 'liuxing.io']
console.log(users.length); // 2
const arr1 = new Array(); []
// 传入数字 间接创立一个长度为 3 的数组
const arr2 = Array(3); [,,]
console.log(users.length); // 3
在应用 Array 构造函数时,加不加 new 操作符,后果都一样。
扩大运算符
能够应用扩大运算符 ...
在一个数组字面量里蕴含另一个数组的元素。扩大运算符能够很不便的创立一个浅拷贝的数组。扩大运算符还能够用于任何可迭代的对象。
const original = [1, 2, 3];
const copy = [...original]
copy[0] = 0 // 批改 copy 不会法扭转原数组
original[0] // 1
Array 静态方法
ES6 Array 新增了两个用于创立数组的静态方法: from()
和 of()
。from()
用于将类数组转换为数组,而 of()
用于将一组参数转换为数组。
Array.form()
从类数组对象或者可迭代对象中创立一个新的数组实例。
Array.form()
能够接管 3 个参数:
- 第一个必须参数为想要转换成数组的伪数组对象或可迭代对象:如 Map 和 Set、Arguments 对象
- 第二个是可选的映射函数参数,能够间接加强新数组的值,相似数组的 map 办法
- 第三个可选参数,用于指定映射函数中 this 的值,但这个重写的 this 值在箭头函数中不实用
console.log(Array.from('foo'));
// expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));
// expected output: Array [2, 4, 6]
const a1 = [1, 2, 3, 4];
const a2 = Array.from(a1, function(x) {return x**this.exponent}, {exponent: 2});
console.log(a2); // [1, 4, 9, 16]
Array.of()
依据一组参数来创立新的数组实例,反对任意的参数数量和类型。将按程序成为返回数组中的元素。
Array.of(7); // [7]
Array.of(1, 2, 3); // [1, 2, 3]
数组的检测
实质上,数组属于一种非凡的对象。typeof
运算符会返回数组的类型是 object
。有一个经典问题是判断一个对象是不是数组,通常能够通过 instanceof
、constructor
曾经 Object.prototype.toString
来判断,然而前两这个可能不精确,后者较麻烦。为解决这个小问题,JavaScript 提供了 Array.isArray()
办法,它返回一个布尔值,示意参数是否为数组。它能够补救 typeof
运算符的有余。
Array.isArray([1, 2, 3]);
// true
Array.isArray({foo: 123});
// false
Array.isArray("foobar");
// false
Array.isArray(undefined);
// false
然而在不反对该办法的环境中咱们能够提供如下 Polyfill,也就是应用 Object.prototype.toString
来判断
if (!Array.isArray) {Array.isArray = function(arg) {return Object.prototype.toString.call(arg) === '[object Array]';
};
}
数组办法
数组的办法有很多,本文会将这些办法分为操作方法、排序办法、栈与队列办法、迭代办法、搜寻办法及数组的转换方法一一解说。在数组的操作方法中只有 concat()
与slice()
不会扭转原数组,其余办法均会扭转原数组。所有的排序办法都会扭转原数组。所有栈与队列办法也会扭转原数组。
数组的操作方法
对于数组中的元素,咱们有很多操作方法,如:concat()
用于连贯两个数组。slice()
用于切片,splice()
在数组两头删除或插入元素 …
contact()
concat
办法用于多个数组的合并。它将新数组的元素,增加到原数组元素的后部,而后返回一个新的后果数组,原数组不变。
['liu', 'xing'].concat(['love', 'dev'])
// ["liu", "xing", "love", 'dev']
如果传入一个或多个数组,则 concat()
会把这些数组的每一项都增加到后果数组。如果参数不是数组,则间接把它们增加到后果数组开端
[1, 2, 3].concat(4, 5, 6)
// [1, 2, 3, 4, 5, 6]
同时,concat
办法还能够很不便的创立一个以后数组的一个浅拷贝。
slice()
slice()
办法用于切片,即截取数组的中的局部元素,而后返回一个新数组,原数组不变。
slice()
办法能够接管一个或两个参数:返回元素的开始索引和完结索引。slice()
返回的后果包含开始索引,不包含完结索引。
arr.slice(start, end);
如果有两个参数,则 slice()
返回从开始索引到完结索引对应的所有元素,其中不蕴含完结索引对应的元素。
arr.slice(start, end);
如果只有一个参数,则 slice()会返回该索引到数组开端的所有元素。
arr.slice(start, end);
如果不给 slice()
传递任何参数,它就会从头到尾截取所有元素,等于返回了一个原数组的拷贝。
arr.slice(start, end);
如果 slice()
办法的参数是正数,则示意倒数计算的地位
arr.slice(start, end);
splice()
或者最弱小的数组办法就属 splice()
了,他是批改数组的“万能办法”,它能够从指定的索引开始删除若干元素,而后再从该地位增加若干元素。返回值是被删除的元素,该办法会扭转原数组。
arr.splice(start, count, addElement1, addElement2, ...);
splice
的第一个参数是指定批改的开始地位(从 0 计数),第二个参数是被删除的元素个数。如果前面还有更多的参数,则示意这些就是要被插入数组的新元素。
次要有以下三种应用形式:
- 删除 。须要给
splice()
传 2 个参数: 要删除的第一个元素的地位和要删除的元素数量。能够从
数组中删除任意多个元素,比方splice(0, 2)
会删除前两个元素。 - 插入 。须要给
splice()
传 3 个参数: 开始插入地位索引、0(删除 0 个元素)和要插入的元素,可
以在数组中指定的地位插入元素。第三个参数之后还能够传第四个、第五个参数,乃至任意多
个要插入的元素。 - 替换。
splice()
在删除元素的同时能够在指定地位插入新元素,这样咱们能够很不便实现元素的替换。如splice(2, 1, "liuxing")
会在地位 2 删除一个元素,而后从该地位开始向数组中插入 ”liuxing”。
copyWithin()
copyWithin()
办法能够浅复制数组的一部分到同一数组中的另一个地位,并返回它,不会扭转原数组的长度。
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
fill()
fill()
办法能够用一个固定值填充一个数组中从起始索引到终止索引内的全副元素。不包含终止索引。
['a', 'b', 'c'].fill(7)
// [7, 7, 7]
new Array(3).fill(7)
// [7, 7, 7]
排序办法
sort()
sort
办法可对数组成员进行排序,排序后原数组将被扭转。默认排序程序是在将元素转换 (调用 String()
转型函数) 为字符串,而后比拟它们的 UTF-16 代码单元值序列时构建的。如:
let values = [0, 3, 1, 2, 10];
values.sort();
console.log(values); // [0, 1, 10, 2, 3]
从上例能够看到,默认的排序办法,对数字的排序是有问题的,为此,sort()办法能够接管一个比拟函数作为第二个参数,用于判断哪个值应该排在后面。比拟函数能够接管两个参数,示意进行比拟的两个数组成员。
- 如果第一个参数应该排在第二个参数后面,就返回负值;
- 如果两个参数相等,就返回 0;
- 如果第一个参数应该排在第二个参数前面,就返回正值。
格局如下
function compare(a, b) {if (a < b) { // 按某种排序规范进行比拟, a 小于 b
return -1;
}
if (a > b) {return 1;}
// a must be equal to b
return 0;
}
对于后面的实例数组的排序能够写成这样
let values = [0, 3, 1, 2, 10];
values.sort((a, b) => a - b);
console.log(values); // [0, 1, 2, 3, 10]
reverse()
顾名思义,reverse()
办法就是将数组元素反向排列。该办法将扭转原数组。
let values = [1, 2, 3, 4, 5];
values.reverse();
console.log(values); // 5,4,3,2,1
栈与队列
JavaScript 的数组以及原生办法能够很好的模仿另外两种罕用数据结构:栈与队列
栈是一种后进先出 (LIFO,Last-In-First-Out) 的构造 ,也就是最近增加的项先被删除。数据项的插入(称为推入,push) 和删除 (称为弹出,pop) 只在栈的一个中央产生,即栈顶。JavaScript 数组提供了 push()和 pop()办法,以实现相似栈的行为。
push()
push()
办法用于向数组开端增加元素(任意数量的元素),返回数组的最新长度,该办法会扭转原数组。
let arr = [];
arr.push(1) // 1
arr.push('liuxing') // 2
arr // [1, 'liuxing', true, {}]
pop()
pop()
办法用于删除数组的最初一个元素,并返回该元素。该办法会扭转原数组。
let arr = ['a', 'b', 'c'];
arr.pop() // 'c'
arr // ['a', 'b']
队列以先进先出 (FIFO,First-In-First-Out) 模式限度拜访 。队列在列表开端增加数据,但从列表结尾获取数据。因为有了在数据开端增加数据的 push() 办法,所以要模仿队列就差一个从数组结尾获得数据的办法了。这个数组办法叫 shift(),它会删除数组的第一项并返回它,而后数组长度减 1。应用 shift()和 push(),能够把数组当成队列来应用
shift()
shift()
办法用于删除数组的第一个元素,并返回该元素。该办法会扭转原数组。
let arr = ['a', 'b', 'c'];
arr.shift() // 'a'
arr // ['b', 'c']
unshift()
unshift()
办法用于在数组的第一个地位增加元素,并返回增加新元素后的数组长度。该办法会扭转原数组。
let arr = ['a', 'b', 'c'];
arr.unshift('liuixng'); // 4
arr // ['liuxing', 'a', 'b', 'c']
数组的迭代办法
ES6 数组提供三个新的用于检索数组内容的办法: entries()
,keys()
和 values()
。它们都返回一个迭代器对象。keys()
返回数组索引的迭代器,values()
返回数组元素的迭代器,而 entries()
返回键 / 值对的迭代器。能够用 for...of
循环进行遍历。
keys()
返回一个蕴含所有数组元素的索引的迭代器对象。
for (let index of ['a', 'b'].keys()) {console.log(index);
}
// 0
// 1
values()
返回一个蕴含所有数组元素值得迭代器对象。
for (let elem of ['a', 'b'].values()) {console.log(elem);
}
// 'a'
// 'b'
entries()
返回一个蕴含所有数组元素的键值对迭代器对象。
for (let [index, elem] of ['a', 'b'].entries()) {console.log(index, elem);
}
// 0 "a"
// 1 "b"
forEach()
forEach
办法迭代数组的每一个元素,并对每个元素都调用一次咱们指定的回调函数。它不返回值,只用来操作数据。forEach()
办法相当于应用 for
循环遍历数组。
let numbers = [1, 2, 3, 4, 5];
numbers.forEach((item, index, array) => {
// 执行某些操作
console.log(item)
});
map()
map()
会将数组的每个元素都按程序传入回调函数,而后返回由每次函数调用的后果组成的新数组。留神:回调 函数只会在有值的索引上被调用。
let numbers = [1, 2, 3, 4, 5];
let mapResult = numbers.map((item, index, array) => item * 2);
console.log(mapResult); // [2,4,6,8,10]
flat()
flat()
用于打平数组。即从多维数组转化为一位数组。该办法接管一个参数(默认为 1),用于指定的深度遍历,而后将所有元素与遍历到的子数组中的元素合并为一个新数组返回。
[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
如果想要打平无论多少层的数组,能够传入 Infinity
作为参数
[1, [2, [3]]].flat(Infinity)
// [1, 2, 3]
flatMap()
flatMap()
与 map()
办法类似。只不过返回的数组会主动被打平。调用 flatMap()
等同于调用 map()
,然而flatMap()
效率更高。
[2, 3, 4].flatMap((x) => [x, x * 2])
// [2, 4, 3, 6, 4, 8]
filter()
filter()
办法用于过滤数组元素。它会对数组每一项都运行传入的函数,返回后果为 true
的元素将组成一个新数组返回。该办法不会扭转原数组。
let numbers = [1, 2, 3, 4, 5];
let filterResult = numbers.filter((item, index, array) => item > 2);
console.log(filterResult); // [3, 4, 5]
some()
与 every()
十分相似,都用于判断数组中的元素是否合乎某种条件(断言函数)。
- 对
every()
来说,传入的函数必须对每一项都返回true
,它才会返回true
; - 对
some()
来说,只有有一项让传入的函数返回true
,它就会返回true
。
some()
如果数组中至多有一个元素满足测试函数,则返回 true
,否则返回 false
。
let numbers = [1, 2, 3, 4, 5];
let everyResult = numbers.every((item, index, array) => item > 2);
console.log(everyResult); // false
every()
如果数组中的每个元素都满足测试函数,则返回 true
,否则返回 false。
let numbers = [1, 2, 3, 4, 5];
let someResult = numbers.some((item, index, array) => item > 2);
console.log(everyResult); // true
reduce()
和 reduceRight()
这两个办法都会迭代数组的所有项,并在此基础上累计构建一个最终返回值。reduce()
办法从数组第一项开始遍历到最初一项。而 reduceRight()
从最初一项开始遍历至第一项。
reduce()
从左到右为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最初一次回调函数的返回值。
let values = [1, 2, 3, 4, 5];
let sum = values.reduce(function(prev, cur, index, array){console.log(prev, cur)
return prev + cur;
});
// 1 2
// 3 3
// 6 4
// 10 5
console.log(sum) // 15
reduceRught()
从右到左为每个数组元素执行一次回调函数,并把上次回调函数的返回值放在一个暂存器中传给下次回调函数,并返回最初一次回调函数的返回值。
let values = [1, 2, 3, 4, 5];
let sum = values.reduceRight(function(prev, cur, index, array){console.log(prev, cur)
return prev + cur;
});
// 5 4
// 9 3
// 12 2
// 14 1
console.log(sum) // 15
在数组中搜寻
JavaScript 提供两类搜寻数组的办法: 按严格相等搜寻和按断言函数搜寻。
indexOf()
、lastIndexOf()
和includes()
是按严格相等的搜寻办法,它们都这些办法都接管两个参数: 要查找的元素和一个可选的起始搜寻地位。find()
和findIndex()
办法应用了断言函数。它们接管 3 个参数: 元素、索引和数组自身
indexOf()
indexOf
办法返回给定元素在数组中第一次呈现的地位,如果没有呈现则返回-1
,它还能够承受第二个参数,示意搜寻的开始地位
let arr = Array.from('liuxing');
arr.indexOf('i') // 1
arr.indexOf('i', 2) // 4
lastIndexOf()
lastIndexOf
办法返回给定元素在数组中最初一次呈现的地位(从后往前找),如果没有呈现则返回-1
。
let arr = Array.from('liuxing');
arr.lastIndexOf('i') // 4
arr.lastIndexOf('i',3) // 1
includes()
includes()
办法用来判断一个数组是否蕴含一个指定的值,返回布尔值。如果蕴含则返回 true,否则返回 false。
let arr = Array.from('liuxing');
arr.includes('i') // true
arr.includes('io') // false
find()
find()
办法返回数组中满足断言函数的第一个元素的值。否则返回 undefined。
const people = [
{
name: "LiuXing",
age: 99
},
{
name: "XingLiu",
age: 9
}
];
people.find(element => element.name === 'LiuXing') // {name: "LiuXing", age: 99}
findIndex()
findIndex()
办法返回数组中满足断言函数的第一个元素的地位。否则返回 -1。
const people = [
{
name: "LiuXing",
age: 99
},
{
name: "XingLiu",
age: 9
}
];
people.findIndex(element => element.name === 'LiuXing') // 0
数组的转换方法
所有对象都有 toLocaleString()
、toString()
和 valueOf()
办法。其中,数组的 valueOf()
返回的还是数组自身。而 toString()
返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。也就是说,对数组的每个值都会调用其 toString()
办法,以失去最终的字符串。
valueOf()
valueOf
办法是一个所有对象都领有的办法,示意对该对象求值。数组的 valueOf
办法返回数组自身。
let arr = [1, 2, 3];
arr.valueOf() // [1, 2, 3]
toString() 与 toLocaleString()
toString
办法也是对象的通用办法,toLocaleString()
与 toString()
的区别在于 toLocaleString()
与执行环境的地区对应,如日期对象。数组的 toString
及toLocaleString()
办法返回数组的字符串模式。
let arr = [10000, 20000, 3000];
arr.toString() // 10000,20000,3000
arr.toLocaleString() // "10,000,20,000,3,000"
join()
join()
办法接管一个作为字符串分隔符的参数,将所有数组成员连贯为一个字符串返回。如果不提供参数,默认用逗号分隔。
let a = [1, 2, 3, 4, 5];
a.join('') //'1 2 3 4 5'a.join() //"1,2,3,4,5"
总结
本文从数组的创立讲到数组的检测,最初讲了数组的各种办法,如数组的遍历办法、搜寻办法等。这是一篇偏文档式的文章,须要常常温习。
参考链接
- JavaScript Array – MDN
- ECMAScript 6 入门
- 刘星的集体网站 – liuxing.io
本文完
欢送能够关注我的公众号,一起游玩。有技术干货也有扯淡乱谈
左手代码右手砖,抛砖引玉
文档信息
版权申明:署名 - 非商业性应用 - 禁止演绎 4.0 国内 (CC BY-NC-ND 4.0)
原文链接:https://www.liuxing.io/blog/j…
发表日期:2021 年 04 月 17 日