一个 TODO 引发的血案

Chrome 都用过吧,V8 都知道吧,V8 性能杠杠的没问题吧。但是你知道吗,有这样一段神奇的代码因为 V8 的优化导致了与实际设计预期的行为发生了相悖。

Object.defineProperty

  • Object.defineProperty([], 'length', {value: -1, configurable: true})
  • Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})

我们可以来看一下这俩段代码,从 JavaScript 的语义上来说,这俩段代码并没有什么不同,但是当我们将他们运行在 Chrome 或者 Node.js 中,我们便能得到俩个截然不同的错误。

javascript 复制代码
Object.defineProperty([], 'length', {value: -1, configurable: true})
VM8179:1 Uncaught RangeError: Invalid array length
    at Function.defineProperty (<anonymous>)
    at <anonymous>:1:8
(anonymous) @ VM8179:1

Object.defineProperty([], 'len' + 'gth', {value: -1, configurable: true})
VM8183:2 Uncaught TypeError: Cannot redefine property: length
    at Function.defineProperty (<anonymous>)
    at <anonymous>:2:8

为什么呢?

那这和 V8 性能优化极好又有什么关系呢?我们可以在他们的源码中看到如下内容:

github.com/v8/v8/blob/...

cpp 复制代码
  if (*name == ReadOnlyRoots(isolate).length_string()) {
    // 2a. Return ArraySetLength(A, Desc).
    return ArraySetLength(isolate, o, desc, should_throw);
  }

简单理解一下,就是在这里直接使用了引用与系统内的常量字符串 "length" 引用进行了一个 O(1) 的快速匹配,从而优化了性能,但是也导致了我们上面的错误。

那么为什么 'length''len' + 'gth' 有区别呢?这个没有优化吗?我们可以在这《Exploring V8's strings: implementation and optimizations》看到实际上在 V8 中存在着不同的字符串,当我们使用 %DebugPrint 对俩段内容进行输出的时候,我们可以发现:

  • %DebugPrint('leng'+'th'):ONE_BYTE_STRING_TYPE
  • %DebugPrint('length'):ONE_BYTE_INTERNALIZED_STRING_TYPE

这也是为什么后者没办法作为一个常量字符串和全局只读常量字符串 "length" 进行比较的原因。

标题党!

你可能会问,你说的我都看懂了,但是和 TODO 又有什么关系呢?经常写代码都知道,我们不想处理的问题,都是先 TODO NEVER DO ,其实我们刚刚的代码上面看到一段东西:

github.com/v8/v8/blob/...

arduino 复制代码
  // TODO(jkummerow): Check if we need slow string comparison.

是不是很有趣呢 V8,哈哈哈哈,希望看完这些你能对 JS 一些底层的内容产生兴趣。

后续进展

在去年年底的时候,我与 TC39 的成员 Jack Works 讨论了这个问题,最后得到了一个结论,在 ECMAScript 的设计上来看,这确实是 V8 的一个问题(虽然它是为了优化性能),但是 TC39 也没有料到 V8 的优化这么细节,居然导致了上面的问题。所以在那段时间,他们对于该行为进行了约束,并且在 TC39 的标准中加入了该检测。

github.com/tc39/test26...

当时我在 v8 bugs 中也反映了该问题,他们在最近也对这个问题进行了修复。

bugs.chromium.org/p/v8/issues...

总结

挺开心的,有一种建设世界的快乐了。

相关推荐
ZC跨境爬虫8 小时前
跟着 MDN 学 HTML day_9:(信件语义标记)
前端·css·笔记·ui·html
前端老石人8 小时前
HTML 字符引用完全指南
开发语言·前端·html
matlab_xiaowang8 小时前
Redux 入门:JavaScript 可预测状态管理库
开发语言·javascript·其他·ecmascript
幼儿园技术家8 小时前
前端如何设计权限系统(RBAC / ABAC)?
前端
前端摸鱼匠10 小时前
Vue 3 的v-bind合并行为:讲解v-bind与普通属性合并的规则
前端·javascript·vue.js·前端框架·ecmascript
REDcker10 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
donecoding12 小时前
一个 sudo 引发的血案:npm 全局包权限错乱彻底修复
前端·node.js·前端工程化
风骏时光牛马12 小时前
Raku正则匹配与数据批量处理实操案例
前端
nbwenren12 小时前
2026实测:Gemini 3 镜像站视觉能力实践——拍照原型图,一键生成 HTML+CSS 代码
前端·css·html
Lee川12 小时前
Prisma 实战指南:像搭积木一样设计古诗词数据库
前端·数据库·后端