前言
在前端开发中,跟数组日常打交道,所以研究 Array 和 Array.prototype 的属性和方法对于做前端开发的我们非常有必要:
我们可以借助Object.getOwnPropertyNames()方法来看看Array和Array.prototype都有哪些属性和方法:

其中是 ES5 新增的有:
- reduce、filter、sort、forEach、map、some、every、indexOf
其中是 ES6 新增的有:
- Array.from()、Array.of()
- find、findIndex、fill、includes、entries、keys、values、flat、flatMap
讲解下面的这些方法的时候我使用 4 个模块去讲解:
- 基本使用(基础操作)
- 注意事项(bug 预防)
- API 实现(考虑性能)
- 应用拓展(API 拓展)
在此之前我们先了解一下创建数组的方式和其实例属性:
创建数组的方法
创建数组据我了解有下面四种方式:
1. 直接使用字面量[]创建
let arr = [0, 1, 2];
2. 使用 Array 构造函数创建
let arr = new Array(0, 1, 2);
3. 调用 Array 内置方法创建
let arr = Array(0, 1, 2);
4. 调用 Array of 内置方法创建
let arr = Array of(0, 1, 2);
复制代码
第 3 种方式创建与其它方式的区别在于:
let arr = [7] // [7]
Array.of(7); // [7]
Array(7); // [ , , , , , , ]
复制代码
实例属性
constructor
指向构造出该数组的构造函数
length
length 最大的妙用,就是直接改变 length 属性可以直接删除元素或增加元素
let arr = [0, 1, 2];
arr.length = 2; // [0, 1]
arr.length = 5; // [0, 1, empty × 3]
复制代码
直接清空数组,还可实现复用,不会产生内存垃圾
let arr = [0, 1, 2];
arr.length = 0; // []
复制代码
值得注意的是:在 ECMAScript6 的文档中,明确规定了 empty 就是等于 undefined ,在任何情况下都应该这样对待 empty
Array 的方法
Array.from( obj ):转化为数组
基本使用
将对象(广义上的)转化为数组(包括:字符串、数组、Set、Map、函数里的Arguments、Generator 函数的Generator对象)
Array.from("foo") // ["f","o","o"]
Array.from([1,2,3]) // [1,2,3] 注意:第一层是深拷贝,第二层及以上是浅拷贝(即数组元素为数组/对象)
复制代码
const set = new Set(['foo', 'bar', 'baz']);
Array.from(set); // ["foo", "bar", "baz"]
复制代码
const map = new Map([['1', 'a'], ['2', 'b']]);
Array.from(map); // [['1', 'a'], ['2', 'b']]
Array.from(map.keys()); // ['1' , '2']
Array.from(map.values()); // ['a', 'b'];
复制代码
function f() {
console.log(arguments); // Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(Array.from(arguments)); // [1,2,3]
}
f(1, 2, 3);
复制代码
function* fn(){
yield 1
yield 2
yield 3
}
var f = fn()
console.log(f) // fn {<suspended>}
Array.from(f) // [1,2,3]
复制代码
应用拓展
将数组转换为 Set 再转回数组可以起到快速 去重 的效果
const set = new Set(['foo', 'bar', 'bar', 'foo']);
Array.from(set); // ["foo", "bar"]
复制代码
Array.of( obj ):创建数组实例
基本使用
功能跟 Array 差不多:
Array(1, 2, 3); // [1, 2, 3]
Array.of(1, 2, 3); // [1, 2, 3]
复制代码
注意事项
跟 Array 的区别在于,传入参数为数字时:
Array(7); // [ , , , , , , ],length 为 7 的一个空位数组
Array.of(7); // [7] // 值为 7 的单元素数组
复制代码
Array.isArray( obj ):判断是否为数组
基本使用
功能:作用:用于判断某个变量是否是数组对象,是则返回 true,否则返回 false
Array.prototype 的方法
arr.push(v)、arr.pop()
基本使用
var arr = [1,2,3]
arr.push(5) // 4
console.log(arr) // [1,2,3,5]
arr.pop() // 3
console.log(arr) // [1,2,3]
复制代码
注意事项
- 会改变原数组
- 返回值是改变后新数组的长度
应用拓展
验证括号是否有效匹配
arr.unshift(v)、arr.shift()
基本使用
var arr = [1,2,3]
arr.unshift(5) // 4
console.log(arr) // [5,1,2,3]
arr.shift() // 3
console.log(arr) // [1,2,3]
复制代码
注意事项
- 会改变原数组
- 返回值是改变后新数组的长度
应用拓展
结合 arr.push(v)、arr.pop() 可以把数组作为队列处理,可用于如 树的层序遍历:
function fn(root){
var arr = [] // 二元数组放置每一层遍历结果
if(root == null) return arr
var queue = [] // 维护一个队列
queue.push(root)
while(queue.length != 0){ // 队空表示当前队内的元素都是叶子节点上的元素,退出循环
arr.psuh([])
for(let i = 0;i < queue.length;i++){ // 当前队内元素按序出队
var node = queue.shift()
arr[arr.length - 1].push(node.val)
if(node.left != null) queue.push(node.left) // 元素出队的同时将其子节点元素入队
if(node.right != null) queue.push(node.right)
}
}
}
复制代码
arr.includes(v , [fromIndex])
基本使用
- 参数 v:表示查找的目标元素;
- 参数 fromIndex:可选,表示从从fromIndex 索引处开始查找;若该值为 负数,则按升序从
array.length + fromIndex的索引开始搜(表示从倒数第 n 个开始搜的意思)
功能:判断数组中是否含有该元素,有就返回 true,否则返回 false
const arr = [1, 2, 3];
arr.includes(1); // true
arr.includes(22); // false
复制代码
注意事项
不能直接检索出引用类型,解决方案是利用其引用
const arr1 = [[1], 2, 3];
arr1.includes([1]); // false
const t = [1];
const arr2 = [t, 2, 3];
arr2.includes(t); // true
复制代码
原理实现
单纯 for 循环遍历,找到则退出循环,并返回 true,在循环结束后返回 false,时间复杂度为 O(n)
arr.indexOf(v , [fromIndex])、arr.lastIndexOf(v , [fromIndex])
基本使用
- 参数 v:表示查找的目标元素;
- 参数 fromIndex:可选,表示从从fromIndex 索引处开始查找;若该值为 负数,则按升序从
array.length + fromIndex的索引开始搜(表示从倒数第 n 个开始搜的意思)
功能:
- arr.indexOf(v , [fromIndex]):查找目标元素在数组中
第一次出现的位置,返回索引,若查找不到则返回-1; - arr.indexOf(v , [fromIndex]):查找目标元素在数组中
最后一次出现的位置,返回索引,若查找不到则返回-1;
const arr = [1, 2, 3, 6, 3, 2, 1];
arr.indexOf(1); // 0
arr.indexOf(22); // -1
arr.lastIndexOf(2); // 5
arr.lastIndexOf(10); // -1
复制代码
注意事项
不能直接检索出引用类型,解决方案是利用其引用
const arr1 = [[1], 2, 3];
arr1.indexOf([1]); // -1
arr1.lastIndexOf([1]); // -1
const t = [1];
const arr2 = [t, 2, 3, t, 1];
arr2.indexOf(t); // 0
arr2.lastIndexOf(t); // 3
复制代码
原理实现
单纯 for 循环遍历,找到则退出循环,并返回 i 值,在循环结束后返回 -1,时间复杂度为 O(n)
- arr.indexOf(v):for 循环里的 i 是从 0 到 n 的
- arr.lastIndexOf(v):for 循环里的 i 是从 n 到 0 的
arr = arr.concat(arr / v1,v2,…)
基本使用
- 参数 arr:可以直接填一个数组,前面的数组会接上括号里面的数组
- 参数 v1,v2,v3,…:也可以填多个值,接入数组时会一个一个按序接入,也可以不填
注意事项
不会改变原数组,如果需要改变原数组,记得赋值
var arr1 = [1,2,3]
var arr = [4]
arr.concat(arr1) // [4,1,2,3]
console.log(arr) // [4]
arr = arr.concat(arr1) // [4,1,2,3]
复制代码
arr.reverse()
基本使用
将数组倒序,会改变原数组,不用填参数,会改变原数组
var arr = [1,2,3]
arr.reverse() // [3,2,1]
复制代码
API 实现
利用 for 循环遍历数组,借助中间变量完成交换
var MyReverse = function(arr){
var t = 0
var len = arr.length
for(let i = 0;i < Math.floor(len / 2); i++){
t = arr[i]
arr[i] = arr[len - 1 - i]
arr[len - 1 - i] = t
}
return arr
}
var arr = [1,2,3,4]
MyReverse(arr)
复制代码
arr = str.split(“分割字符串”)
基本使用
功能:将一个 str 以指定的 分割字符串 为界限分割成多个子字符串,并返回这些子字符串组成的数组;
- 如果原字符串中不包含分割字符串,则返回原数组
- 如果分割字符串为空字符串,则将
str原字符串中每个字符的数组形式返回; - 如果没有传入参数,则返回原字符串的数组形式

