JavaScript 数组 详解

234 阅读41分钟

前言

在开发中,数组的使用非常多,会涉及很多的 api 的使用,但是除了几个最常用的 api 之外,其他的 api 用完之后经常就会忘掉了,再用的时候还要查一下。所以就希望系统的总结梳理一下这块内容,写下这篇文章,如果喜欢的话,可以 点赞/收藏,支持一下,希望看完会对大家有帮助。


本文篇幅较长,建议点赞保存再看,也便于日后翻阅。


创建数组

语法

[element1, element2, element3, ... , elementN]

new Array(element1, element2, element3, ... , elementN)

new Array(arrayLength)

例子:

let a = [1,2,3]

let b = new Array(4,5,6)

let c = new Array(3)

参数:

  • elementN

    Array 构造器会根据给定的元素创建一个 JS 数组,但是当且仅当只有一个参数且是数字时除外(详见下面的 arrayLength 参数)。注意,后面这种情况,仅适用于 Array 构造器创建 数组, 不适用于用方括号创建的数组字面量。

  • arrayLength

    给定一个范围在 0 - 2^32 - 1 的整数,此时将会返回一个 **长度(length 的值)**为 arrayLength 的数组对象。注意,此时该数组并没有包含任何 实际的元素,不能不能理所当然地认为它包含 arrayLength 个值为 undefined 的元素,如:上面例子的 c 值是:

    let c = new Array(3)   // (3) [empty × 3]
    Object.keys(c)         // []
    let d=[undefined, undefined, undefined]
    Object.keys(d)         // (3) ["0", "1", "2"]
    // c 和 d 比较证明了上面的说法
    

    但是,如果对这个数组取值,值为 undefined, 这点需要注意:

    c[0]   // undefined
    

数组的深度理解

数组是一种 类列表对象JS 万物皆对象,数组也是一种特殊的对象,这一点要特别注意),它的原型中提供了遍历和修改元素的相关操作。JavaScript 数组的长度和元素类型不是固定的。因为数组的长度可随时改变,并且其数据在内存中也可以不连续,所以 JavaScript 数组不一定是密集型的,这取决于它的使用方式。

只能用整数作为数组元素的索引,而不能用字符串。使用非整数并通过方括号点号来访问或设置数组元素时,所操作的并不是数组列表中的元素,而是数组对象的属性集合上的变量。数组对象的属性和数组元素列表是分开存储的,并且数组的遍历和修改操作也不能作用于这些命名属性。

访问数组元素

JavaScript 数组的索引是从0开始的,第一个元素的索引为0,最后一个元素的索引等于该数组的长度减1。如果指定的索引是一个无效值,JavaScript 数组并不会报错,而是会返回 undefined

虽然数组元素可以看做是数组对象的属性,就像 toString 一样,但是下面的中间一行写法是错误的,运行时会抛出 SyntaxError 异常,而原因则是使用了非法的属性名;并不是 JavaScript 数组有什么特殊之处,而是因为在 JavaScript 中,以数字开头的属性不能用点号引用,必须用方括号。

var years = [1950, 1960, 1970, 1980, 1990, 2000, 2010];
console.log(years.0);   // 语法错误 Uncaught SyntaxError: Unexpected number
console.log(years[0]);  // √

也可以将数组的索引用引号引起来,比如 years[2] 可以写成 years['2']years[2] 中的 2 会被 JavaScript 解释器通过调用 toString 隐式转换成字符串。正因为这样,'2''02'years 中所引用的可能是不同位置上的元素。而下面这个例子也可能会打印 true

console.log(years['2'] != years['02']);  // true
Object.keys(years)  // (7) ["0", "1", "2", "3", "4", "5", "6"] 说明了 JavaScript 解释器通过调用 `toString` 隐式转换成字符串

length 和数字下标之间的关系

JavaScript 数组的 length属性和其数字下标之间有着紧密的联系。数组内置的几个方法(例如 joinsliceindexOf等)都会考虑 length的值。另外还有一些方法(例如 pushsplice等)还会改变 length的值。

var fruits = [];
fruits.push('banana', 'apple', 'peach');

console.log(fruits.length); // 3

使用一个合法的下标为数组元素赋值,并且该下标超出了当前数组的大小的时候,解释器会同时修改 length的值:

fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits));  // ['0', '1', '2', '5']  注意中间的 3,4 没有,说明是两个空值
console.log(fruits.length); // 6

也可以显式地给 length赋一个更大的值:

fruits.length = 10;
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 10

而为 length 赋一个更小的值则会删掉一部分元素:

fruits.length = 2;
console.log(Object.keys(fruits)); // ['0', '1']
console.log(fruits.length); // 2

属性

Array.length

Array 构造函数的 length 属性,其值为1(注意该属性为静态属性,不是数组实例的 length 属性)。

get Array[@@species\]

返回 Array 构造函数。

Array[Symbol.species]
// ƒ Array() { [native code] }

Array.prototype

通过数组的原型对象可以为所有数组对象添加属性。

years[Symbol.unscopables]
// {copyWithin: true, entries: true,fill: true, find: true, findIndex: true, flat: true, flatMap: true, includes: true, keys: true, values: true}

方法

Array.isArray()

用来判断某个变量是否是一个数组对象。

Array.of()

根据一组参数来创建新的数组实例,支持任意的参数数量和类型。

Array.of()Array 构造函数之间的区别在于处理整数参数:Array.of(7) 创建一个具有单个元素 7 的数组,而 Array(7) 创建一个长度为7的空数组(**注意:**这是指一个有7个空位(empty)的数组,而不是由7个undefined组成的数组)。

Array.of(7);       // [7] 
Array.of(1, 2, 3); // [1, 2, 3]

Array(7);          // [ , , , , , , ]
Array(1, 2, 3);    // [1, 2, 3]

原生实现:

if (!Array.of) {
  Array.of = function() {
    return Array.prototype.slice.call(arguments);
  };
}

Array.from()

从类数组对象或者可迭代对象中创建一个新的数组实例

Array.from(arrayLike[, mapFn[, thisArg]])
// arrayLike: 想要转换成数组的伪数组对象或可迭代对象。
// mapFn 可选: 如果指定了该参数,新数组中的每个元素会执行该回调函数。
// thisArg 可选: 可选参数,执行回调函数 mapFn 时 this 对象。

