Почему значение typeof null изменяется внутри цикла?

109

Выполнение этого фрагмента в консоли Chrome:

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

должен печатать 1000 раз false, но на некоторых машинах будет печатать falseнесколько итераций, а затем true- все остальные.

введите описание изображения здесь

Почему это происходит? Это просто ошибка?

Акос
источник
4
Это возвращается в 1000 раз верным для меня ...
Хоанг Лонг
2
Я думаю, это ошибка, у меня 262 ложных / 738 истинных
Jax Teller
1
это что-то странное с консолью Chrome: если вы нажмете на массив и запишете массив, то все false. как есть, количество trues колеблется в зависимости от хрома.
dandavis
1
@ HoàngLong, как я уже сказал в вопросе, это происходит только на некоторых машинах. Также возможно, что это происходит только в некоторых версиях Chrome
Agos
2
@ HoàngLong, убедитесь, что вы запускаете его в Chrome
Nobita

Ответы:

74

Для этого есть ошибка хрома:

Проблема 604033 - JIT-компилятор не сохраняет поведение метода.

Так что да, это просто ошибка!

Slumber86
источник
5
"Просто"? Неужели это не может сломать произвольные веб-приложения по всему миру?
jpmc26
6
«Просто», чтобы сказать, что это не особенность или что-то странное. Это критическая ошибка, но просто ошибка!
Slumber86
37

На самом деле это ошибка движка V8 JavaScript ( Wiki ).

Этот движок используется в Chromium, Maxthron, Android OS, Node.js и т. Д.

Относительно простое описание ошибки вы можете найти в этой теме Reddit :

Современные движки JavaScript компилируют код JS в оптимизированный машинный код при его выполнении (компиляция Just In Time), чтобы он работал быстрее. Однако этап оптимизации имеет некоторую начальную стоимость производительности в обмен на долгосрочное ускорение, поэтому механизм динамически решает, стоит ли метод того, в зависимости от того, как часто он используется.

В этом случае, похоже, есть ошибка только в оптимизированном пути, тогда как неоптимизированный путь работает нормально. Итак, сначала метод работает так, как задумано, но если он вызывается в цикле достаточно часто, в какой-то момент движок решит его оптимизировать и заменит версией с ошибками.

Эта ошибка, похоже, была исправлена ​​в самой версии V8 ( фиксация ), а также в Chromium ( отчет об ошибке ) и NodeJS ( фиксация ).

Сергей Новиков
источник
Я подтвердил, что ошибка все еще присутствует в Node.js 6.2.2, что меня беспокоит.
Майкл Шопсин
Это было исправлено в движке V8 сегодня (21.06), я думаю, что скоро соответствующее программное обеспечение будет обновлено.
Сергей Новиков
Резервное копирование исправления v8 в Node.js 6.2.x уже выполняется, так как проблема № 7348 принадлежит TheAlphaNerd .
Майкл Шопсин
18

Чтобы ответить на прямой вопрос, почему он меняется, ошибка заключается в программе оптимизации "JIT" движка V8 JS, используемого Chrome. Сначала код выполняется точно так, как написано, но чем больше вы его запускаете, тем больше возможностей для преимуществ оптимизации, которые перевешивают затраты на анализ.

В этом случае после повторного выполнения в цикле JIT-компилятор анализирует функцию и заменяет ее оптимизированной версией. К сожалению, анализ делает неверное предположение, и оптимизированная версия на самом деле не дает правильного результата.

В частности, пользователь Reddit RainHappens предполагает, что это ошибка при распространении типа :

Он также выполняет некоторое распространение типов (например, какие типы могут быть у переменных и т. Д.). Есть специальный «необнаруживаемый» тип, когда переменная не определена или имеет значение NULL. В этом случае оптимизатор считает, что значение null не обнаруживается, поэтому его можно заменить строкой «undefined» для сравнения.

Это одна из сложных проблем оптимизации кода: как гарантировать, что код, который был изменен для повышения производительности, по-прежнему будет иметь тот же эффект, что и исходный.

IMSoP
источник