JavaScript -- Array进阶详解
如没有特别说明,测试宿主环境为Node.js
文章目录
前言
除了Object
之外,Array
应该ECMAScript中最常用的类型了,但和其他语言不一样,ECMAScript中的数组长度是可变的,而且由于ECMAScript的弱类型,这也使得数组Array中存取的类型可以是混杂的,例如数组的第一个元素可能是number
类型,第二个可能是string
类型,第三个可能又是Object
,但通常我们不会这么做,更多地是使用统一的规范
一、逗号创建数组
我们来看两种有意思几种的逗号
数组创建方式:
-
第一种
var arr = [,,,]; //创建包含三个空位的数组 console.log(arr.length); // => 3 console.log(arr); // => [ <3 empty items> ]
-
第二种
var arr = [2,,,6]; //创建包含四个空位的数组 console.log(arr.length); // => 4 console.log(arr); // => [ 2, <2 empty items>, 6 ]
-
第三种
var arr = [2,,,]; //创建包含三个空位的数组 console.log(arr.length); // => 3 console.log(arr); // => [ 2, <2 empty items> ]
-
第四种
var arr = [,,1,]; //创建包含四个空位的数组 console.log(arr.length); // => 3 console.log(arr); // => [ <2 empty items>, 1 ]
-
第五种
var arr = [,,,3]; //创建包含四个空位的数组 console.log(arr.length); // => 4 console.log(arr); // => [ <3 empty items>, 3 ]
不知道你所预期的结果和实际输出的有没有区别,得到这样结果的原因是从ES5规范开始就允许在列表(数组值,属性列表)末尾多加一个逗号(在实际处理中会被忽略不计)
那如何简便判断数组的length呢?
如果你读懂了上文的解释,你或许猜到了,如果数组最后一个是
,
控制台输出会忽略这个,
然后正常的思维计算数组长度,现在利用这个规律返回看看上面的五个示例,得到的结果是不是这样的呢?
数组创建的静态方法:
关于数组创建,这里我引入Array.from()
和Array.of()
两个方法
- Array.from()
//字符串会被拆分为单字符数组 console.log(Array.from("hello")); // => ["h","e","l","l","o"]; //对现有数组进行复制 const arr1 = [1,2,3,4,5]; const console.log(Array.from(arr1)); // => [1,2,3,4,5] console.log(arr1 === arr2); // => flase //还可接受第二个可选的映射函数参数,这个函数可以直接增强新数组的值,类似于调用函数map() const arr3 = [2,3,4,5]; console.log(Array.from(arr3,x => x**2)); // =>[4,9,14,25] 注:** 相当于执行了Math.pow()函数
- Array.of()
这个函数主要用在把一组参数转换为数组//以前我们通常会用如下函数返回一系列参数组成的数组 function fn() { console.log(Array.prototype.slice.call(arguments)); } fn(1, 2, 3, 4); // => [1,2,3,4] //但现在我们有了更简便的方法 console.log(Array.of(1,2,3,4)); // => [1,2,3,4]
二、容易忽略的.length
JavaScript的数组长度length具有一些其他语言没有的特性
- 数组长度可变,可以更改length的值来修改数组长度
let arr = ["a","b","c","d"]; arr.length = 5; console.log(arr); // => ["a","b","c","d",undefined,underfined]; 注:未定义的单元用undefined填充 arr.lengeh = 2; console.log(arr); // => ["a","b"]; 注:缩小数组长度会覆盖以前的以定义的单元
- 插入问题,设置某位置的值,有可能会改变数组长度,哪怕设置为
undefined
let arr = [2,4,6,8]; console.log(arr); // => [ 2, 4, 6, 8 ] console.log(arr.length); // => 4 arr[80] = 100; console.log(arr); // => [ 2, 4, 6, 8, <76 empty items>, 100 ] console.log(arr.length); // => 81 arr[99] = undefined; console.log(arr); // => [ 2, 4, 6, 8, <76 empty items>, 100, <18 empty items>, undefined ] console.log(arr.length); // => 100
三、检测数组的两种方法
说到检测数组,不管是在Java中还是JavaScript中,我们都不由想到instanceof
操作符
这是正确的,你可以使用如下方法检测数组
const arr = [12,2,3,4];
console.log(arr instanceof Array); // => true
我希望你也能知道还有一种少见但重要的检测方法,试想我们有两个框架在同一个网页中,涉及两个不同的全局执行上下文,就可能有两个不同版本的Array构造函数,如果把一个用第一个框架的声明的数组传入另一个框架,在用instanceof Array检测是否是数组,显然此时会返回false
因为 instanceof Array操作代表的并不是像我们理解的那样判断一个对象是不是数组,而是告诉我们该对象是否是当前全局作用域下的Array的实例
为了解决这个问题,ECMAScript提供了Array.isArray()
方法,这个方法的目的就是确定一个值是否为数组,如下:
let arr = ['a','b','c','d'];
if (Array.isArray(arr)) {
console.log('yes');
}
四、迭代器方法(返回迭代器)
我们知道,对象
Object
有三个静态方法Object.keys()、Object.values()、Object.entries()
同样的,ECMAScript也在Array
的对象原型上为我们添加了类似方法来实现数组的迭代
- Array.prototype.keys()
这个方法返回数组 索引 的迭代器let arr = [3,5,7,9,11]; console.log(Array.from(arr.keys())); // => [ 0, 1, 2, 3, 4 ]
- Array.prototype.values()
这个方法返回数组 值 的迭代器let arr = [3,5,7,9,11]; console.log(Array.from(arr.values())); // => [ 3, 5, 7, 9, 11 ]
- Array.prototype.entries()
这个方法返回数组 索引/值 对的迭代器let arr = [3,5,7,9,11]; console.log(Array.from(arr.entries())); // => [ [ 0, 3 ], [ 1, 5 ], [ 2, 7 ], [ 3, 9 ], [ 4, 11 ] ]
对,如你所想,Array.from()方法还可以接收迭代器,转为数组
五、迭代方法(返回boolean值或者Array对象)
你可能意识到了,上面的三种迭代器方法方法只能帮我们输出数组的内容,并不能做一些判断和操作,那么接下来的五个迭代方法将会对你操作数组提供更便捷的方式,传入的第一个参数为 断言函数,在某些方法中通常简写为匿名函数,如下就是采用匿名函数的形式,断言函数的三个参数分别为 值 、索引、该数组本身
- every():
对数组的每一项都运行传入的参数,如果对每一项函数都返回
true
,则这个方法返回true
像every翻译过来一样(全部),我们可以用这个方法来判断某个数组的所有值是否都满足某个条件,例如
let arr = [24,56,87,33,45];
console.log(arr.every((item, index, array) => item > 10)); //=> true 注:5个值都大于10,返回true
- some():
对数组的每一项都运行传入的参数, 如果有一项函数返回
true
,则这个方法返回true
像some翻译过来一样(某些),我们可以用这个方法来判断某个数组的某个值都满足某个条件
let arr = [24,56,87,33,45];
console.log(arr.some((item, index, array) => item > 80)); //=> true 注:87大于80,返回true
- filter():
对数组的每一项都运行传入的参数,函数返回
true
的项将会组成数组后返回
像filter翻译过来一样(过滤器),我们只挑选符合我们需求的值组成数组并返回
let arr = [24,56,87,33,45];
console.log(arr.filter((item, index, array) => item > 35)); //=> [56, 87, 45] 注:返回大于35的值组成的数组
- forEach():
对数组的每一项都运行传入的参数,类似于
for
循环,只是forEach
会将每一项函数返回值组成数组返回
let arr = [24, 56, 87, 33, 45];
console.log(arr.filter((item, index, array) => arr[index])); //=>[ 24, 56, 87, 33, 45 ]
//等价于
let arr_2 = [];
for (let i = 0; i < arr.length; i++) {
arr_2.push(arr[i]));
}
console.log(arr_2); //=>[ 24, 56, 87, 33, 45 ]
- map():
对数组的每一项都运行传入的参数,将每一项函数
return
返回的值组成数组后返回
可用此方法对数组的值进行操作改变后返回
let arr = [24, 56, 87, 33, 45];
console.log(arr.map(item, index, array) => item * 2); //=>[ 48, 112, 174, 66, 90 ]
尽管有了如上的迭代器方法,可以很容易对数组进行我们想要的的操作,但我们还是感到对数组某些方面没有足够边便捷的操作,例如如何快速在头部和尾部添加值或者删减值(你可能注意到上文用了一次push函数),又或者如何对数组进行快速整齐地赋值,接下来两节着重围绕此进行说明
六、复制和填充方法
ES6新增了两个批量复制和填充数组的方法:
-
Array.prototype.fill(value[, start[, end]])
//一个参数时,默认value填充数组所有元素 let arr = [1,2,3,4,5,6]; arr.fill(5); console.log(arr); // =>[5,5,5,5,5,5]; 注:全被覆盖- //两个参数时,使用value填充大于等于start的元素 let arr = [4,8,6,5,3]; arr.fill(2,5); console.log(arr); // =>[4,2,2,2,3]; 注:8,6,5都大于等于5,被覆盖 //三个参数时,使用value填充大于等于start,小于等于end的元素 let arr = [4,8,6,5,3]; arr.fill(2,5,6); console.log(arr); // =>[4,8,2,2,3]; 注:6,5都大于等于5,小于等于6,被覆盖
-
Array.prototype.copyWithin(target[, start[, end]])
//一个参数时,复制索引0的开始的内容,插值到索引start开始的地方 let arr = [1,2,3,4,5,6]; arr.copyWithin(2); console.log(arr); // =>[ 1, 2, 1, 2, 3, 4 ]; //注:复制索引0的开始的内容,插值到索引为2开始的地方 //两个参数时,复制索引start的开始的内容,插值到索引target开始的内容 let arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(2, 3); console.log(arr); // =>[ 1, 2, 4, 5, 6, 6 ]; // 注:复制4,5,6,插值到下标为2开始的位置 //三个参数时,复制索大于等于start,小于end的内容,插值到索引target开始的地方 let arr = [1, 2, 3, 4, 5, 6]; arr.copyWithin(2, 3, 5); console.log(arr); // =>[ 1, 2, 4, 5, 5, 6 ]; 注:复制4,5,插入到索引值为2的地方
copyWithin()和fill()的参数满足以下条件
- 负值索引从数组末尾开始计算
- 静默忽略超出数组边界,0长度及方向反向的索引范围
- 索引值有部分可用,则用该可用的部分
七、栈与队列方法
- Array.protype.push()
//从数组尾部添加元素,返回新数组长度 let arr = [1,2,3,4]; console.log(arr.push(5,6)); // => 6 console.log(arr); // => [1,2,3,4,5,6];
- Array.protype.pop()
//从数组尾部删除一个元素,返回该删除的元素 let arr = ["a","b","c","d"]; console.log(arr.pop()); // => "d" console.log(arr); // => ["a","b","c"];
- Array.protype.unshift()
//从数组头部添加元素,返回新数组长度 let arr = [1,2,3,4]; console.log(arr.unshift(7,8,9)); // => 7 console.log(arr); // => [7,8,9,1,2,3];
- Array.protype.shift()
//从数组头部删除一个元素,返回该删除的元素 let arr = [1,2,3,4]; console.log(arr.shift()); // => 1 console.log(arr); // => [2,3,4];
到现在应该算了解了操作数组的大部分方法,我们已经能对数组内部元素做许多操作了,接下来更深入了解数组本身的一些基本操作,如数组翻转,排序和数组之间的连接
八、翻转与排序
…
本文地址:https://blog.csdn.net/JKR10000/article/details/109158515