你真的了解js中的Array吗?拿来吧你!

206 阅读8分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

今天上午面试被问到数组API,我答出常用的几个像pop、push和shift这些,但是问到细节的地方,包括紧张导致我这些数组API的功能好些记混了ᓫ(°⌑°)ǃ

就很尬,我本来自以为我这些基础知识掌握得很好,当面试官笑眯眯地说“你确定你真的答对了吗”的时候我尬得能用脚趾抠出两室一厅来……(呜呜

于是我毅然决然地要把Array的知识点一个个都搞清楚!

所以这篇文章就诞生了,纯干货啊喂(◍˃̶ᗜ˂̶◍)✩都给我好好看好好记下来,有朝一日在面试官面前报仇雪恨(哼哼๑•̀ •́

创建数组的方法

使用Array构造函数

很简单,new一个Array实例出来就有了

let arr = new Array()

但是啊,我之前在这种很简单的场景下见过一道题,当时有点给我考蒙了,是这样的,它往数组里面传了一个数字,重新赋值后问输出什么

虽然实际上这道题想考的并不是数组的相关知识,但我还是在Array(5)那边卡了一下,我以为是创建了一个[5]这样的数组,很简单但容易错

const array = new Array(5).map((item) => {
    // console.log('item', item)
    return item = {
        name: '1'
    }
});
console.log('array', array)  //array [ <5 empty items> ]

new Array(5)其实就是创建了一个初始length为5的数组;如果里面传入的是其他类型的值,则只会创建一个只包含该特定值的数组,比如

let arr = new Array('dazzlingwen')  //['dazzlingwen']

数组字面量

数组字面量是在中括号中包含以逗号分隔的元素列表

let arr = [1,2,3];
let names = ['gray','summy'];

这里有两个值得注意的细节,一个是如下数组,里面实际上只有两个元素,但是数组末尾还有一个逗号,看似还有一个空元素,那么大家思考一下这样的数组输出的长度会是多少呢

let value = [1,2, ];
console.log('value',value.length) //value 2

还有一个注意点是,在使用数组字面量表示法创建数组不会调用Aray构造函数,这个意味着什么呢,我们知道构造函数的实例对象是能够访问原型链上的属性的,那么利用数组字面量创造的构造函数,就不存在原型链,只能访问自身的属性,存在比较大的局限性

数组空位

这里我们为什么要提及这个知识点呢,因为这里很容易在实际工作做项目的时候踩坑!!!

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

注意不要和上面的栗子弄混了!!!

const options = [, , , ,];
console.log(options.length); //4

简单来看,大家可以这样理解。只计算逗号前面的空格有几个,那就有几个空元素

而这些占着茅坑不拉屎的空元素本质其实是什么呢,ES6新增方法普遍将这些空位当成存在的元素,只不过值为undefined,给大家看个测试案例

const options = [1, , , ,5];
for (const option of options){
    console.log(option === undefined)
}
//false
//true
//true
//true
//false

话说其实空位写起来又臭又长的,咱就是说,能不用则不用好吧

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

数组索引

哈,可别小瞧这个数组索引了,多得是你没注意过的细节学问呢ꉂꉂ꒰•̤▿•̤*ૢ꒱

数组越界真的存在吗

如果把一个值设置给超过数组最大索引的索引,则数组长度会自动扩展到该索引值+1,如下栗子

let arr = [1, 2, 3];
arr[3] = 4;
console.log(arr); //[1, 2, 3, 4]

会自动增加长度诶,length在这里是个很神奇的玩意儿,数组中元素的数量保存在length属性中,这个属性始终返回0或大于0的值

数组length的独特之处在于,它不是只读的。通过修改length属性,可以从数组末尾删除或添加元素

但是但是,敲黑板哈,数组越界确实是真的存在的(不要被标题忽悠了hhh),数组越界指的是,你在定义了一个定长的数组,却访问数组长度之外的元素,那就会抛出异常。但并不代表对于一个数组,我们不可以通过修改length来增删数组,只是访问数组外的元素会报错

检测数组

检测数组你们可能第一个想到的就会是instanceof,不就是检测数据类型嘛

确实instanceof是检测数组的一个最基础的办法,但是红宝书中写道,使用instanceof的问题是假设只有一个全局执行上下文也就是只有单个网页的情况下。

但是如果网页里有多个框架,则可能涉及两个不同的全局执行上下文,因此就会有两个不同版本的Array构造函数。如果要把数组从一个框架传给另一个框架,则这个数组的构造函数将有别于第二个框架内本地创建的数组

这个问题用Array.isArray()方法来解决。这个方法的目的就是确定一个值是否为数组,而不用管它是哪个全局执行上下文中创建的

这里大家可能不太理解为什么一个网页里面会存在两种框架的情况,举一个最简单的栗子,你们见过iframe吗,就是往网页里面嵌入另一个网页,两个都是独立的窗口,独立的document

<!DOCTYPE html>
<html>
<body>

<p>单击该按钮以在框架中创建带有 baidu.com 的 IFRAME 元素。</p>

<button onclick="myFunction()">试一试</button>

<script>
function myFunction() {
  var x = document.createElement("IFRAME");
  x.setAttribute("src", "https://www.baidu.com/");
  document.body.appendChild(x);
}
</script>

</body>
</html>

这里放了一个w3school改版的栗子,呈现的效果是这样的 image.png

迭代器方法

所谓迭代器方法啊,听着高级,如果是前端刚入门的小白们可能看着不太友好,说白了能够通过不断遍历迭代进行的方法,迭代就是一种重复反馈过程的活动

Array的原型上暴露了三个用于检索数组内容的方法:keys()、values()、entries()

keys()

keys()返回数组索引的迭代器,注意一下用法哈,很多人知道但是实操的时候就会懵逼了

const arr = ['foo',123,'baz','qux'];
console.log(arr.keys()); //Object [Array Iterator] {}

是这么用的吗?不是!这样输出的是这玩意儿Object [Array Iterator] {}这是个迭代器

因为这些方法返回的都是一个迭代器,我们可以通过Array.from()方法直接转换为数组实例,下面这个才是正确食用方法

const arr = ['foo', 123, 'baz', 'qux'];
console.log(Array.from(arr.keys())); //[ 0, 1, 2, 3 ]

values()

values()返回数组元素的迭代器

const arr = ['foo', 123, 'baz', 'qux'];
console.log(Array.from(arr.values())); //[ 'foo', 123, 'baz', 'qux' ]

entries()

entries()返回索引/值对的迭代器

const arr = ['foo', 123, 'baz', 'qux'];
console.log(Array.from(arr.entries())); //[ [ 0, 'foo' ], [ 1, 123 ], [ 2, 'baz' ], [ 3, 'qux' ] ]

而我们如果想获取一对键值对的话,可以巧用ES6的解构方法+循环

const arr = ['foo', 123, 'baz', 'qux'];
for (let [index,item] of arr.entries()){
    console.log('index,item',index,item)
}
//index,item 0 foo
//index,item 1 123
//index,item 2 baz
//index,item 3 qux

栈方法

这里我们重点掌握的不是数据结构,而是数组的方法

我们将数组模拟成一个栈,如果数据结构基础比较好的小伙伴们应该能理解,在栈这个数据结构中,有出栈和入栈这两种经典操作

拿这个数组['foo', 123, 'baz', 'qux']来说

数组索引(index)数组元素(item)说明
0foo第一个元素
1123
2baz
3qux栈顶元素

栈的操作遵循一个原则后进先出,也就是机动的元素在栈顶

入栈为push(),出栈为pop(),相信大家对这两种方法都很眼熟了已经,我们就用代码来验证一下吧

let arr = ['foo', 123, 'baz', 'qux'];
arr.push('dazzlingwen');
let popElement = arr.pop();
console.log('popElement',popElement); //dazzlingwen

队列方法

队列的核心理念是先进先出

如果要进行删除操作,那就是先进去的元素先出来

推入的操作还是push(),但是删除的操作,也就是“先出”就是shift(),也就是删除数组的第一个元素,可以参考上面的表格

let arr = ['foo', 123, 'baz', 'qux'];
arr.shift(); //shift删除的是第一个元素 'foo'
console.log(arr); //[ 123, 'baz', 'qux' ]

那么如果要在数组的第一个位置推入新元素,该用什么样的方法呢?我当时面试的时候比较呆,没答上来( ˚ཫ˚ )

不就unshift()嘛!!!!已经滚回去补了一波英语了……

排序方法

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

顾名思义reverse()就是反向排序,而sort()会按照升序重新排列数组元素,那如果要用sort()来实现降序效果呢?

let arr1=[6,2,4]
function compare(val1,val2){
    if(val1<val2){
        return 1;
    }else if(val1>val2){
        return -1;
    }else{
        return 0;
    }
}
console.log(arr1.sort(compare)); //[ 6, 4, 2 ]

只需往sort内传入一个比较函数即可

感谢看到最后的小伙伴们,后期我还会专门出一篇关于Array的归并、搜索以及迭代方法等等,也是数组的常见API,如果你们喜欢我的文章,那就点个赞赞再走吧~̋(๑˃́ꇴ˂̀๑)

我是dazzlingwen,热爱前端的小小白,期待也和我同样热爱前端的小伙伴们一起进步、一起成长~