Javascript 数组及其方法详解

更新日期: 2021-04-17阅读: 1.1k标签: 数组

简介

在 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"

总结

本文从数组的创建讲到数组的检测,最后讲了数组的各种方法,如数组的遍历方法、搜索方法等。这是一篇偏文档式的文章,需要经常复习。

原文链接:https://www.liuxing.io/blog/javascript-array-methods-tutorial/
本文首发于 刘星的个人网站liuxing.io


链接: https://www.fly63.com/article/detial/10255

探索JavaScript数组奥秘

avaScript数组同后端语言一样,具有它自己的数据结构,归根结底,这种数据结构,本质就是一种集合。在后端语言中(如java,.net等),数组是这样定义的:数组是用来存储相同数据类型的集合

js使用数组+循环+条件实现数字转换为汉字的简单方法。

单个数字转汉字的解决方法:利用数组存储0-9的汉字、 ary.length和str.length不用多说,这是指ary数组和str字符串的长度。这里我们需要注意的是str.charAt(j)和ary[i],分别指在str这个字符串中索引为j的元素,在ary中索引为i的元素。

[译]async-await 数组循环的几个坑

在 Javascript 循环中使用 async/ await 循环遍历数组似乎很简单,但是在将两者结合使用时需要注意一些非直观的行为。让我们看看三个不同的例子,看看你应该注意什么,以及哪个循环最适合特定用例。

数组、字符串去重

今天说的数组和字符串去重呢,主要用到es6新的数据结构 Set,它类似于数组,但是成员的值都是唯一的,没有重复的值,所以活用Set来进行数组和字符串的去重。

JavaScript 数组方法

数组方法:1、Array.join([param]) 方法:将数组中所有的元素都转换为字符串并连接起来,通过字符 param 连接,默认使用逗号,返回最后生成的字符串2、Array.reverse() 方法:将数组中的元素颠倒顺序(在原数组中重新排列它们),返回逆序数组

如何删除JavaScript 数组中的虚值

falsy(虚值)是在 Boolean 上下文中已认定可转换为‘假‘的值.JavaScript 在需要用到布尔类型值的上下文中使用强制类型转换(Type Conversion )将值转换为布尔值,比如:在条件语句或者循环语句中。

JavaScript中十种一步拷贝数组的方法

JavaScript中我们经常会遇到拷贝数组的场景,但是都有哪些方式能够来实现呢,我们不妨来梳理一下。扩展运算符(浅拷贝)自从ES6出现以来,这已经成为最流行的方法。

JS数组的几个经典api

本文主要来讲数组api的一些操作,如简单实现扁平化n维数组、数组去重、求数组最大值、数组求和、排序、对象和数组的转化等。扁平化嵌套数组/展平和阵列孔——flat()

关于Vue不能监听(watch)数组变化

vue无法监听数组变化的情况,但是数组在下面两种情况下无法监听:利用索引直接设置数组项时,例如arr[indexofitem]=newValue;修改数组的长度时,例如arr.length=newLength

JS计算两个数组的交集、差集、并集、补集(多种实现方式)

使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本,使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本。也不用引入其他第三方库。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!