Array.from() 可以通过以下方式来创建数组对象:

  • 伪数组对象(拥有一个 length 属性和若干索引属性的任意对象)
  • 可迭代对象(可以获取对象中的元素,如 Map和 Set 等)

Array.from() 方法有一个可选参数 mapFn,让你可以在最后生成的数组上再执行一次 map 方法后再返回。也就是说Array.from(obj, mapFn, thisArg)就相当于Array.from(obj).map(mapFn, thisArg), 除非创建的不是可用的中间数组。

从 String 生成数组:

Array.from('foo');    // [ "f", "o", "o" ]

从 Set 生成数组:

const set = new Set(['foo', 'bar', 'baz', 'foo']);
Array.from(set);   // [ "foo", "bar", "baz" ]

从 Map 生成数组:

const map = new Map([[1, 2], [2, 4], [4, 8]]);
Array.from(map);   // [[1, 2], [2, 4], [4, 8]]

const mapper = new Map([['1', 'a'], ['2', 'b']]);
Array.from(mapper.values());   // ['a', 'b'];

Array.from(mapper.keys());   // ['1', '2'];

从类数组对象(arguments)生成数组:

function f() {
  return Array.from(arguments);
}
f(1, 2, 3);   // [ 1, 2, 3 ]

在 Array.from 中使用箭头函数:

Array.from([1, 2, 3], x => x + x);   // [2, 4, 6]

Array.from({length: 5}, (v, i) => i);   // [0, 1, 2, 3, 4]

生成器 (range):

const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));

range(0, 4, 1);   // [0, 1, 2, 3, 4] 

range(1, 10, 2);    // [1, 3, 5, 7, 9]

range('A'.charCodeAt(0), 'Z'.charCodeAt(0), 1).map(x => String.fromCharCode(x));
// ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

数组去重合并:

function combine(){ 
    let arr = [].concat.apply([], arguments);  //没有去重复的新数组 
    return Array.from(new Set(arr));
} 

var m = [1, 2, 2], n = [2,3,3]; 
console.log(combine(m,n));                     // [1, 2, 3]

实例方法


一、改变原数组的方法(9个)

let a = [1,2,3]
ES5: a.push() / a.pop() / a.unshift() / a.shift() / a.splice() / a.reverse() / a.sort()
ES6: a.fill() / a.copyWithin()  // 这两个为实验性API

push() 向数组的末尾添加元素

定义:push() 方法可以向数组的末尾添加一个或多个元素,返回 添加元素后 数组的长度

参数:item1, item2, ... , itemX, 要添加到数组末尾的元素

let a = [1, 2, 3]
let item = a.push('末尾1', '末尾2')
console.log(a, item)   // [1, 2, 3, "末尾1", "末尾2"]  5

pop() 删除数组的最后一个元素

定义:pop() 方法 删除数组的最后一个元素,并返回这个元素

参数:无

let a = [1, 2, 3]
let item = a.pop()
console.log(a, item)   // [1, 2]  3

unsifrt() 向数组的开头添加元素

定义:unshift() 方法 向数组的开头添加一个 或 多个元素,返回 添加元素后 数组的长度

参数:item1, item2, ... , itemX, 要添加到数组开头的元素

let a = [1, 2, 3]
let item = a.unshift('开头1', '开头2')
console.log(a, item)   // ["开头1", "开头2", 1, 2, 3]   5

shift() 删除数组的第一个元素

定义:pop() 方法 删除数组的最后一个元素,并返回这个元素

参数:无

let a = [1, 2, 3]
let item = a.shift()
console.log(a, item)   // [2, 3]   1

splice() 添加/删除/替换数组元素

定义:splice() 方法 在任意的位置给数组添加或删除任意个元素,并返回被删除元素组成的数组

语法:array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

参数:

  1. start

指定修改的开始位置(从0计数)。如果超出了数组的长度,则从数组末尾开始添加内容(前提是指定了要添加的元素,否则不添加,数组不变);如果是负值,则表示从数组末位开始的第几位(从-1计数,这意味着-n是倒数第n个元素并且等价于array.length-n);如果负数的绝对值大于数组的长度,则表示开始位置为第0位。

注意:start 值 是负数,删除的时候也是从前往后删除,不是从后往前删除。

  1. deleteCount 可选

整数,表示要移除的数组元素的个数。

如果 deleteCount 被省略了,或者它的值大于等于array.length - start(也就是说,如果它大于或者等于start之后的所有元素的数量),那么start之后数组的所有元素都会被删除。

如果 deleteCount 是 0 或者负数,则不移除元素。这种情况下,至少应添加一个新元素,否则相当于没有改变数组。

  1. item1, item2, *...* 可选

要添加进数组的元素,从start 位置开始,先删除要删除的元素,然后添加。如果没有要添加的元素,则 splice() 只删除数组元素。

注意:添加元素时,从指定的索引位置开始添加的,当前处于指定索引位置的元素会后移

返回值:由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。

eg1:删除元素

// 从数组下标0开始,删除3个元素
let a = [1, 2, 3, 4, 5, 6, 7];
let item = a.splice(0, 3); // [1,2,3]
console.log(a); // [4,5,6,7]

let item = a.splice(-1, 3); // [7]
// 从最后一个元素开始删除3个元素,因为最后一个元素,所以只删除了7

eg2: 删除并添加

// 从数组下标0开始,删除3个元素,并添加元素'添加'
let a = [1, 2, 3, 4, 5, 6, 7];
let item = a.splice(0,3,'添加'); // [1,2,3]
console.log(a); // ['添加',4,5,6,7]
// 从数组最后第二个元素开始,删除3个元素,并添加两个元素'添加1'、'添加2'
let b = [1, 2, 3, 4, 5, 6, 7];
let item = b.splice(-2,3,'添加1','添加2'); // [6,7]
console.log(b); // [1,2,3,4,5,'添加1','添加2']

eg3: 不删除只添加:

let a = [1, 2, 3, 4, 5, 6, 7];
let item = a.splice(0,0,'添加1','添加2'); // [] 没有删除元素,返回空数组
console.log(a); // ['添加1','添加2',1,2,3,4,5,6,7]

