和其他语言里的数组一样,ECMAScript里的数组也是一组有序数据,但不同的是ECMAScript里数组可以存储不同的值,这意味着可以创建一个数组,它的第一个元素是字符串,第二个元素是数值,第三个是对象。ECMAScript 数组也是动态大小的,会随着数据添加而自动增长。

4.1 创建数组

JavaScript里有两种方式来创建数组

1. 使用 Array构造函数来创建数组

  1. var 数组名 = new Array();
  2. var arr = Array(8); //创建一个数组长度为8的数组
  3. var arr = Array(); //创建空数组
  4. let colors = new Array(3); // 创建一个包含 3 个元素的数组
  5. let names = new Array("Greg"); // 创建一个只包含一个元素,即字符串"Greg"的数组
  • Array函数的首字母要大写
  • 创建时可以省略new操作符
  • 创建数组时可以给构造函数传一个值。这时候就有点问题了,因为如果这个值是数值,则会创建一个长度为指定数值的数组;而如果这个值是其他类型的,则会创建一个只包含该特定值的数组

    2. 数组字面量表示法创建数组

    1. var 数组名 = []; //创建一个空数组
    2. var color = ['red'.'green','blue'];
    3. var num = [1,2,3]
  • 并未使用Array构造数组函数

  • 声明时赋值给数组,为空时创建空数组

4.2 获取数组元素

通过数组的索引来指向数组中确定的元素,数组的索引从0开始,所以最大索引永远比长度(length)少1

  1. var num = [1,2,3,4,5,6]
  2. console.lognum[2]) // 3

4.3 数组长度 (length)

数组中元素的数量保存在 length 属性中,这个属性始终返回 0 或大于 0 的值

  1. let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
  2. let names = []; // 创建一个空数组
  3. alert(colors.length); // 3
  4. alert(names.length); // 0

1. 通过length来缩短数组

  1. let colors = ["red", "blue", "green"];
  2. color.length=2;
  3. alert(colors[2]); // undefined
  • 数组 colors 一开始有 3 个值。将 length 设置为 2,就删除了最后一个(位置 2 的)值,因此 colors[2]就没有值了。

    2. 通过length来加长数组

    1. color.length=5;
    2. console.log(color) //"red", "blue", "green" undefined undefined
  • 如果将 length 设置为大于数组元素数的值,则新添加的元素都将以undefined 填充

5. 数组的检测

判断变量是否为数组时,

  • instanceof操作符在只有一个网页(因而只有一个全局作用域)的情况下,使用 instanceof 操作符就足矣
  1. let arr = [1,2,3],
  2. obj = {};
  3. console,log(arr instanceof Array ); //true
  4. console.log(obj instanceof Array); //false
  • 使用 instanceof 的问题是假定只有一个全局执行上下文。如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,因此就会有两个不同版本的 Array 构造函数
  • 如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于在第二个框架内本地创建的数组。

  • Array.isArray()这个方法的目的就是确定一个值是否为数组,而不用管它是在哪个全局执行上下文中创建的。
    1. let arr = [5,4,3,2,1],
    2. obj = {};
    3. console.log(Array.isArray(arr)); // true
    4. console.log(Array.isArray(obj)); // false

4.4 栈方法(push和pop)

数组对象可以像栈一样,也就是最近添加的项先被删除。数据项的插入(称为推入,push)和删除(称为弹出,pop)只在栈的一个地方发生,即栈顶。ECMAScript 数组提供了 push()和 pop()方法,以实现类似栈的行为。

  • push()方法接收任意数量的参数,并将它们添加到数组末尾,返回数组的最新长度。
  • pop()方法则用于删除数组的最后一项,同时减少数组的 length值,返回被删除的项。

    1. let colors = new Array(); // 创建一个数组
    2. let count = colors.push("red", "green"); // 推入两项
    3. alert(count); // 2
    4. count = colors.push("black"); // 再推入一项
    5. alert(count); // 3
    6. let item = colors.pop(); // 取得最后一项
    7. alert(item); // black
    8. alert(colors.length); // 2
  • 这里创建了一个当作栈来使用的数组(注意不需要任何额外的代码,push()和 pop()都是数组的默认方法)。

  • 首先,使用 push()方法把两个字符串推入数组末尾,将结果保存在变量 count 中(结果为 2)。
  • 然后,再推入另一个值,再把结果保存在 count 中。因为现在数组中有 3 个元素,所以 push()返 回 3。
  • 在调用 pop()时,会返回数组的最后一项,即字符串”black”。此时数组还有两个元素。

