博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
奇妙JS代码系列(三)一道有趣的题(创建一个长度为x数组)
阅读量:6272 次
发布时间:2019-06-22

本文共 2940 字,大约阅读时间需要 9 分钟。

原题描述:

不使用loop循环,创建一个长度为100的数组,并且每个元素的值等于它的下标

这个问题的一些变种例如给一个长度length和value,返回长度为length值全为value的数组等。

1.误区

最容易掉入陷阱的:

var arr = new Array(100);arr = arr.map(function(item, index) {return index;});

如果你的答案是这个,恭喜你成功掉入出题人的坑了。

2.解释

坑在Array(100),可以看看MDN的规范,

这里写图片描述
new Array(100)返回的是一个什么都没有的“稀疏数组”,其实我觉得这个答案可以看做是浏览器显示处理的Bug。

在47版本Chrome下(或者早些的Chrome版本),new Array(10)会返回[undefined,undefined,undefined,.......(100)],但这个undefined又跟你直接声明的[undefined,undefined,undefined,.......(100)]不同(都是47版本的Chrome),测试:

这里写图片描述

这里写图片描述

看出来了把,这个就是以前浏览器的显示Bug,后来修复了也就是现在显示的empty,用来区别undefined,表明这个和undefined是不一样的。

上面提到的Array()创建的是稀疏数组(Sparse Array),在一些资料中是这样说的,但是在js里随便创建个数组都可以是「稀疏」的吧(因为实际上就是 Object ?)

a = new Array(2); a[1] = 1; a;  // (2) [undefined × 1, 1]
b = []; b[4] = 5; b[9] = 10;b;  // (10) [undefined × 4, 5, undefined × 4, 10]而 JS 对于这类数组,由于上述规范的定义,遍历的时候会跳过空的地方
a.map(e => 2*e)  //(2) [undefined × 1, 2]

JS 引擎对于稀松数组是用字典保存的,能省一些空间,但是在索引方面肯定是比 Full Array 吃亏了。所以 JavaScript 不把 new Array 设计成全用 undefined 填满,有点反直觉。

以上是一种解释,另一种不用稀疏数组来解释:

对于 new Array(2),它实际上返回的是一个长度为 2,里面什么都没有的数组。即对应empty字面表现的意义。

根据 ECMA-262 6th edition,即 ES6 规范 22.1.1.2 小节 Array (len), Array(len) 仅仅就是给新数组设了个 length 属性而已,其他什么也没做……而对于 JS 数组的索引,就算是数组越界了,也都是返回 undefined:

new Array(2)[999] === undefined // true

能影响到 Array.prototype.map() 的原因是因为,根据规范 22.1.3.15 小节 Array.prototype.map ( callbackfn [ , thisArg ] ),map 函数会先检查 HasProperty。要 map,首先这个元素/属性要存在。可是:

new Array(2).hasOwnProperty(0) // false[undefined, undefined].hasOwnProperty(0) // true

所以,传给 map 的 callback 其实根本就没执行,可以测试

new Array(2).map(e => console.log('done'))

会发现啥也没 log 出来。

这个“bug”在edge和Safari也发现过。

3.正确的解决

扯了那么多,那么这道题应该怎么做呢。

其实这道题的面试官当时是说不用loop的情况下的解决方案,用Array方法的话其实都是属于loop,目的是考察递归:

1. 递归+自执行函数
var arr = [];(function dfs(i) {  if (i < 100) {    arr.push(i);    dfs(++i);  }}(0));
2. 转化成字符串
Array(100).join(",").split(",").map(function(key,index){return index;})
3. apply转换一下,可以看到js的大多数坑的解决都有apply的身影。。。
Array.apply(null,Array(100)).map((item,index)=>index)

apply在es5可以加类数组对象,在传入的时候,由于每一项的值都是不存在的,相当于进去的是 undefined(这里的undefined是真真切切的undefined。。。)

4. Int8Array
new Int8Array(100).map((item,index)=>index);

Int8Array的规范看相关文档把

5.讨论的时候B大给的两个最骚的解法。。。

这里写图片描述

这里写图片描述

第二种都看得懂把,第一种新手还是不好理解的,这里解释一下:

1、 js 里的 + 号在不同表达式里有不同含义

2、在这里,是加号
3、加号后面跟一个数组,触发了隐式转换
4、数组的隐式转换包含两个,toString 和 valueOf,根据数组的隐式转换规则,这里调用 valueOf
5、但是 ary 的 valueOf 被改写了,改成了看到的那个函数,那个函数里每次会给 ary 新加一个与下标相同的元素
6、 如果下标还没到 100,就再 +ary 一次,相当于又调用了一次 valueOf
7、于是就形成了递归

其他的
Object.keys(Array(100).toString().split(","))function genArr(i, arr){    if(i < 10){        arr[i] = i++        return genArr(i, arr);    } else {        return arr;    }}var arr = genArr(0, [])var a =[],b= function () { return a.length<100?(a.push(a.length)&&arguments.callee()):a }(); console.log(a,b);[...Array(10).keys()]Array(100).fill().map((v, i) => i)

4.问题的升级

题目改成100w,百万级的数组,怎么生成快?

关键词:弄个 getter setter ,惰性求值

5.求是求真

以上的问题可以看V8的源码,不看源码的时候,只能确定行为,谁解释都有一套。

v8源码链接:
stackoverflow的讨论:

转载于:https://www.cnblogs.com/zhangmingzhao/p/8604788.html

你可能感兴趣的文章
QML学习笔记-入门篇(2)
查看>>
H3C设备网络组建和设置
查看>>
windows上一样使用linux命令--xshell登陆cygwin
查看>>
Linux查看系统配置常用命令
查看>>
大端 VS 小端
查看>>
dojo.mixin(混合进)、dojo.extend、dojo.declare
查看>>
虚拟机克隆之后无法正确获取静态ip
查看>>
Java 连接Kafka报错java.nio.channels.ClosedChannelExcep
查看>>
字符设备驱动程序——poll机制介绍
查看>>
Markdown使用
查看>>
iOS - cocoapods/pod
查看>>
Apache+Tomcat(windows环境下)整合
查看>>
Java程序员应该收藏的书籍
查看>>
小菜学设计模式——策略模式
查看>>
Python 数据类型
查看>>
iOS--环信集成并修改头像和昵称(需要自己的服务器)
查看>>
PHP版微信权限验证配置,音频文件下载,FFmpeg转码,上传OSS和删除转存服务器本地文件...
查看>>
教程前言 - 回归宣言
查看>>
PHP 7.1是否支持操作符重载?
查看>>
Vue.js 中v-for和v-if一起使用,来判断select中的option为选中项
查看>>