let b = [1, 2, 3, 4, 5, 6, 7];
let item = b.splice(-1,0,'添加1','添加2'); // [] 没有删除元素,返回空数组
console.log(b); // [1,2,3,4,5,6,'添加1','添加2',7] 在最后一个元素的前面添加两个元素

sort() 数组排序

定义: sort()方法对数组元素进行排序,并返回 这个 数组, 注意返回的是这个数组本身,不是一个新数组。

参数可选: 规定排序顺序的比较函数。

默认情况下sort()方法没有传比较函数的话,默认按字母升序,如果不是元素不是字符串的话,会调用toString()方法将元素转化为字符串的Unicode(万国码)位点,然后再比较字符。

// 字符串排列 看起来很正常
var a = ["Banana", "Orange", "Apple", "Mango"];
a.sort(); // ["Apple","Banana","Mango","Orange"]
// 数字排序的时候 因为转换成Unicode字符串之后,有些数字会比较大会排在后面 这显然不是我们想要的
var	a = [10, 1, 3, 20,25,8];
console.log(a.sort()) // [1,10,20,25,3,8];

比较函数的两个参数:

sort的比较函数有两个默认参数,要在函数中接收这两个参数,这两个参数是数组中两个要比较的元素,通常我们用 a 和 b 接收两个将要比较的元素:

  • 若比较函数返回值<0,那么a将排到b的前面;
  • 若比较函数返回值=0,那么a 和 b 相对位置不变;
  • 若比较函数返回值>0,那么b 排在a 将的前面;

sort排序常见用法

  1. 数组元素为数字的升序、降序:

    var array =  [10, 1, 3, 4,20,4,25,8];
    // 升序 a-b < 0   a将排到b的前面,按照a的大小来排序的 
    // 比如被减数a是10,减数是20  10-20 < 0   被减数a(10)在减数b(20)前面   
    array.sort(function(a,b){
        return a-b;  // 升序
    });
    console.log(array); // [1,3,4,4,8,10,20,25];
    // 降序 被减数和减数调换了  20-10>0 被减数b(20)在减数a(10)的前面
    array.sort(function(a,b){
        return b-a;  // 降序
    });
    console.log(array); // [25,20,10,8,4,4,3,1];
    
  2. 数组多条件排序

    var array = [{id:10,age:2},{id:5,age:4},{id:6,age:10},{id:9,age:6},{id:2,age:8},{id:10,age:9}];
    array.sort(function(a,b){
        if(a.id === b.id){// 如果id的值相等,按照age的值降序
            return b.age - a.age
        }else{ // 如果id的值不相等,按照id的值升序
            return a.id - b.id
        }
    })
    // [{"id":2,"age":8},{"id":5,"age":4},{"id":6,"age":10},{"id":9,"age":6},{"id":10,"age":9},{"id":10,"age":2}] 
    
  3. 自定义比较函数

类似的:运用好返回值,我们可以写出任意符合自己需求的比较函数

var array = [{name:'Koro1'},{name:'Koro1'},{name:'OB'},{name:'Koro1'},{name:'OB'},{name:'OB'}];
array.sort(function(a,b){
    if(a.name === 'Koro1'){// 如果name是'Koro1' 返回-1 ,-1<0 a排在b的前面
        return -1
    }else{ // 如果不是的话,a排在b的后面
        return 1
    }
})
// [{"name":"Koro1"},{"name":"Koro1"},{"name":"Koro1"},{"name":"OB"},{"name":"OB"},{"name":"OB"}] 
  1. 对 非 ASCII 字符排序

当排序非 ASCII 字符的字符串(如包含类似 e, é, è, a, ä 等字符的字符串)。一些非英语语言的字符串需要使用 String.localeCompare。这个函数可以将函数排序到正确的顺序。

var items = ['réservé', 'premier', 'cliché', 'communiqué', 'café', 'adieu'];
items.sort(function (a, b) {
  return a.localeCompare(b);
});
// items is ['adieu', 'café', 'cliché', 'communiqué', 'premier', 'réservé']

reverse() 调转数组顺序

定义:reverse() 方法 颠倒数组中元素的排列顺序,即原先的第一个变为最后一个,原先的最后一个变为第一个,并返回 这个 数组

参数: 无

  • 颠倒数组中的元素

reverse() 的调用返回了一个颠倒后的数组 a的引用。

const a = [1, 2, 3];
console.log(a); // [1, 2, 3]

a.reverse(); 
console.log(a); // [3, 2, 1]
  • 颠倒类数组中的元素

下例创造了一个类数组对象 a, 包含3个元素和一个 length 属性, 然后颠倒这个类数组对象。 reverse() 的调用返回一个颠倒后的类数组对象 a的引用。

const a = {0: 1, 1: 2, 2: 3, length: 3};
console.log(a); // {0: 1, 1: 2, 2: 3, length: 3}

Array.prototype.reverse.call(a); // 相同的语法使用 apply()
console.log(a); // {0: 3, 1: 2, 2: 1, length: 3}

copyWithin() 指定的位置成员复制到其他位置

定义: 在当前数组内部,将指定位置的成员复制到其他位置,并返回这个数组

需要注意的是:

  1. 读了几个元素就从开始被替换的地方替换几个元素
  2. 数组的长度 不会 改变

语法: arr.copyWithin(target[, start[, end]])

参数:

三个参数都是数值,如果不是,会自动转为数值.

  1. target(必需):从该位置开始替换数据。如果为负值,表示倒数。
  2. start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
  3. end(可选):到该位置前停止读取数据,默认等于数组长度。使用负数可从数组结尾处规定位置。

举例:

// -2相当于3号位,-1相当于4号位
[1, 2, 3, 4, 5].copyWithin(0, -2, -1)
// [4, 2, 3, 4, 5]
var a=['OB1','Koro1','OB2','Koro2','OB3','Koro3','OB4','Koro4','OB5','Koro5']
// 2位置开始被替换,3位置开始读取要替换的 5位置前面停止替换
a.copyWithin(2,3,5)
// ["OB1","Koro1","Koro2","OB3","OB3","Koro3","OB4","Koro4","OB5","Koro5"] 

fill() 填充数组

定义:fill() 方法 将数组中指定区间的所有元素的值,都替换成某个固定的值。从起始索引到终止索引内的全部元素。不包括终止索引。并返回 这个 数组