push()、pop()方法可以和数组的其他方法一起使用:

  1. let colors = ["red", "blue"];
  2. colors.push("brown"); // 再添加一项
  3. colors[3] = "black"; // 添加一项
  4. alert(colors.length); // 4
  5. let item = colors.pop(); // 取得最后一项
  6. alert(item); // black

4.5 队列方法 (unshift和shift)

队列以先进先出(FIFO,First-In-First-Out)形式限制访问。队列在列表末尾添加数据,但从列表开头获取数据。因为有了在数据末尾添加数据的 push()方法,所以要模拟队列就差一个从数组开头取得数据的方法了。

  • 这个数组方法叫 shift(),它会删除数组的第一项并返回它,然后数组长度减 1。
  • 使用 shift()和 push(),可以把数组当成队列来使用:
    1. let colors = new Array(); // 创建一个数组
    2. let count = colors.push("red", "green"); // 推入两项
    3. alert(count); // 2
    4. count = colors.push("black"); // 再推入一项
    5. alert(count); // 3
    6. let item = colors.shift(); // 取得第一项
    7. alert(item); // red
    8. alert(colors.length); // 2
    shift()方法用来取出数组头部的元素,取出时返还取出的元素值

ECMAScript 也为数组提供了``unshift()方法。unshift()执行跟 shift()相反的操作:

  • 在数组开头添加任意多个值,然后返回新的数组长度。
  • 通过使用 unshift()和 pop(),可以在相反方向上模拟队列,即在数组开头添加新数据,在数组末尾取得数据
  • 如下例所示:
    1. let colors = new Array(); // 创建一个数组
    2. let count = colors.unshift("red", "green"); // 从数组开头推入两项
    3. alert(count); // 2
    4. count = colors.unshift("black"); // 再推入一项
    5. alert(count); // 3
    6. let item = colors.pop(); // 取得最后一项
    7. alert(item); // green
    8. alert(colors.length); // 2

4.6 排序方法

数组有两个方法可以用来对元素重新排序:reverse()sort()

  • reverse()方法将数组反向排序
  • sort()会按照升序重新排列数组元素,即最小的值在前面,最大的值在后面。为此,sort()会在每一项上调用 String()转型函数,然后比较字符串来决定顺序。即使数组的元素都是数值,也会先把数组转换为字符串再比较、排序。
  1. let values = [0, 1, 5, 10, 15];
  2. values.reverse();
  3. alert(values); // 15、10、5、1、0

reverse()方法来反向排列数组


  1. let values = [0, 1, 5, 10, 15];
  2. values.sort();
  3. alert(values); // 0,1,10,15,5

sort()方法如果不加比较函数的话,默认为字典排序,即为按照首字母大小以及首位数字大小排序


比较方法:比较函数接收两个参数,如果第一个参数应该排在第二个参数前面,就返回负值;如果两个参数相
等,就返回 0;如果第一个参数应该排在第二个参数后面,就返回正值
例子:

  1. function compare(a,b){
  2. return a-b; //如果a>b ,a排在b后面,为从小到大排
  3. }
  4. let b = [5,6,8,4,1,2]
  5. console.log(b.sort(compare)) //1,2,4,5,6,8
  • 当a-b>0时,调换a、b位置,使之从小往大排序

    1. function compare(a,b){
    2. return b-a; //如果a<b ,a排在b后面,为从小到大排
    3. }
    4. let a=[5,3,2,1,6,4]
    5. console.log(a.sort(compare)) //[6,5,4,3,2,1]
  • 如果传入“function(a,b){ return a-b;}”,则为升序

  • 如果传入“function(a,b){ return b-a;}”,则为降序

4.7 操作方法

1. concat()方法

concat()方法可以在现有数组全部元素基础上创建一个新数组。它首先会创建一个当前数组的副本,然后再把它的参数添加到副本末尾,最后返回这个新构建的数组。如果传入一个或多个数组,则concat()会把这些数组的每一项都添加到结果数组。如果参数不是数组,则直接把它们添加到结果数组末尾。

  1. let colors = ["red", "green", "blue"];
  2. let colors2 = colors.concat("yellow", ["black", "brown"]);
  3. console.log(colors); // ["red", "green","blue"]
  4. console.log(colors2); // ["red", "green", "blue", "yellow", "black", "brown"]

2. slice()方法

