Chromeコンソールでこのスニペットを実行する:
function foo() {
return typeof null === 'undefined';
}
for(var i = 0; i < 1000; i++) console.log(foo());
1000回false
を印刷する必要がありますが、一部のマシンでは、多数の反復でfalse
を印刷し、残りの場合はtrue
を印刷します。
なぜこうなった?単なるバグですか?
実際には V8 JavaScriptエンジン( Wiki )バグです。
このエンジンはChromium、Maxthron、Android OS、Node.jsなどで使用されます。
比較的単純 バグの説明 この中にあります Redditトピック :
最新のJavaScriptエンジンは、実行時にJSコードを最適化されたマシンコードにコンパイルし(Just In Timeコンパイル)、より高速に実行します。ただし、最適化ステップでは、長期的な高速化と引き換えに初期パフォーマンスコストが発生するため、エンジンは、使用頻度に応じてメソッドが価値があるかどうかを動的に決定します。
この場合、最適化されたパスにのみバグがあるように見えますが、最適化されていないパスは正常に機能します。そのため、最初はメソッドは意図したとおりに機能しますが、ある時点でループ内で頻繁に呼び出されると、エンジンは最適化を決定し、バグのあるバージョンに置き換えます。
このバグはV8自体( commit )、およびChromium( bug report )およびNodeJS( commit )で修正されているようです。
なぜ変更されるのかという直接的な質問に答えるために、バグはChromeで使用されるV8 JSエンジンの「JIT」最適化ルーチンにあります。最初は、記述されたとおりにコードが実行されますが、実行するほど、最適化の利点が分析のコストを上回る可能性が高くなります。
この場合、ループ内で繰り返し実行された後、JITコンパイラーは関数を分析し、最適化されたバージョンに置き換えます。残念ながら、分析では誤った仮定が行われ、最適化されたバージョンでは実際には正しい結果が得られません。
具体的には、 RedditユーザーRainHappensが示唆している型伝播のエラーであること:
また、いくつかの型の伝播も行います(変数などがどの型になる可能性があるかなど)。変数が未定義またはnullの場合には、特別な「検出不能」タイプがあります。この場合、オプティマイザーは「nullは検出できないため、比較のために「undefined」文字列に置き換えることができます。
これは、コードの最適化に関する困難な問題の1つです。パフォーマンスのために再配置されたコードが元のコードと同じ効果を持つことを保証する方法。
これは2か月前に修正され、Chromeすぐに(カナリアで既に)に到着します。