JS数组常用方法总结
数组是一种类列表对象,它的原型中提供了遍历和修改元素的相关操作。JavaScript 数组的长度和元素类型都是非固定的。因为数组的长度可随时改变,并且其数据在内存中也可以不连续,所以 JavaScript 数组不一定是密集型的,这取决于它的使用方式。一般来说,数组的这些特性会给使用带来方便,但如果这些特性不适用于你的特定使用场景的话,可以考虑使用类型数组 TypedArray
。
只能用整数作为数组元素的索引,而不能用字符串。后者称为关联数组(即对象,因为每个属性都有一个用于访问它的字符串值) 。使用非整数并通过方括号或点号来访问或设置数组元素时,所操作的并不是数组列表中的元素,而是数组对象的属性集合上的变量。数组对象的属性和数组元素列表是分开存储的,并且数组的遍历和修改操作也不能作用于这些命名属性。
var myCar = new Object();// var myCar = {};
myCar.make = "Ford";
myCar["model"] = "Mustang";//这种方式能够使用特殊的属性名
静态方法
Array.from()
从一个类数组或可迭代对象创建一个新的、浅拷贝的Array实例。
// 从 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]
浅拷贝
let arr = [1, 2, { val: 4 }, () => { }];
arr.bar=arr;
let newArr3 = Array.from(arr);//浅拷贝
newArr3[0] = 2000;
newArr3[2].val = 2000;
console.log(arr);// [1, 2, {val: 2000}, ƒ, bar: Array(4)]
console.log(newArr3);// [2000, 2, {val: 2000}, ƒ]
Array.isArray()
用来判断某个变量是否是一个数组对象
如果不存在Array.isArray()
呢?可以借助Object.prototype.toString.call()
进行判断,此方式兼容性最好
if (!Array.isArray) {
Array.isArray = function(o) {
return typeof(o) === 'object'
&& Object.prototype.toString.call(o) === '[object Array]';
}
}
[2].toString()
调用的是数组的toString()
方法,而不是对象的toString()
方法。Array改写了Object的toString方法。
toString.call()
实际上就是Object.prototype.toString.call()
调用该方法,统一返回格式“[object Xxx]”
的字符串。
[2].toString() // '2'
toString.call([2]) // '[object Array]'
instanceof
判断
// 如果为true,则arr为数组
arr instanceof Array
instanceof
判断数组类型如此之简单,为何不推荐使用?
当检测Array实例时, Array.isArray
优于 instanceof
,因为Array.isArray
能检测iframes
。
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
xArray = window.frames[window.frames.length-1].Array;
var arr = new xArray(1,2,3); // [1,2,3]
// Correctly checking for Array
Array.isArray(arr); // true
// Considered harmful, because doesn't work though iframes
arr instanceof Array; // false
Array.of()
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]
实例属性
Array.prototype.length
数组中的元素个数
实例方法
改变原数组的方法(9个):
Array.prototype.push()
push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度length。
Array.prototype.pop()
pop()
方法从数组中删除最后一个元素,并返回该元素的值。此方法更改数组的长度。
Array.prototype.unshift()
unshift()
方法将一个或多个元素添加到数组的开头,并返回该数组的新长度length。
Array.prototype.shift()
shift()
方法从数组中删除第一个元素,并返回该元素的值。此方法更改数组的长度。
补充
push/pop()
方法具有通用性。该方法和 call()
或 apply()
一起使用时,可应用在类似数组的对象上。push
方法根据 length
属性来决定从哪里开始插入给定的值。如果 length
不能被转成一个数值,则插入的元素索引为 0,包括 length
不存在时。当 length
不存在时,将会创建它。
唯一的原生类数组(array-like)对象是 Strings
,尽管如此,它们并不适用该方法,因为字符串是不可改变的。
shift/unshift
方法并不局限于数组:这个方法能够通过 call()
或 apply()
方法作用于类似数组的对象上。但是对于没有 length 属性(从0开始的一系列连续的数字属性的最后一个)的对象,调用该方法可能没有任何意义。
Array.prototype.splice()
splice()
方法通过删除或替换现有元素或者原地添加新的元素来修改数组,并以数组形式返回被修改的内容。此方法会改变原数组。
返回值:由被删除的元素组成的一个数组。如果只删除了一个元素,则返回只包含一个元素的数组。如果没有删除元素,则返回空数组。
参数:
- 第一个,表示开始位置的索引
- 第二个,表示删除的数量
- 第三个及以后,可以传递一些新的元素,这些元素将会自动插入到开始位置索引前边
Array.prototype.sort()
sort()
方法用原地算法对数组的元素进行排序,并返回数组。
默认排序顺序是在将元素转换为字符串,然后比较它们的UTF-16代码单元值序列时构建的。
由于它取决于具体实现,因此无法保证排序的时间和空间复杂性。
我们可以自己来指定排序的规则:
在sort()添加一个回调函数,来指定排序规则,回调函数中需要定义两个形参。
arr.sort((a,b)=>{
//升序排列 前面比后面大时交换位置
//return a - b;
//降序排列
return b - a;
});
Array.prototype.reverse()
reverse()
方法将数组中元素的位置颠倒,并返回该数组。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。该方法会改变原数组。
Array.prototype.fill()
fill()
方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。返回修改后的数组。
参数:
- 第一个元素(必须): 要填充数组的值
- 第二个元素(可选): 填充的开始位置,默认值为0
- 第三个元素(可选):填充的结束位置,默认是为
this.length
Array.prototype.copyWithin()
copyWithin()
方法浅复制数组的一部分到同一数组中的另一个位置,并返回它,不会改变原数组的长度。
参数:
三个参数都是数值,如果不是,会自动转为数值.
- 第一个(必需):从该位置开始替换数据。如果为负值,表示倒数。
- 第二个(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。
- 第三个(可选):到该位置前停止读取数据,默认等于数组长度。使用负数可从数组结尾处规定位置。
[1, 2, 3, 4, 5].copyWithin(-2)
// [1, 2, 3, 1, 2]
[1, 2, 3, 4, 5].copyWithin(0, 3)
// [4, 5, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// [4, 2, 3, 4, 5]
[1, 2, 3, 4, 5].copyWithin(-2, -3, -1)
// [1, 2, 3, 3, 4]
不改变原数组的方法(8个):
Array.prototype.slice()
slice()
方法返回一个新的数组对象,这一对象是一个由 begin
和 end
决定的原数组的浅拷贝(包括 begin
,不包括end
)。原始数组不会被改变。
Array.prototype.join()
join()
方法将一个数组(或一个类数组对象)的所有元素连接成一个字符串并返回这个字符串。如果数组只有一个项目,那么将返回该项目而不使用分隔符。
参数 separator
可选
指定一个字符串来分隔数组的每个元素。如果需要,将分隔符转换为字符串。如果缺省该值,数组元素用逗号(,
)分隔。如果separator
是空字符串(""
),则所有元素之间都没有任何字符。
Array.prototype.concat()
concat()
方法用于合并两个或多个数组。此方法不会更改现有数组,而是返回一个新数组。
参数 valueN
可选
数组和/或值,将被合并到一个新的数组中。如果省略了所有 valueN
参数,则 concat
会返回调用此方法的现存数组的一个浅拷贝。
Array.prototype.toString()
toString()
返回一个字符串,表示指定的数组及其元素。
Array
对象覆盖了Object
的 toString
方法。对于数组对象,toString
方法连接数组并返回一个字符串,其中包含用逗号分隔的每个数组元素。
当一个数组被作为文本值或者进行字符串连接操作时,将会自动调用其 toString
方法。
该方法的效果和join
方法一样,都是用于数组转字符串的,但是与join
方法相比没有优势,也不能自定义字符串的分隔符,因此不推荐使用。
Array.prototype.toLocaleString()
toLocaleString()
返回一个字符串表示数组中的元素。数组中的元素将使用各自的 toLocaleString
方法转成字符串,这些字符串将使用一个特定语言环境的字符串(例如一个逗号 ",")隔开。
Array.prototype.indexOf()
indexOf()
方法返回在数组中可以找到一个给定元素的第一个索引,如果不存在,则返回-1
。
参数:
- 第一个(必须):被查找的元素
- 第二个(可选):开始查找的位置(不能大于等于数组的长度,返回-1),接受负值,默认值为0。
严格相等的搜索:
数组的indexOf搜索跟字符串的indexOf不一样,数组的indexOf使用严格相等===
搜索元素,即数组元素要完全匹配才能搜索成功。
注意:indexOf()不能识别NaN
数组去重
const unique = arr =>{
const uniqueArr = [];
for(let i=0;i<arr.length;i++){
if(uniqueArr.indexOf(arr[i])=== -1){
//indexof返回-1表示在新数组中不存在该元素
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
}
Array.prototype.includes()
includes()
方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true
,否则返回 false
。
参数:
- 第一个(必须):被查找的元素
- 第二个(可选):默认值为0,参数表示搜索的起始位置,接受负值。正值超过数组长度,数组不会被搜索,返回false。负值绝对值超过长数组度,重置从0开始搜索。
includes方法是为了弥补indexOf方法的缺陷而出现的:
indexOf
方法不能识别NaN
indexOf
方法检查是否包含某个值不够语义化,需要判断是否不等于-1
,表达不够直观
数组去重
const unique = arr =>{
const uniqueArr = [];
for(let i=0;i<arr.length;i++){
if (!uniqueArr.includes(arr[i])) {
//includes 检测数组是否有某个值
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
}
Array.prototype.lastIndexOf()
lastIndexOf()
方法返回指定元素(也即有效的 JavaScript 值或变量)在数组中的最后一个的索引,如果不存在则返回 -1。从数组的后面向前查找,从 fromIndex
处开始。
Array.prototype.at()
实验中
遍历方法(12个):
不会改变原始数组
Array.prototype.forEach()
forEach()
方法对数组的每个元素执行一次给定的函数。
语法: array.forEach(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
关于forEach()你要知道:
- 无法中途退出循环,只能用
return
退出本次回调,进行下一次回调。 - 它总是返回
undefined
值,即使你return了一个值。
Array.prototype.map()
创建一个新数组,其结果是该数组中的每个元素都调用一个提供的函数后返回的结果。
语法:
let new_array = arr.map(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
let a = ['1','2','3','4'];
let result = a.map(function (value, index, array) {
return value + '新数组的新元素'
});
console.log(result, a);
// ["1新数组的新元素","2新数组的新元素","3新数组的新元素","4新数组的新元素"] ["1","2","3","4"]
Array.prototype.filter()
返回一个新数组, 其包含通过所提供函数实现的测试的所有元素。
语法:
let new_array = arr.filter(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
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]
Array.prototype.reduce()
reduce() 方法对累加器和数组中的每个元素(从左到右)应用一个函数,最终合并为一个值。
语法:
array.reduce(function(total, currentValue, currentIndex, arr), initialValue)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- total(必须),初始值, 或者上一次调用回调返回的值
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
initialValue(可选): 指定第一次回调的第一个参数。
回调第一次执行时:
- 如果
initialValue
在调用 reduce 时被提供,那么第一个total
将等于initialValue
,此时currentValue
等于数组中的第一个值; - 如果
initialValue
未被提供,那么total
等于数组中的第一个值,currentValue
等于数组中的第二个值。此时如果数组为空,那么将抛出 TypeError。 - 如果数组仅有一个元素,并且没有提供 initialValue,或提供了 initialValue 但数组为空,那么回调不会被执行,数组的唯一值将被返回。
// 数组求和
let sum = [0, 1, 2, 3].reduce(function (a, b) {
return a + b;
}, 0);
// 6
// 将二维数组转化为一维 将数组元素展开
let flattened = [[0, 1], [2, 3], [4, 5]].reduce(
(a, b) => a.concat(b),
[]
);
// [0, 1, 2, 3, 4, 5]
Array.prototype.reduceRight()
这个方法除了与reduce执行方向相反外,其他完全与其一致,请参考上述 reduce 方法介绍。
Array.prototype.every()
用于检测数组所有元素是否都符合函数定义的条件
语法:
array.every(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
方法返回值规则:
- 如果数组中检测到有一个元素不满足,则整个表达式返回 false,且剩余的元素不会再进行检测。
- 如果所有元素都满足条件,则返回 true。
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
Array.prototype.some()
数组中是否有满足判断条件的元素
语法:
array.some(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
方法返回值规则:
-
如果有一个元素满足条件,则表达式返回true, 剩余的元素不会再执行检测。
-
如果没有满足条件的元素,则返回false。
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
Array.prototype.find() & Array.prototype.findIndex()
find()
:用于找出第一个符合条件的数组成员,并返回该成员,如果没有符合条件的成员,则返回undefined
。
findIndex()
:返回第一个符合条件的数组成员的位置,如果所有成员都不符合条件,则返回-1
。
语法:
let new_array = arr.find(function(currentValue, index, arr), thisArg)
let new_array = arr.findIndex(function(currentValue, index, arr), thisArg)
参数:
function(必须): 数组中每个元素需要调用的函数。
回调函数的参数
- currentValue(必须),数组当前元素的值
- index(可选), 当前元素的索引值
- arr(可选),数组对象本身
thisArg(可选): 当执行回调函数时this绑定对象的值,默认值为undefined
这两个方法都可以识别NaN
,弥补了indexOf
的不足.
// Object.is() 方法判断两个值是否为同一个值。
// 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
Array.prototype.keys()/values()/entries()
三个方法都返回一个新的 Array Iterator 对象,对象根据方法不同包含不同的值。
遍历键名、遍历键值、遍历键名+键值
语法:
array.keys()
array.values()
array.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']