语法:arr.fill(value[, start[, end]])

参数:

value:用来填充数组元素的值。

start 可选: 起始索引,默认值为0。

end 可选: 终止索引,默认值为 this.length

如果 start 是个负数, 则开始索引会被自动计算成为 length+start, 其中 lengththis 对象的 length属性值。如果 end 是个负数, 则结束索引会被自动计算成为 length+end

[1, 2, 3].fill(4);               // [4, 4, 4]
[1, 2, 3].fill(4, 1);            // [1, 4, 4]
[1, 2, 3].fill(4, 1, 2);         // [1, 4, 3]
[1, 2, 3].fill(4, 1, 1);         // [1, 2, 3]
[1, 2, 3].fill(4, 3, 3);         // [1, 2, 3]
[1, 2, 3].fill(4, -3, -2);       // [4, 2, 3]
[1, 2, 3].fill(4, NaN, NaN);     // [1, 2, 3]
[1, 2, 3].fill(4, 3, 5);         // [1, 2, 3]
Array(3).fill(4);                // [4, 4, 4]
[].fill.call({ length: 3 }, 4);  // {0: 4, 1: 4, 2: 4, length: 3}

// Objects by reference.
var arr = Array(3).fill({}) // [{}, {}, {}];
// 需要注意如果fill的参数为引用类型,会导致都执行都一个引用类型
// 如 arr[0] === arr[1] 为true
arr[0].hi = "hi"; // [{ hi: "hi" }, { hi: "hi" }, { hi: "hi" }]

二、访问方法(不改变原数组)(8个)

ES5:
slice, concat, join, indixOf, lastIndexOf, toString, toLocalString,
ES7:
includes

slice() 浅拷贝数组的一部分元素

定义:slice 方法返回一个 原数组的从开始索引到结束索引(不包括结束索引)之间的 浅拷贝数组。返回的是一个新数组,是原数组的一个浅拷贝。

注意:字符串也有一个slice() 方法是用来提取字符串的,不要弄混了。

语法:arr.slice([begin[, end]])

参数:

  • begin 可选

    提取起始处的索引(从 0 开始),从该索引开始提取原数组元素。

    如果该参数为负数,则表示从原数组中的倒数第几个元素开始提取,slice(-2) 表示提取原数组中的倒数第二个元素到最后一个元素(包含最后一个元素)。

    如果省略 begin,则 slice 从索引 0 开始。

    如果 begin 大于原数组的长度,则会返回空数组。

  • end 可选

    提取终止处的索引(从 0 开始),在该索引处结束提取原数组元素。slice 会提取原数组中索引从 beginend 的所有元素(包含 begin,但不包含 end)。

    slice(1,4) 会提取原数组中从第二个元素开始一直到第四个元素的所有元素 (索引为 1, 2, 3的元素)。

    如果该参数为负数, 则它表示在原数组中的倒数第几个元素结束抽取。 slice(-2,-1) 表示抽取了原数组中的倒数第二个元素到最后一个元素(不包含最后一个元素,也就是只有倒数第二个元素)。

    如果 end 被省略,则 slice 会一直提取到原数组末尾。

    如果 end 大于数组的长度,slice 也会一直提取到原数组末尾。

说明:

