js如何快速创建长度为n且值都为1的数组?

我心飞翔 分类:javascript

前言

最近公司找我这个菜鸟烧烤哥出春招题,第一次出题内心激动又紧张,生怕出的题别人答不上来。

006APoFYly1g58xx6p45ag308s08s7rn.gif

或许这就是菜鸟的自信吧!

朋友,请听题

js如何快速创建长度为n且值都为1的数组?

这道题咋一看啥也不是,再一看也就那样,咋看一下:有陷阱!

image.png

抽取关键词:快速、长度为n值为1。

下面我们先看一下各种各样的备选答案。

备选答案

答案1

    var a = [];
    for (var i = 0; i < n; i++) {
        a[i] = 1;
    }
 

这个答案谁都懂,for循环创建数组。

答案2

 for (var a = [], j= 0; j< n; a[j++] = 1);
 

该答案是答案1的升级,将赋值语句与循环自增结合在一起,简洁明了!

答案3

[...new Array(n).keys()].map(() => 1);
 

...扩展运算符对数组来说可用于将数组转变成逗号分隔的参数序列,对于对象来说就是取出参数对象的所有可遍历属性。keys() 方法用于从数组创建一个包含数组键的可迭代对象(也就是具备es6中的遍历器Iterator接口的对象)。

分解以上行为:

Image 6.png

扩展运算符将可迭代对象的属性扩展成一个逗号分隔的参数序列。

从这里看出,可迭代对象既同时具备了对象和数组的属性。

答案4

[...Array(n)].map(() => 1);
 

该答案是答案3的进化版本,直接通过扩展运算符扩展初始化数组。我们发现:

image.png

数组初始化生成的是一个长度为n的空数组,但扩展运算符可以将其转化,空间es6中这个语法糖的强大之处。

答案5

Array(n).fill(1);
 

有人不喜欢使用map方法,所以es6给你提供了一个fill方法,一次到位,用固定值填充数组。

答案6

Array.from({ length: n }, () => 1);
 

Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

数组的其实也是一种对象,所以,对象和数组是近亲。那啥类数组

  • 可以通过索引访问成员,并且拥有length属性
  • 不存在数组中的方法如push、forEach等

image.png

答案7

Array.apply(null, Array(n)).map(() => 1);
 

使用apply方法的特性,将初始化数组当作第二参数传入可以实现数组的克隆。很明显,这个比答案4差太多,不建议使用。

答案8

 let arr = new Array(n),i = arr.length;
 while (i--) {
     arr[i] = 1;
 }
 

这个答案很鸡贼,不是使用for循环创建初始化数组,而是通过while对数组进行赋值。结果也是可以得到目标数组。

答案n

或许你还有很多不一样的答案!

哪种答案最correct

接下来我们从性能、可读性来对比这几种答案。

为让我们的对比更有高效,首先需要排除掉答案2、答案7,这两个答案看起来就不是好答案!

性能对比

很多同学做代码的性能对比首先想到的就是运行时间的对比,烧烤哥也是这样。但是烧烤哥的测量的手段比较专业!并不是下面代码所示的简单测量。

    console.time('性能测试');
    //运行代码
    console.timeEnd('性能测试');
 

烧烤哥使用的是专业的测试工具benchmarks: 一个可靠的js基准测试工具。有兴趣的可以看看github上的指引。

基准测试是一种测量和评估软件性能指标的活动,对于js而言,可以保证运行上下文的不变从而得出公平的测试结果。

测试代码如下

const Benchmark = require('benchmark');
const suite = new Benchmark.Suite;
const n = 1000000;

suite.add('答案1', function() {
    let a = [];
    for (var i = 0; i < n; i++) {
        a[i] = 1;
    }
})
.add('答案3', function() {
    [...new Array(n).keys()].map(() => 1);
})
.add('答案4', function() {
    [...Array(n)].map(() => 1);
})
.add('答案5', function() {
    Array(n).fill(1);
})
.add('答案6', function() {
    Array.from({ length: n }, () => 1);
})
.add('答案8', function() {
    let arr = new Array(n),i = arr.length;
    while (i--) {
        arr[i] = 1;
    }
})
.on('cycle', function(event) {
  console.log(String(event.target));
})
.on('complete', function() {
  console.log('Fastest is ' + this.filter('fastest').map('name'));
})
.run({ 'async': true });
 

运行结果:nodejs版本为 v12.14.1

n值 结果
n=1000000 image.png
n=100000 image.png
n=10000 image.png

运行结果:nodejs版本为 v10.12.0

n值 结果
n=1000000 image.png
n=100000 image.png
n=10000 image.png

图片中的Ops/sec是指每秒钟执行测试代码的次数,后面的百分号是指误差率。

可以看出答案5和答案8的性能指标对比其他的答案都遥遥领先!

代码可读性

判断代码可读性,完全是烧烤哥的主观意志,如有雷同,兴甚至哉!

答案1 答案3 答案4 答案5 答案6 答案8
可读性
科技感

所以

从性能和代码可读性来看,答案5更胜一筹:

Array(n).fill(1);
 

可是,fill方法是es6的对数组新加的方法,在低版本浏览器和node中不能愉快的使用,所以这时候可以退而求其次使用一下其他看起来还行的方式:如while、for循环。

当然

这种对比其实毫无意义,除非你创建数组的时候:

n=100000000
 

还有就是你面试的时候!

除此之外,你可以随意使用!

tenor.gif

最后

觉得烧烤哥写的不错的,点赞关注吧;如有不足的地方,请不吝指正。

关注「前端烧烤摊」 掘金 & 微信公众号, 第一时间获取烧烤哥前的总结与发现。

回复

我来回复
  • 暂无回复内容