为什么 typeof null 的值在循环中会改变?

在 Chrome 控制台中执行以下代码片段:

function foo() {
return typeof null === 'undefined';
}
for(var i = 0; i < 1000; i++) console.log(foo());

应该打印1000次 false,但在一些机器上将打印 false为一些迭代,然后 true为其余。

enter image description here

为什么会这样,只是虫子吗?

7490 次浏览

There is a chromium bug open for this:

Issue 604033 - JIT compiler not preserving method behavior

So yes It's just a bug!

It's actually a V8 JavaScript engine (Wiki) bug.

This engine is used in Chromium, Maxthron, Android OS, Node.js etc.

Relatively simple bug description you can find in this Reddit topic:

Modern JavaScript engines compile JS code into optimized machine code when it is executed (Just In Time compilation) to make it run faster. However, the optimization step has some initial performance cost in exchange for a long term speedup, so the engine dynamically decides whether a method is worth it depending on how commonly it is used.

In this case there appears to be a bug only in the optimized path, while the unoptimized path works fine. So at first the method works as intended, but if it's called in a loop often enough at some point the engine will decide to optimize it and replaces it with the buggy version.

This bug seems to have been fixed in V8 itself (commit), aswell as in Chromium (bug report) and NodeJS (commit).

To answer the direct question of why it changes, the bug is in the "JIT" optimisation routine of the V8 JS engine used by Chrome. At first, the code is run exactly as written, but the more you run it, the more potential there is for the benefits of optimisation to outweigh the costs of analysis.

In this case, after repeated execution in the loop, the JIT compiler analyses the function, and replaces it with an optimised version. Unfortunately, the analysis makes an incorrect assumption, and the optimised version doesn't actually produce the correct result.

Specifically, Reddit user RainHappens suggests that it is an error in type propagation:

It also does some type propagation (as in what types a variable etc can be). There's a special "undetectable" type for when a variable is undefined or null. In this case the optimizer goes "null is undetectable, so it can be replaced with the "undefined" string for the comparison.

This is one of the hard problems with optimising code: how to guarantee that code which has been rearranged for performance will still have the same effect as the original.