slice 不会修改原数组,只会返回一个浅拷贝了原数组中的元素的一个新数组。原数组的元素会按照下述规则拷贝:

  • 如果该元素是个对象引用 (不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。

  • 对于字符串、数字及布尔值来说(不是 StringNumber或者 Boolean对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

如果向两个数组任一中添加了新元素,则另一个不会受到影响。

let a= [{name:'OBKoro1'}];
let b=a.slice();
console.log(b,a); // [{"name":"OBKoro1"}]  [{"name":"OBKoro1"}]
a[0].name='改变原数组';
console.log(b,a); // [{"name":"改变原数组"}] [{"name":"改变原数组"}]
b[0].name='改变拷贝数组',b[0].koro='改变拷贝数组';
console.log(b,a);  //  [{"name":"改变拷贝数组","koro":"改变拷贝数组"}] [{"name":"改变拷贝数组","koro":"改变拷贝数组"}]

类数组(Array-like)对象

slice 方法可以用来将一个类数组(Array-like)对象/集合转换成一个新数组。你只需将该方法绑定到这个对象上。 一个函数中的 arguments就是一个类数组对象的例子。

function list() {
  return Array.prototype.slice.call(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

除了使用 Array.prototype.slice.call(arguments),你也可以简单的使用 [].slice.call(arguments) 来代替。另外,你可以使用 bind 来简化该过程。

var unboundSlice = Array.prototype.slice;
var slice = Function.prototype.call.bind(unboundSlice);

function list() {
  return slice(arguments);
}

var list1 = list(1, 2, 3); // [1, 2, 3]

concat() 合并数组

定义:concat() 方法 返回一个由当前数组和其它若干个数组或者若干个非数组值组合而成的新数组。

语法:var new_array = old_array.concat(value1[, value2[, ...[, valueN]]])

参数:valueN可选

将数组和/或值连接成新数组。如果省略了valueN参数参数,则concat会返回一个它所调用的已存在的数组的浅拷贝。

concat方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。它不会递归到嵌套数组参数中。

concat方法不会改变this或任何作为参数提供的数组,而是返回一个浅拷贝,它包含与原始数组相结合的相同元素的副本。 原始数组的元素将复制到新数组中,如下所示:

  • 对象引用(而不是实际对象):concat将对象引用复制到新数组中。 原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。 这包括也是数组的数组参数的元素。

  • 数据类型如字符串,数字和布尔(不是StringNumberBoolean对象):concat将字符串和数字的值复制到新数组中。

**注意:**数组/值在连接时保持不变。此外,对于新数组的任何操作(仅当元素不是对象引用时)都不会对原始数组产生影响,反之亦然。

ES6扩展运算符...合并数组

因为ES6的语法更简洁易懂,所以现在合并数组我大部分采用...来处理,...运算符可以实现cancat的每个栗子,且更简洁和具有高度自定义数组元素位置的效果。

    let a = [2, 3, 4, 5]
    let b = [ 4,...a, 4, 4]
    console.log(a,b); //  [2, 3, 4, 5] [4,2,3,4,5,4,4]
复制代码

更多关于扩展符的详细内容移步阮一峰大神的ECMAScript 6 入门

join() 数组转字符串

join() 方法将一个数组(或一个类数组对象)的所有元素通过一个指定的分隔符分割连接成一个字符串,并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符;如果是一个空数组,则返回空字符串

语法:arr.join([separator])

参数:separator 可选

指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。如果缺省该值,数组元素用逗号(,)分隔。如果separator是空字符串(""),则所有元素之间都没有任何字符。

注意:如果一个元素为 undefinednull,它会被转换为空字符串。

let a= ['hello','world'];
let str=a.join(); // 'hello,world'
let str2=a.join('+'); // 'hello+world'

使用 join 方法或者下文说到的 toString 方法时,当数组中的元素也是数组或者是对象时会出现什么情况?

let a= [['OBKoro1','23'],'test'];
let str1=a.join(); // OBKoro1,23,test
let str2=a.join(';'); // OBKoro1,23;test
let b= [{name:'OBKoro1',age:'23'},'test'];
let str2 = b.join(); // [object Object],test
// 对象转字符串推荐JSON.stringify(obj);

所以,join()/toString()方法在数组元素是数组的时候,会将里面的数组调用 join()/toString()方法,如果是对象的话,对象会被转为[object Object]字符串。注意:里面的数组调用 join() 方法,没有 分隔符参数

连接类数组对象

下面的示例将连接类数组对象(arguments),通过在Array.prototype.join上调用Function.prototype.call

function f(a, b, c) {
  var s = Array.prototype.join.call(arguments);
  console.log(s); // '1,a,true'
}
f(1, 'a', true);

toLocalString() 数组转字符串

定义: 返回一个表示数组元素的字符串。该字符串由数组中的每个元素的 toLocaleString() 返回值经调用 join() 方法连接(由逗号隔开)组成。

语法: array.toLocaleString()

参数:无。

let a=[{name:'OBKoro1'},23,'abcd',new Date()];
let str=a.toLocaleString(); // [object Object],23,abcd,2018/5/28 下午1:52:20 

如上述栗子:调用数组的toLocaleString方法,数组中的每个元素都会调用自身的toLocaleString方法,对象调用对象的toLocaleString,Date调用Date的toLocaleString

toString() 数组转字符串

定义:toString() 返回一个用 逗号 分割的字符串,表示指定的数组及其元素。

语法: arr.toString()

Array对象覆盖了ObjecttoString 方法。对于数组对象,toString 方法连接数组并返回一个字符串,其中包含用逗号分隔的每个数组元素。

当一个数组被作为文本值或者进行字符串连接操作时,将会自动调用其 toString 方法。

不足的是:该方法的效果和join方法一样,都是用于数组转字符串的,但是与join方法相比没有优势,也不能自定义字符串的分隔符。

   let b= [ 'toString','演示'].toString(); // toString,演示
   let a= ['调用toString','连接在我后面']+'啦啦啦'; // 调用toString,连接在我后面啦啦啦

indexOf()查找数组中某个元素第一次出现的下标(索引)

定义:返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1。

语法:arr.indexOf(searchElement[, fromIndex])

参数:

  • searchElement(必须):要查找的元素

  • fromIndex 可选

开始查找的位置。如果该索引值大于或等于数组长度,意味着不会在数组里查找,返回-1。如果参数中提供的索引值是一个负值,则将其作为数组末尾的一个抵消,即-1表示从最后一个元素开始查找,-2表示从倒数第二个元素开始查找 ,以此类推。 注意:如果参数中提供的索引值是一个负值,并不改变其查找顺序,查找顺序仍然是从前向后查询数组。如果抵消后的索引值仍小于0,则整个数组都将会被查询。其默认值为0.

数组的indexOf搜索跟字符串的indexOf不一样,数组的indexOf使用严格相等===搜索元素,即数组元素要完全匹配才能搜索成功。

注意:indexOf()不能识别NaN

举例:

let a=['啦啦',2,4,24,NaN]
console.log(a.indexOf('啦'));  // -1 
console.log(a.indexOf('NaN'));  // -1 
console.log(a.indexOf('啦啦')); // 0

使用场景:

  1. 数组去重
  2. 根据获取的数组下标执行操作,改变数组中的值等。
  3. 判断是否存在,执行操作。
var array = [2, 5, 9];
array.indexOf(2);     // 0
array.indexOf(7);     // -1
array.indexOf(9, 2);  // 2
array.indexOf(2, -1); // -1
array.indexOf(2, -3); // 0

找出指定元素出现的所有位置

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.indexOf(element);
while (idx != -1) {
  indices.push(idx);
  idx = array.indexOf(element, idx + 1);
}
console.log(indices);
// [0, 2, 4]

lastIndexOf() 查找指定元素在数组中的最后一个位置

定义: 方法返回指定元素,在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex 处开始。

语法: arr.lastIndexOf(searchElement[, fromIndex])

参数:

  • searchElement(必须):被查找的元素。

  • fromIndex 可选:

    从此位置开始逆向查找。默认为数组的长度减 1(arr.length - 1),即整个数组都被查找。如果该值大于或等于数组的长度,则整个数组会被查找。如果为负值,将其视为从数组末尾向前的偏移。即使该值为负,数组仍然会被从后向前查找。如果该值为负时,其绝对值大于数组长度,则方法返回 -1,即数组不会被查找。

let a=['OB',4,'Koro1',1,2,'Koro1',3,4,5,'Koro1']; // 数组长度为10
// let b=a.lastIndexOf('Koro1',4); // 从下标4开始往前找 返回下标2
// let b=a.lastIndexOf('Koro1',100); //  大于或数组的长度 查找整个数组 返回9
// let b=a.lastIndexOf('Koro1',-11); // -1 数组不会被查找
let b=a.lastIndexOf('Koro1',-9); // 从第二个元素4往前查找,没有找到 返回-1

查找所有元素

下例使用 lastIndexOf 查找到一个元素在数组中所有的索引(下标),并使用 push 将所有添加到另一个数组中。

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.lastIndexOf(element);

while (idx != -1) {
  indices.push(idx);
  idx = (idx > 0 ? array.lastIndexOf(element, idx - 1) : -1);
}

console.log(indices);
// [4, 2, 0];

注意,我们要单独处理idx==0时的情况,因为如果是第一个元素,忽略了fromIndex参数则第一个元素总会被查找,陷入死循环。这不同于indexOf方法

includes() 查找数组是否包含某个元素 返回布尔值

定义:方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。

语法:arr.includes(valueToFind[, fromIndex])

参数:

  • valueToFind(必须):需要查找的元素值。

    注意: 使用 includes()比较字符串和字符时是区分大小写。

  • fromIndex 可选

    默认为 0。从fromIndex 索引处开始查找 valueToFind。如果为负值,则按升序从 array.length + fromIndex 的索引开始搜 (即从末尾开始往前跳 fromIndex 的绝对值个索引,然后往后搜寻),负值绝对值超过长数组度,重置从0开始搜索。

includes方法是为了弥补indexOf方法的缺陷而出现的:

  1. indexOf方法不能识别NaN
  2. indexOf方法检查是否包含某个值不够语义化,需要判断是否不等于-1,表达不够直观

举例:

let a=['OB','Koro1',1,NaN];
let b=a.includes(NaN); // true 识别NaN
let b=a.includes('Koro1',100); // false 超过数组长度 不搜索
let b=a.includes('Koro1',-3);  // true 从倒数第三个元素开始搜索 
let b=a.includes('Koro1',-100);  // true 负值绝对值超过数组长度,搜索整个数组

兼容性(MDN): chrome47, Firefox 43,Edge 14,Opera 34, Safari 9,IE 未实现。


三、遍历方法(迭代方法)(12个)

在下面的众多遍历方法中,有很多方法都需要指定一个回调函数作为参数。在每一个数组元素都分别执行完回调函数之前,数组的length属性会被缓存在某个地方,所以,如果你在回调函数中为当前数组添加了新的元素,那么那些新添加的元素是不会被遍历到的。此外,如果在回调函数中对当前数组进行了其它修改,比如改变某个元素的值或者删掉某个元素,那么随后的遍历操作可能会受到未预期的影响。总之,不要尝试在遍历过程中对原数组进行任何修改,虽然规范对这样的操作进行了详细的定义,但为了可读性和可维护性,请不要这样做。

ES5:
forEach, every, some, map, filter, reduce, reduceRight,
ES6:
find, findIndex, keys, values, entries

forEach()

定义:对数组的每个元素执行一次给定的函数。返回值 为 undefined

语法:arr.forEach(callback(currenundefinedtValue [, index [, array]])[, thisArg])

参数:

  • callback

    为数组中每个元素执行的函数,该函数接收一至三个参数:

    1. currentValue数组中正在处理的当前元素。

    2. index 可选数组中正在处理的当前元素的索引。

    3. array 可选forEach() 方法正在操作的数组。

  • thisArg 可选

    可选参数。当执行回调函数 callback 时,用作 this 的值。

对于 forEach() 需要知道的是:

  1. forEach() 遍历的范围在第一次调用 callback 就会确定,调用 forEach 后添加到数组中的项不会被 callback 访问到。
  2. 如果已经存在的值被改变,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值。
  3. 对于已在迭代过程中删除的元素,或者空元素会跳过回调函数,不会被遍历到。
  4. 如果已访问的元素在迭代时被删除了(例如使用 shift()),之后的元素将被跳过.
  5. forEach() 为每个数组元素执行一次 callback 函数,与 map()]或者 reduce()不同的是,它总是返回 undefined值,即使 return 了一个值,并且不可链式调用。
  6. 除了抛出异常以外,没有办法中止或跳出 forEach() 循环,只能return 结束本次回调,进行下一次回调

若你想要提前终止循环,你可以使用:

  • 一个普通的 for 循环
  • while 循环
  • for ... of / for ... in 循环
  • every
  • some
  • find
  • findIndex

只要条件允许,也可以使用 filter()提前过滤出需要遍历的部分,再用 forEach() 处理。

将 for 循环转换为 forEach

const items = ['item1', 'item2', 'item3'];
const copy = [];

// before
for (let i=0; i<items.length; i++) {
  copy.push(items[i]);
}

// after
items.forEach(function(item){
  copy.push(item);
});

空元素会跳过回调函数

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}

// 注意索引 2 被跳过了,因为在数组的这个位置没有项
[2, 5, , 9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[3] = 9

使用 thisArg

举个勉强的例子,按照每个数组中的元素值,更新一个对象的属性:

function Counter() {
  this.sum = 0;
  this.count = 0;
}
Counter.prototype.add = function(array) {
  array.forEach(function(entry) {
    this.sum += entry;
    ++this.count;
  }, this);
  // ^---- Note
};

const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 === (1 + 1 + 1)
obj.sum;
// 16 === (2 + 5 + 9)

因为 thisArg 参数(this)传给了 forEach(),每次调用时,它都被传给 callback 函数,作为它的 this 值。

**注意:**如果使用 箭头函数表达式 来传入函数参数, thisArg 参数会被忽略,因为箭头函数在词法上绑定了 this值。

如果数组在迭代时被修改了,则其他元素会被跳过。

下面的例子会输出 "one", "two", "four"。当到达包含值 "two" 的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 "four" 正位于在数组更前的位置,所以 "three" 会被跳过。 forEach() 不会在迭代之前创建数组的副本。

var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
  console.log(word);
  if (word === 'two') {
    words.shift();
  }
});
// one
// two
// four

扁平化数组

下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组,你可以考虑使用 Array.prototype.flat()(预计将成为 ES2019 的一部分,并且已在主要浏览器中实现)或参考其 polyfill。

function flatten(arr) {
  const result = [];
  arr.forEach((i) => {
    if (Array.isArray(i))
      result.push(...flatten(i));
    else
      result.push(i);
  })
  
  return result;
}
// Usage
const problem = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
flatten(problem); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

every()

定义:测试一个数组内的所有元素是否都能通过某个指定函数的测试。它返回一个布尔值。不会改变原数组

注意:若收到一个空数组,此方法在一切情况下都会返回 true

语法:arr.every(callback[, thisArg])

参数:

  • callback

    用来测试每个元素的函数,它可以接收三个参数:

    1. element:用于测试的当前值。

    2. index可选:用于测试的当前值的索引。

    3. array可选:调用 every 的当前数组。

  • thisArg

    执行 callback 时使用的 this 值。

every 方法为数组中的每个元素执行一次 callback 函数,直到它找到一个会使 callback 返回 falsy 的元素。如果发现了一个这样的元素,every 方法将会立即返回 false,剩余的元素不会再进行检测。否则,callback 为每一个元素返回 trueevery 就会返回 truecallback 只会为那些已经被赋值的索引调用。不会为那些被删除或从未被赋值的索引调用。

关于 every() 需要知道的:

  1. every 遍历的元素范围在第一次调用 callback 之前就已确定了。
  2. 在调用 every 之后添加到数组中的元素不会被 callback 访问到。
  3. 如果数组中存在的元素被更改,则他们传入 callback 的值是 every 访问到他们那一刻的值。
  4. 那些被删除的元素或从来未被赋值的元素将不会被访问到。
function isBigEnough(element, index, array) { 
    return element >= 10; // 判断数组中的所有元素是否都大于10
}
let result = [12, 5, 8, 130, 44].every(isBigEnough);   // false
let result = [12, 54, 18, 130, 44].every(isBigEnough); // true
// 接受箭头函数写法 
[12, 5, 8, 130, 44].every(x => x >= 10); // false
    [12, 54, 18, 130, 44].every(x => x >= 10); // true

some()

定义:测试数组中是不是至少有1个元素通过了被提供的函数测试。它返回的是一个Boolean类型的值。不改变原数组

**注意:**如果用一个空数组进行测试,在任何情况下它返回的都是false。、

语法:arr.some(callback(element[, index[, array]])[, thisArg])

参数:

  • callback

    用来测试每个元素的函数,接受三个参数:

    1. element:数组中正在处理的元素。
    2. index 可选:数组中正在处理的元素的索引值。
    3. array可选:some()被调用的数组。
  • thisArg可选

    执行 callback 时使用的 this 值。

some() 为数组中的每一个元素执行一次 callback 函数,直到找到一个使得 callback 返回一个“真值”(即可转换为布尔值 true 的值)。如果找到了这样一个值,some() 将会立即返回 true。否则,some() 返回 falsecallback 只会在那些”有值“的索引上被调用,不会在那些被删除或从来未被赋值的索引上调用。

关于 some 需要知道的同上 every() 方法

 function isBigEnough(element, index, array) {
   return (element >= 10); //数组中是否有一个元素大于 10
 }
 let result = [2, 5, 8, 1, 4].some(isBigEnough); // false
 let result = [12, 5, 8, 1, 4].some(isBigEnough); // true

filter()

定义:返回一个新数组, 其包含 通过测试函数(在测试函数中返回 true ) 的所有元素。如果没有任何数组元素通过测试,则返回空数组。不改变原数组

语法:var newArray = arr.filter(callback(element[, index[, array]])[, thisArg])

参数:

  • callback

    用来测试数组的每个元素的函数。返回 true 表示该元素通过测试,保留该元素,false 则不保留。它接受以下三个参数:

    1. element数组中当前正在处理的元素。
    2. index可选正在处理的元素在数组中的索引。
    3. array可选调用了 filter 的数组本身。
  • thisArg可选

    执行 callback 时,用于 this 的值。

filter 为数组中的每个元素调用一次 callback 函数,并利用所有使得 callback 返回 true 或[等价于 true 的值]的元素创建一个新数组。callback 只会在已经赋值的索引上被调用,对于那些已经被删除或者从未被赋值的索引不会被调用。那些没有通过 callback 测试的元素会被跳过,不会被包含在新数组中。

关于 filter() 方法需要知道的同上。

let a = [32, 33, 16, 40];
let result = a.filter(function (value, index, array) {
    return value >= 18; // 返回a数组中所有大于18的元素
});
console.log(result,a);// [32,33,40] [32,33,16,40]

map()

定义:创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的新数组。原数组不变

语法:

var new_array = arr.map(function callback(currentValue[, index[, array]]) {
 // Return element for new_array 
}[, thisArg])

参数:

  • callback

    生成新数组元素的函数,使用三个参数:

    1. currentValue : callback 数组中正在处理的当前元素。
    2. index可选: callback 数组中正在处理的当前元素的索引。
    3. array可选: map 方法调用的数组。
  • thisArg可选

    执行 callback 函数时值被用作this

map 方法会给原数组中的每个元素都按顺序调用一次 callback 函数。callback 每次执行后的返回值(包括 undefined)组合起来形成一个新数组。注意:如果 callback 函数 没有return 一个值,那么或默认 return undefined。

callback 函数只会在有值的索引上被调用;那些从来没被赋过值或者使用 delete 删除的索引则不会被调用。

因为map生成一个新数组,当你不打算使用返回的新数组却使用map是违背设计初衷的,请用forEach或者for-of替代。不该使用map的情况: A) 不打算使用返回的新数组,或/且 B) 没有从回调函数中返回值。

