javascript - 为什么在 JavaScript 中比较数组和数字这么慢?它在做什么?

标签 javascript arrays

我不小心将一个大数组和一个数字与 < 进行了比较, JavaScript 锁定超过 5 秒。这种比较的预期行为是什么?它是否遍历整个数组? MDN没有说明情况。

作为一个具体的例子,这个代码片段需要超过 5 秒的时间来打印 done :

var m = [];
m[268435461] = -1;
console.log('start');
if (m < 0) { }
console.log('done');

最佳答案

Javascript“数组”(那些带有 Array 原型(prototype),而不是类型化数组)只是对象,因此这是

var m = [];
m[268435461] = -1;

完全一样

var m = {
    "268435461": -1
}

除了第一种情况,mArray原型(prototype)和特殊 length属性(property)。

但是,Array.prototype 中定义的方法(如 forEachjoin )试图隐藏这一事实并“模拟”顺序数组,因为它们存在于其他语言中。当迭代他们的“this”数组时,这些方法采用它的 length属性,从 0 增加循环计数器最多 length-1并用键下的值做一些事情 String(i) (或 undefined 如果没有这样的 key )

// built-in js array iteration algorithm

for (let i = 0; i < this.length - 1; i++) {
     if (this.hasOwnProperty(String(i))
         do_something_with(this[String(i)])
     else
         do_something_with(undefined)

现在,length正如名称所暗示的那样,数组的元素不是其中的元素数量,而是其键的最大数值 + 1,因此在您的情况下,length将是 268435462 (检查它!)

当你做 m < 0 ,即比较一个非数字和一个数字,JS将它们都转换为字符串,Array.toString调用 Array.join ,它又使用上述循环将元素转换为字符串并在它们之间插入一个逗号:

// built-in js Array.join algorithm

target = '';

for (let i = 0; i < this.length - 1; i++) {
    let element = this[String(i)]

    if(element !== undefined)
        target += element.toString()

    target += ','
}

插图:

m  = [];
m[50] = 1;
console.log(m.join())

这涉及大量内存分配,这就是导致延迟的原因。

(经过更多测试,分配不是这里的决定因素,“空心”循环会导致同样的减速:

console.time('small-init')
var m = [];
m[1] = -1;
console.timeEnd('small-init')

console.time('small-loop')
m.forEach(x => null)
console.timeEnd('small-loop')

console.time('big-init')
var m = [];
m[1e8] = -1;
console.timeEnd('big-init')

console.time('big-loop')
m.forEach(x => null);
console.timeEnd('big-loop')

话虽这么说,我不认为现代 JS 引擎那么愚蠢,并且完全按照上面描述的方式实现迭代。他们确实有特定于数组的优化,但这些优化针对的是“良好”的顺序数组,而不是像这样的奇怪的边缘情况。底线:不要那样做!

关于javascript - 为什么在 JavaScript 中比较数组和数字这么慢?它在做什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55363621/

相关文章:

javascript - 如何使用 ngrx 状态管理编辑表单数据?我得到错误(尝试比较错误 '[object Object]' 。仅允许数组和迭代”))

javascript - 用从源代码中删除代码代替显示无

javascript - 有人可以解释以下 javascript

javascript - VueJS父鼠标悬停事件屏蔽子鼠标悬停事件

javascript - innerHTML 修改整个数组

java - 我可以迭代 Java 中 for 循环中定义的数组吗?

调用 e.preventDefault() 时,javascript 错误 "e"未定义

javascript - 使用数组存储多个ID或类,然后在函数中操作它们

javascript - 使用其名称/值从数组中删除项目

ios - 在 Swift 中通过 JSON 正文将字符串数组发布到 API