方法 slice()用于创建一个包含原有数组中一个或多个元素的新数组。slice()方法可以接收一个或两个参数:

  • 返回元素的开始索引和结束索引。
  • 如果只有一个参数,则 slice()会返回该索引到数组末尾的所有元素。
  • 如果有两个参数,则 slice()返回从开始索引到结束索引对应的所有元素,其中不包含结束索引对应的元素。记住,这个操作不影响原始数组。
    1. let colors = ["red", "green", "blue", "yellow", "purple"];
    2. let colors2 = colors.slice(1);
    3. let colors3 = colors.slice(1, 4);
    4. alert(colors2); // green,blue,yellow,purple
    5. alert(colors3); // green,blue,yellow
    返回时不反回第二个参数代表的位置的元素,此时从1开始到位置4结束,仅仅返回1、2、3这三个位置的元素。
    注意 :如果 slice()的参数有负值,那么就以数值长度加上这个负值的结果确定位置。比如,在包含 5 个元素的数组上调用 slice(-2,-1),就相当于调用 slice(3,4)。如果结束位置小于开始位置,则返回空数组。

2. splice()方法

或许最强大的数组方法就属 splice()了,使用它的方式可以有很多种。splice()的主要目的是在数组中间插入元素,但有 3 种不同的方式使用这个方法。

  • 删除。需要给 splice()传 2 个参数:要删除的第一个元素的位置和要删除的元素数量。可以从数组中删除任意多个元素,比如 splice(0, 2)会删除前两个元素。
  • 插入。需要给 splice()传 3 个参数:开始位置、0(要删除的元素数量)和要插入的元素,可以在数组中指定的位置插入元素。第三个参数之后还可以传第四个、第五个参数,乃至任意多个要插入的元素。比如,splice(2, 0, “red”, “green”)会从数组位置 2 开始插入字符串”red”和”green”。
  • 替换。splice()在删除元素的同时可以在指定位置插入新元素,同样要传入 3 个参数:开始位置、要删除元素的数量和要插入的任意多个元素。要插入的元素数量不一定跟删除的元素数量一致。比如,splice(2, 1, “red”, “green”)会在位置 2 删除一个元素,然后从该位置开始向数组中插入”red”和”green”。

splice()方法始终返回这样一个数组,它包含从数组中被删除的元素(如果没有删除元素,则返回空数组)。

  1. let colors = ["red", "green", "blue"];
  2. let removed = colors.splice(0,1); // 删除第一项
  3. alert(colors); // green,blue
  4. alert(removed); // red,只有一个元素的数组
  5. removed = colors.splice(1, 0, "yellow", "orange"); // 在位置 1 插入两个元素
  6. alert(colors); // green,yellow,orange,blue
  7. alert(removed); // 空数组
  8. removed = colors.splice(1, 1, "red", "purple"); // 插入两个值,删除一个元素
  9. alert(colors); // green,red,purple,orange,blue
  10. alert(removed); // yellow,只有一个元素的数组

4.8 转换方法

前面提到过,所有对象都有 toLocaleString()、toString()和 valueOf()方法。其中,valueOf()返回的还是数组本身。而 toString()返回由数组中每个值的等效字符串拼接而成的一个逗号分隔的字符串。也就是说,对数组的每个值都会调用其 toString()方法,以得到最终的字符串。

  1. let colors = ["red", "blue", "green"]; // 创建一个包含 3 个字符串的数组
  2. alert(colors.toString()); // red,blue,green
  3. alert(colors.valueOf()); // red,blue,green
  4. alert(colors); // red,blue,green

1.join()方法

join()方法接收一个参数,即字符串分隔符,返回包含所有项的字符串。

  1. let colors = ["red", "green", "blue"];
  2. alert(colors.join(",")); // red,green,blue
  3. alert(colors.join("||")); // red||green||blue
  • 这里在 colors 数组上调用了 join()方法,得到了与调用 toString()方法相同的结果。
  • 传入逗号,结果就是逗号分隔的字符串。最后一行给 join() 传入了双竖线,得到了字串”red||green||blue”。
  • 如果不给 join()传入任何参数,或者传入 undefined,则仍然使用逗号作为分隔符。

4.9 迭代方法

ECMAScript 为数组定义了 5 个迭代方法。每个方法接收两个参数:以每一项为参数运行的函数,以及可选的作为函数运行上下文的作用域对象(影响函数中this 的值)。传给每个方法的函数接收 3 个参数:数组元素、元素索引和数组本身。因具体方法而异,这个函数的执行结果可能会也可能不会影响方法的返回值。

  • every():对数组每一项都运行传入的函数,如果每一项函数都返回true,则这个方法返回true
  • filter():对数组每一项都运行传入的函数,函数返回 true 的项会组成数组之后返回。
  • forEach():对数组每一项都运行传入的函数,没有返回值。
  • map():对数组的每一项都运行传入的函数,返回由每次函数调用的结果构成的数组
  • some():对数组每一项都运行传入的函数,如果有一项函数返回 true,则这个方法返回 true。 这些方法都不改变调用它们的数组。