var numbers = [1, 4, 9];
var doubles = numbers.map(function(num) {
  return num * 2;
});
// doubles数组的值为: [2, 8, 18]
// numbers数组未被修改: [1, 4, 9]

var map = Array.prototype.map
var a = map.call("Hello World", function(x) { 
  return x.charCodeAt(0); 
})
// a的值为[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

容易忽略的一个问题

["1", "2", "3"].map(parseInt);   //  [1, NaN, NaN]

为什么输出结果不是我们期望的 [1, 2, 3] 呢?

通常情况下,map 方法中的 callback 函数只需要接受一个参数,就是正在被遍历的数组元素本身。但这并不意味着 map 只给 callback 传了一个参数。这个思维惯性可能会让我们犯一个很容易犯的错误。

实际上这行代码是这样执行的:

["1", "2", "3"].map((item, index) => parseInt(item, index));   //  [1, NaN, NaN]
// parseInt(string, radix) 最重要的就是 parseInt 是可以接收两个参数的,第一个参数是 要转化的值,第二个参数是 当前值使用的 基数

解决方案:

function returnInt(element) {
  return parseInt(element, 10);
}
['1', '2', '3'].map(returnInt); // [1, 2, 3]

['1', '2', '3'].map( str => parseInt(str) );
['1', '2', '3'].map(Number); // [1, 2, 3]
// 但是浮点数 或 使用科学计数法的数 使用 Number 不能转化成对应的整数
['1.1', '2.2e2', '3e300'].map(Number); // [1.1, 220, 3e+300]
['1.1', '2.2e2', '3e300'].map( str => parseInt(str) ); // [1, 2, 3]

