奇妙JS代码系列(三)一道有趣的题(创建一个长度为x数组)

原题描述:

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

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

误区

最容易掉入陷阱的:

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

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

解释

坑在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也发现过。

正确的解决

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

其实这道题的面试官当时是说不用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、于是就形成了递归

问题的升级

题目改成100w,百万级的数组,怎么生成快?
关键词:弄个 getter setter ,惰性求值

求是求真

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

原文地址:https://www.cnblogs.com/zhangmingzhao/p/8604788.html

时间: 2024-07-31 16:32:38

奇妙JS代码系列(三)一道有趣的题(创建一个长度为x数组)的相关文章

奇妙JS代码系列(二)call,apply,bind用处整理

延续系列一:链接,此次主要整理JS里面很常见的三个函数call,apply,bind的妙用.(apply和call差不多,只是参数的区别)(下面有些这三个可能只是辅助,但是只要用到,我就在这里整理) 1.类型检测 上一篇最后一个已经讲到,Object.prototype.toString.call().犹豫上一篇说了,这里就不多说了. 2.解参,等同于ES6中的...扩展运算符 es6中提出了一种很方便解构字符串数组的运算符,这提供了一种简便机制,可以将任何部署了 Iterator 接口的数据结

1在html中添加js代码的三种方式

1.第一种方式:在时间句柄后太假js代码: 例如浏览器弹出对话框; 1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 2 "http://www.w3.org/TR/html4/loose.dtd"> 3 <html> 4 <head> 5 <meta http-equiv="Content-Type" content=&qu

Node.js学习笔记(6)——使用Express创建一个工程

前提是搭建好了环境,node,npm,express:(推荐全局安装) 开始用express创建一个基础工程: express –t ejs microblog 进入文件夹之后 npm-install(自动安装好要用的依赖) 这个时候,用浏览器访问localhost:3000,就可以出来一个欢迎界面了,这里简单说下原理:当你在浏览器输入这个地址砸下回车的瞬间,node搭建的一个网站后台程序开始工作,在app.js这个文件里面,有app.get(‘/’, routes.index);这一句话的作用

JavaWeb学习(三) : 如何在 Eclipse 中创建一个Web 项目并成功运行?

前置条件 : 1.确保已安装 Eclipse.Tomcat 服务器安装包 2.jdk.环境变量都已配置成功. 3.注意在安装 Eclipse 时一定要选择第二个有 Web 项目的进行安装, 不然安装成功后还需要下载很多的插件,增加了不必要的麻烦. 创建步骤 : 1.将本地 Tomcat 服务器添加 进去: 首先打开 Window -- preference 搜索 server 双击 Runtime Environments 点击 Add,找到对应的你下载的 TomCat 服务器,进行添加,我的

Discuz common.js代码注释(三)

/** * 去除字符串左右两端的空格 */ function trim(str) { return (str + '').replace(/(\s+)$/g, '').replace(/^\s+/g, ''); } /** * 获取字符串长度(兼容IE:如果是IE,并且有换行符(\n)则替换为‘_’然后再计算长度) */ function strlen(str) { //获取字符串长度 return (BROWSER.ie && str.indexOf('\n') != -1) ? str

js Dom(三)节点、元素创建3种方式、为元素绑定多个事件addEventListener、attachEvent

目录: 1.节点的概念    2.节点的属性(nodeType,nodeName,nodeValue)    3.父节点(父元素)    4.获取子节点或子元素    5.获取节点.元素的方法(12行代码)    6.案例:div标签里面的p标签背景高亮(使用子节点或子元素的方式)    7.封装节点兼容代码    8.案例:切换背景图片    9.案例:全选.全不选    10.元素创建的第一种方式  document.write("<p>文本</P>"); 

Android群英传笔记系列三 view的自定义:实现一个模拟下载

1.实现效果:动态显示进度(分别显示了整个的动态改变的过程,然后完成后,弹出一个对话框)       2.实现过程:可以分为绘制一个圆,圆弧和文本三部分,然后在MainAcitivity中通过线程模拟下载进度. a.定义一个类继承至view,然后添加其构造函数,记得一定要添加含有Attributset参数的构造函数; b.定义和初始化一些数据: private int mCircleXY; private int mWidth; private float mRadius; private Pa

最新的JavaScript核心语言标准&mdash;&mdash;ES6,彻底改变你编写JS代码的方式!【转载+整理】

原文地址 本文内容 ECMAScript 发生了什么变化? 新标准 版本号6 兑现承诺 迭代器和for-of循环 生成器 Generators 模板字符串 不定参数和默认参数 解构 Destructuring 箭头函数 Arrow Functions Symbols 集合 学习Babel和Broccoli,马上就用ES6 代理 Proxies ES6 说自己的宗旨是"凡是新加入的特性,势必已在其它语言中得到强有力的实用性证明."--TRUE!如果你大概浏览下 ES6 的新特性,事实上它

JS代码大全 (都是网上看到 自己整理的)

原文 JS代码大全 (都是网上看到 自己整理的) 事件源对象 event.srcElement.tagName  event.srcElement.type 捕获释放 event.srcElement.setCapture();   event.srcElement.releaseCapture(); 事件按键 event.keyCode  event.shiftKey  event.altKey  event.ctrlKey 事件返回值 event.returnValue 鼠标位置 event