every()和some()

在这些方法中,every()和 some()是最相似的,都是从数组中搜索符合某个条件的元素。对 every() 来说,传入的函数必须对每一项都返回 true,它才会返回 true;否则,它就返回 false。而对 some() 来说,只要有一项让传入的函数返回 true,它就会返回 true。

  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. let everyResult = numbers.every((item, index, array) => item > 2);
  3. alert(everyResult); // false
  4. let someResult = numbers.some((item, index, array) => item > 2);
  5. alert(someResult); // true

以上代码调用了 every()和 some(),传入的函数都是在给定项大于 2 时返回 true。every()返回 false 是因为并不是每一项都能达到要求。而 some()返回 true 是因为至少有一项满足条件。

filter()方法

这个方法基于给定的函数来决定某一项是否应该包含在它返回的数组中。比如,要返回一个所有数值都大于 2 的数组,可以使用如下代码:

  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. let filterResult = numbers.filter((item, index, array) => item > 2);
  3. alert(filterResult); // 3,4,5,4,3

调用 filter()返回的数组包含 3、4、5、4、3,因为只有对这些项传入的函数才返回 true。 这个方法非常适合从数组中筛选满足给定条件的元素。

map()方法

map()方法也会返回一个数组。这个数组的每一项都是对原始数组中同样位置的元素运行传入函数而返回的结果。例如,可以将一个数组中的每一项都乘以 2,并返回包含所有结果的数组,如下所示:

  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. let mapResult = numbers.map((item, index, array) => item * 2);
  3. alert(mapResult); // 2,4,6,8,10,8,6,4,2

以上代码返回了一个数组,包含原始数组中每个值乘以 2 的结果。这个方法非常适合创建一个与原 始数组元素一一对应的新数组。

forEach()方法

最后,再来看一看 forEach()方法。这个方法只会对每一项运行传入的函数,没有返回值。本质 上,forEach()方法相当于使用 for 循环遍历数组。

  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. numbers.forEach((item, index, array) => {
  3. // 执行某些操作
  4. });

4.10 搜索和位置方法

ECMAScript 提供了 3 个严格相等的搜索方法:indexOf()、lastIndexOf()和 includes()。
其中,前两个方法在所有版本中都可用,而第三个方法是 ECMAScript 7 新增的。这些方法都接收两个参数:要查找的元素和一个可选的起始搜索位置

  • indexOf()includes()方法从数组前头(第一项)开始向后搜索
  • lastIndexOf()从数组末尾(最后一项)开始向前搜索。
  • indexOf()和 lastIndexOf()都返回要查找的元素在数组中的位置,如果没找到则返回-1
  • includes()返回布尔值,表示是否至少找到一个与指定元素匹配的项。在比较第一个参数跟数组每一 项时,会使用全等(===)比较,也就是说两项必须严格相等。
  1. let numbers = [1, 2, 3, 4, 5, 4, 3, 2, 1];
  2. alert(numbers.indexOf(4)); // 3 目标元素在数组内的索引号(从前往后)
  3. alert(numbers.lastIndexOf(4)); // 5 目标元素在数组内的索引号(从后往前)
  4. alert(numbers.includes(4)); // true 是否找出此元素
  5. alert(numbers.indexOf(4, 4)); // 5
  6. alert(numbers.lastIndexOf(4, 4)); // 3
  7. alert(numbers.includes(4, 7)); // false
  8. let person = { name: "yuanmou" };
  9. let people = [{ name: "yuanmou" }];
  10. let morePeople = [person];
  11. alert(people.indexOf(person)); // -1
  12. alert(morePeople.indexOf(person)); // 0
  13. alert(people.includes(person)); // false
  14. alert(morePeople.includes(person)); // true

断言函数

ECMAScript 也允许按照定义的断言函数搜索数组,每个索引都会调用这个函数。断言函数的返回值决定了相应索引的元素是否被认为匹配。 断言函数接收 3 个参数:元素、索引和数组本身。其中元素是数组中当前搜索的元素,索引是当前元素的索引,而数组就是正在搜索的数组。断言函数返回真值,表示是否匹配。