reduce()

定义:对数组中的每个元素执行一个由您提供的reducer函数(升序执行 / 从左到右),将其结果汇总为单个返回值。

语法:arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

参数:

  • callback

    执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,包含四个参数:

    1. **accumulator**累计器:累计回调的返回值; 它是上一次调用回调时返回的累积值,或initialValue(见于下方)。
    2. currentValue:数组中正在处理的元素。
    3. index : 可选数组中正在处理的当前元素的索引。 如果提供了initialValue,则起始索引号为0,否则从索引1起始。
    4. array: 可选调用reduce()的数组
  • initialValue可选

    作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错,会抛出TypeError

回调函数第一次执行时,accumulatorcurrentValue的取值有两种情况:

  1. 如果调用reduce()时提供了initialValueaccumulator取值为initialValuecurrentValue取数组中的第一个值;
  2. 如果没有提供 initialValue,那么accumulator取数组中的第一个值,currentValue取数组中的第二个值。

需要注意的是:

  1. 如果数组为空且没有提供initialValue,会抛出TypeError
  2. 如果数组仅有一个元素(无论位置如何)并且没有提供initialValue, 或者有提供initialValue但是数组为空,那么此唯一值将被返回并且callback不会被执行。
// 将二维数组转化为一维
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
 ( acc, cur ) => acc.concat(cur),
 []
);