注意事项
分割界限可以是字符串,也可以是单个字符,也可以是空字符串,也可以为空,但都不改变原字符串
API 实现
【未完成】
var MySplit = function(string,str){
var arr = []
if(string === "" || str === null) arr.push(string) // 无传入参数,返回原字符串
if(str === ""){
for(let i in string){
arr.push(string[i])
}
}
else{
for(let i in string){
var s = ""
if(string[i] === str) arr.push(s)
else s += string[i]
}
}
return arr
}
MySplit("hello,world!",",")
MySplit("hello,world!","")
MySplit("hello,world!","6")
MySplit("","6")
复制代码
测试:
str = arr.join(“连接字符串”)
基本使用
功能:将一个数组的每一个元素以指定的 连接字符串 为“桥梁”合并成一个字符串,并返回合并后的字符串
- 如果传入参数为空字符串
""的话,则把各个元素转化为字符串类型后直接拼接起来 - 如果不传入参数的话,则默认以逗号
,作为连接字符串 - 如果传入参数不是字符串类型,则会将其转化为字符串类型后再拼接
注意事项
其原理是把各个元素先调用toString()后再拼接起来的,所以需要注意一下(当然有些数据类型会覆盖掉这些方法)
let arr = ["h", 9, true, null, undefined, [], {}];
arr.join("|"); // "h|9|true||||[object Object]"
复制代码
其中 null、undefined 、[]对应都转化为 "",{}对应转化为[object Object]
arr = arr.slice(start,[end])
基本使用
功能:按照数组索引从start到end - 1截取数组,并返回截取后的数组,不改变原数组
- 参数 start:可选,不填的话默认值为
0,若为负数表示倒数第 |start| 个 - 参数 end:可选,不填的话默认值为
arr.length,若为负数表示倒数第 |end| 个
var arr = [1,2,3,4,5]
arr.slice() // [1,2,3,4,5]
arr.slice(1,3) // [2,3,4]
arr.slice(1,10) // [2,3,4,5]
复制代码
arr = arr.splice(start,[length],[item1,item2,…])
基本使用
功能:按照数组索引从start截取长度为length的数组,并在尾部添加元素[item1,item2,...],并返回截取后的数组,会改变原数组
- 参数 length:不填的话,该方法默认截取到数组索引从
start到字符串结束
由于 splice 方法的参数的可控性,所以 splice 方法其实有 4 种功能:
- 截取功能:
var arr = [0,1,2,3,4,5];
arr.splice(4); // [4,5]
arr.splice(1,3); // [1,2,3]
复制代码
- 从数组某个索引处插入一个或多个元素的功能:
第二个参数设置为 0,第一个参数为插入的第一个元素插入后所在的索引
var arr = [0, 1, 2, 3];
arr.splice(1, 0, "h"); // 从下标为1的地方插入元素 'h'
console.log(arr); // [0, "h", 1, 2, 3]
复制代码
- 截取 + 插入功能:
var arr = [0, 1, 2, 3];
arr.splice(1, 2, "h"); // 截取 [1,2] 后从下标为1的地方插入元素 'h'
console.log(arr); // [0, "h", 3]
复制代码
arr.sort( [compareFunction] )
注意事项
如果没有指明 compareFunction,则 sort 方法会按照各元素转换为的字符串的诸个字符的 Unicode 位点进行排序
- 例如 “Banana” 会被排列到 “cherry” 之前
- 例如 80 要比 9 要靠前,因为没有指明
compareFunction的情况下,元素会被转为字符串,所以 “80” 要比 “9” 考前
如果指明了 compareFunction ,那么数组会按照调用该函数的返回值排序。即 a 和 b 是两个将要被比较的元素:
- 如果 compareFunction(a, b) 小于 0 ,则 a 会被排列到 b 之前;
- 如果 compareFunction(a, b) 等于 0 ,则 a 和 b 的相对位置不变;
- 如果 compareFunction(a, b) 大于 0 ,则 b 会被排列到 a 之前;
- compareFunction(a, b) 必须总是对相同的输入返回相同的比较结果,否则排序的结果将是不确定的
所以,比较函数格式如下:
function compare(a, b) {
if (a < b ) { // 按某种排序标准进行比较, a 小于 b
return -1;
}
if (a > b ) {
return 1;
}
// a must be equal to b
return 0;
}
复制代码
要比较数字而非字符串,比较函数可以简单的以 a 减 b,如下的函数将会将数组升序排列
function compareNumbers(a, b) {
return a - b;
}
复制代码
原理
传入的参数 compareFunction 会被作为 sort 方法里面调用的一个子函数,无论是插入排序还是快速排序还是其他的排序方式,都逃不过对两个元素的大小进行比较,所以在 sort 方法里面在(比较两个元素大小关系)这一小步时会调用我们传入的自定义的子函数,然后拿取返回的值
返回的值 sort 里面默认需要收到三种结果(大于0的数、等于0的数、小于0的数),然后对这三种结果做出相应处理(要不要交换位置)
基本使用
功能:对数组进行排序
var numbers = [4, 2, 5, 1, 3];
numbers.sort(function(a, b) {
return a - b;
});
console.log(numbers);
也可以写成:
var numbers = [4, 2, 5, 1, 3];
numbers.sort((a, b) => a - b);
console.log(numbers);
// [1, 2, 3, 4, 5]
var items = [
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'And', value: 45 },
{ name: 'The', value: -12 },
{ name: 'Magnetic', value: 6},
{ name: 'Zeros', value: 37 }
];
// sort by value
var result = items.sort(function (a, b) {
return (a.value - b.value)
});
result === [
{ name: 'The', value: -12 },
{ name: 'Magnetic', value: 6},
{ name: 'Edward', value: 21 },
{ name: 'Sharpe', value: 37 },
{ name: 'Zeros', value: 37 },
{ name: 'And', value: 45 }
];
复制代码
排序原理
- 数组长度不超过 10,为插入排序
- 数组长度超过 10,为快速排序
API 实现
具体实现参考 数据结构及算法系列 — 排序算法
arr.forEach((item,index,arr)=>{})
基本使用
功能:相当于一个遍历器
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 forEach 方法的数组本身
const arr = [0, 1, 2, 3, 4, 5];
arr.forEach((item, index, arr) => {
if (item % 2 === 0) {
arr.push(1);
}
})
console.log(arr); // [0, 1, 2, 3, 4, 5, 1, 1, 1]
复制代码
arr.every((item,index,arr)=>{return …}) 是否全部通过
基本使用
功能:相当于一个检测器,在 return 后面写明测试通过规则(一个函数或表达式),然后测试数组内的所有元素是否都能通过该规则。都能通过才返回 true,要是有一个元素不通过测试,则退出遍历并返回 false
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 every 方法的数组本身
const arr = [0, 1, 2, 30, 4, 5];
const result = arr.every((item, index)=> {
console.log(item) // 0 1 2 30
return item < 10;
});
console.log(result); // false
复制代码
arr.some((item,index,arr)=>{return …}) 是否至少一个通过
基本使用
功能:相当于一个检测器,在 return 后面写明测试通过规则(一个函数或表达式),然后遍历数组元素,如果有一个通过测试,则退出遍历并返回 true,全部元素均不通过测试则返回 false
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 some 方法的数组本身
const arr = [0, 1, 2, 30, 4, 5];
const result = arr.some((item, index)=> {
console.log(item); // 0 1 2 30
return item > 10;
});
console.log(result); // true
复制代码
arr.find((item,index,arr)=>{return …}) 第一个通过测试的元素的值
基本使用
功能:在 return 后面写明筛选通过规则(一个函数或表达式),然后遍历数组所以元素,通过规则即返回该元素
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 some 方法的数组本身
const arr = [5, 4, 3, 6, 7, 8];
const result = arr.find((item, index)=> {
return item > 5 ;
});
console.log(result); // 6
复制代码
arr.findIndex((item,index,arr)=>{return …}) 第一个通过测试的元素的索引
基本使用
功能:在 return 后面写明筛选通过规则(一个函数或表达式),然后遍历数组所以元素,通过规则即返回该元素的索引
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 some 方法的数组本身
const arr = [5, 4, 3, 6, 7, 8];
const result = arr.findIndex((item, index)=> {
return item > 5 ;
});
console.log(result); // 3
复制代码
arr.filter((item,index,arr)=>{return …}) 筛选器
基本使用
功能:相当于一个筛选器,在 return 后面写明筛选通过规则(一个函数或表达式),然后遍历数组所有元素,通过规则即把该元素放入一个数组中,最后返回该数组
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 some 方法的数组本身
const arr = [0, 1, 2, 3, 4, 5];
const result = arr.filter((item, index)=> {
return item % 2 === 0;
});
console.log(result); // [0, 2, 4]
复制代码
API 实现
const arr = [0, 1, 2, 3, 4, 5];
const MyFilter = function(arr){
const result = []
for(let item of arr){
if(item % 2 === 0) result.push(item)
}
return result
}
MyFilter(arr)
复制代码
arr.map((item,index,arr)=>{return …}) 平行操作器
基本使用
功能:相当于一个平行操作器,在 return 后面写明平行操作规则(一个函数或表达式),然后遍历数组所有元素,对每个元素进行规定的操作后得到一个新元素,将其放入一个数组中,最后返回该数组
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 some 方法的数组本身
const arr = [0, 1, 2, 3, 4, 5];
const result = arr.map((item, index)=> {
return item * 2;
});
console.log(result); // [0, 2, 4, 6, 8, 10]
复制代码
API 实现
const arr = [0, 1, 2, 3, 4, 5];
const MyMap = function(arr){
const result = []
for(let item of arr){
result.push(item * 2)
}
return result
}
MyMap(arr)
复制代码
arr.reduce((accum,item,index,arr)=>{return …})
基本使用
功能:相当于一个累积器
- 参数 accumulation:当前累积成果(还未执行这一步操作)
- 参数 item:当前项
- 参数 index:当前项的索引
- 参数 arr:调用 reduce 方法的数组本身
var arr = [1, 2, 3, 4, 5, 6];
var result = arr.reduce((accum, item)=> {
return accum + item;
});
console.log(result); // 21
复制代码
拓展:计算机底层原理
数组和对象一样,属于引用类型,在内存中放置的区域是堆内存,所需要的空间由 length 指定,而其引用则是放在栈内存区域,栈内存区域放置的是固定不变的值,而对数组的引用从本质上来讲就是它的内存地址,就是直达堆内存中对应的数组的第一个元素的内存地址(这只是我的理解,如果有错误可以在评论中指出,我可以修改),所以是个固定值。
数组访问某一项、数组遍历的时间复杂度怎么解释?
我的理解是这样的:数组访问某一项时(比如:arr[5])和 遍历数组 时,计算机工作的原理流程就是:
- 从栈内存中拿取数组
arr的内存地址 - 直达堆内存中该数组第一个元素
arr[0]的内存地址 - 由于数组元素挨个并排,所以元素
arr[5]和arr[0]之间的内存地址存在一个固定的差值,经过计算后也可直达,所以时间复杂度为 O(1) - 如果是遍历数组,则每次只跨两两元素之间的内存地址差,所以时间复杂度为 O(n)
数组本质上也是个对象怎么理解?
这是一个数组
["dog", "pig", "cat"];
复制代码
它可以看作成这样一个对象:
{
"0": "dog",
"1": "pig",
"2": "cat",
"length": 3
}
复制代码
- 一个数组元素对应一个对象字段
- 数组下标(索引)就是对应字段的属性名
- 数组元素的值就是对应字段的属性值
- 多了一个属性
length,其值自然就是数组的长度























![[桜井宁宁]COS和泉纱雾超可爱写真福利集-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/4d3cf227a85d7e79f5d6b4efb6bde3e8.jpg)

![[桜井宁宁] 爆乳奶牛少女cos写真-一一网](https://www.proyy.com/skycj/data/images/2020-12-13/d40483e126fcf567894e89c65eaca655.jpg)