find()和findIndex()方法使用断言函数,这两个方法都是从最小索引开始

  • find()返回第一个匹配的元素
  • findIndex()返回第一个匹配的元素的索引

这两个方法都接受第二个可选参数,用于指定断言函数内部的this值

  1. const people = [
  2. {
  3. name: "Matt",
  4. age: 27
  5. },
  6. {
  7. name: "Nicholas",
  8. age: 29
  9. }
  10. ];
  11. alert(people.find((element, index, array) => element.age < 28));
  12. // {name: "Matt", age: 27} find方法返回元素
  13. alert(people.findIndex((element, index, array) => element.age < 28));
  14. // 0 findIndex方法返回元素索引

找到匹配项后,这两个方法都不再继续搜索。

  1. const evens = [2, 4, 6];
  2. // 找到匹配后,永远不会检查数组的最后一个元素
  3. evens.find((element, index, array) => {
  4. console.log(element);
  5. console.log(index);
  6. console.log(array);
  7. return element === 4;
  8. });
  9. // 2
  10. // 0
  11. // [2, 4, 6]
  12. // 4
  13. // 1
  14. // [2, 4, 6]

4.11 归并方法

  • reduce()方法 :从数组的第一项遍历到最后一项,并通过内部设置的函数方法来返回一个值
  • reduceRight()方法:从数组的最后一项遍历到第一项,也是通过内部函数返回一个值

reduce()方法

  1. reduce((上一次循环的结果,当前的值,当前的索引,当前循环的数组),输出变量的初始值)

求和

  1. let arr=[1,2,3,4,5,6]
  2. arr.reduce((sum,item)=>sum+item,0) //21

究竟是使用 reduce()还是 reduceRight(),只取决于遍历数组元素的方向。除此之外,这两个 方法没什么区别。

ES6 新增方法

Array 构造函数还有两个 ES6 新增的用于创建数组的静态方法:from()和 of()。

  • from() :将类数组结构转换为数组实例
  • of() :将一组参数转换为数组实例

from()方法

Array.from()的第一个参数是一个类数组对象,即任何可迭代的结构(就是对象或者复杂类型数据),或者一个有length属性和可索引元素的结构(例如:伪数组)

  1. //使用form()方法拆分字符串
  2. console.log(Array,from("yuan")); //["y","u","a","n"]
  3. //可以再次将集合(set)和映射(map)转换成一个新数组
  4. const m = new Map().set(1,2)
  5. .set(3,4);
  6. const s = new Set().add(1)
  7. .add(2)
  8. .add(3)
  9. .add(4);
  10. console.log(Array.from(m));//[[1,2],[3,4]]
  11. console.log(Array.from(s));//[1,2,3,4]

通过Array.from()对数组进行浅复制

  1. const a1 = [1,2,3,4];
  2. const a2 = Array.from(a1);
  3. console.log(a1); // [1, 2, 3, 4]
  4. alert(a1 === a2); // false

将arguments转换成数组
(arguments是函数内部不确定形参时替代用的伪数组)

  1. function fun() {
  2. return Array.from(arguments);
  3. }
  4. console.log(fun(1, 2, 3, 4)); // [1, 2, 3, 4]

将自定义对象转换为数组

  1. const unknow={
  2. 0:1,
  3. 1:2,
  4. 2:3,
  5. 3:4.
  6. length:4
  7. }
  8. console.log(Array.from(unknow));//[1,2,3,4]

数组空位

使用数组字面量初始化数组时,可以使用一串逗号来创建空位(hole)。ECMAScript 会将逗号之间相应索引位置的值当成空位,ES6 规范重新定义了该如何处理这些空位。

创建一个空位数组:

  1. const options = [,,,,,]//5个逗号代表五个空位
  2. console.log(options.length); //有数组长度 5

注意 由于行为不一致和存在性能隐患,因此实践中要避免使用数组空位。如果确实需要 空位,则可以显式地用 undefined 值代替。


for-of 迭代循环

for-of 语句是一种严格的迭代语句,用于遍历可迭代对象的元素,语法如下:

  1. for (property of expression) statement
  2. //下面是示例:
  3. for (const el of [2,4,6,8]) {
  4. document.write(el);
  5. }

在这个例子中,我们使用 for-of 语句显示了一个包含 4 个元素的数组中的所有元素。循环会一直持续到将所有元素都迭代完。与 for 循环一样,这里控制语句中的 const 也不是必需的。但为了确保这个局部变量不被修改,推荐使用 const。

for-of 循环会按照可迭代对象的 next()方法产生值的顺序迭代元素。