// 计算数组中每个元素出现的次数
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) { 
  if (name in allNames) {
    allNames[name]++;
  }
  else {
    allNames[name] = 1;
  }
  return allNames;
}, {});
// countedNames is: { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }

// 按属性对object分类
var people = [
  { name: 'Alice', age: 21 },
  { name: 'Max', age: 20 },
  { name: 'Jane', age: 20 }
];

function groupBy(objectArray, property) {
  return objectArray.reduce(function (acc, obj) {
    var key = obj[property];
    if (!acc[key]) {
      acc[key] = [];
    }
    acc[key].push(obj);
    return acc;
  }, {});
}
var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// { 
//   20: [
//     { name: 'Max', age: 20 }, 
//     { name: 'Jane', age: 20 }
//   ], 
//   21: [{ name: 'Alice', age: 21 }] 
// }

// 按顺序运行Promise
function runPromiseInSequence(arr, input) {
  return arr.reduce(
    (promiseChain, currentFunction) => promiseChain.then(currentFunction),
    Promise.resolve(input)
  );
}
// promise function 1
function p1(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 5);
  });
}
// promise function 2
function p2(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 2);
  });
}
// function 3  - will be wrapped in a resolved promise by .then()
function f3(a) {
 return a * 3;
}
// promise function 4
function p4(a) {
  return new Promise((resolve, reject) => {
    resolve(a * 4);
  });
}
const promiseArr = [p1, p2, f3, p4];
runPromiseInSequence(promiseArr, 10)
  .then(console.log);   // 1200

reduceRight 从右至左累加

这个方法除了与reduce执行方向相反外,其他完全与其一致,请参考上述 reduce 方法介绍。

ES6:find()& findIndex() 根据条件找到数组成员

定义:

find() : 找到第一个满足测试函数的元素并返回那个元素的值,如果找不到,则返回 undefined

findIndex() : 找到第一个满足测试函数的元素并返回那个元素的索引,如果找不到,则返回 -1

语法:

arr.find(callback[, thisArg])
arr.findIndex(callback[, thisArg])

参数(两者的参数相同):

  • callback

    在数组每一项上执行的函数,接收 3 个参数:

    1. element当前遍历到的元素。
    2. index可选当前遍历到的索引。
    3. array可选数组本身。
  • thisArg可选

    执行回调时用作this 的对象。

这两个方法都可以识别NaN,弥补了indexOf的不足.

 // find
let a = [1, 4, -5, 10].find((n) => n < 0); // 返回元素-5
let b = [1, 4, -5, 10,NaN].find((n) => Object.is(NaN, n));  // 返回元素NaN
// findIndex
let a = [1, 4, -5, 10].findIndex((n) => n < 0); // 返回索引2
let b = [1, 4, -5, 10,NaN].findIndex((n) => Object.is(NaN, n));  // 返回索引4

浏览器兼容(MDN):Chrome 45,Firefox 25,Opera 32, Safari 8, Edge yes,

ES6 keys()&values()&entries() 遍历键名、遍历键值、遍历键名+键值

定义:

**keys()**方法返回一个数组迭代器对象,该迭代器会包含所有数组元素的

**values()**方法返回一个数组迭代器对象,该迭代器会包含所有数组元素的

**entries()**方法返回一个数组迭代器对象,该迭代器会包含所有数组元素的 键值对

语法:

arr.keys()
arr.values()
arr.entries()

参数: 无

for (let index of ['a', 'b'].keys()) {
    console.log(index);
}
// 0
// 1

for (let elem of ['a', 'b'].values()) {
    console.log(elem);
}
// 'a'
// 'b'

for (let [index, elem] of ['a', 'b'].entries()) {
    console.log(index, elem);
}
// 0 "a"
// 1 "b"

for..of中如果遍历中途要退出,可以使用break退出循环。

如果不使用for...of循环,可以手动调用遍历器对象的next方法,进行遍历:

let letter = ['a', 'b', 'c'];
let entries = letter.entries();
console.log(entries.next().value); // [0, 'a']
console.log(entries.next().value); // [1, 'b']
console.log(entries.next().value); // [2, 'c']

entries()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 7.1

keys()浏览器兼容性(MDN):Chrome 38, Firefox 28,Opera 25,Safari 8,

注意:目前只有Safari 9支持,,其他浏览器未实现,babel转码器也还未实现

结语


断断续续终于写完了,上班偷偷写了一些,星期天写了一些,虽说很幸苦,但是对数组的操作方法,整体清晰了很多,不像是之前的片面的运用一些常用API,其他的只是知道,很少使用掌握。现在对各个API 理解的更深入了一些,收获颇多。写的如有不正确的地方,欢迎各位大佬指正鞭策!也希望大家看完能有所收获,喜欢的话,赶紧 关注/点赞 哦!!!