Пишем производительный JavaScript. 3 совета.

Эта статья основана на советах Бенедикта Маурера, встреченных мной в статьях, комментариях и лекциях. Почему эта информация интересна и важна? Бенедикт принимает непосредственное участие в разработке V8 и досконально знает, как именно интерпретатор и компилятор обрабатывают наш код.

Помогите JavaScript понять, что на самом деле вы хотите сделать

Избегайте неопределённости. Например, если у вас есть значение obj, которое может быть undefined или объектом, рассмотрите возможность исключения неопределенности через явное условие:

if (obj !== undefined) {
  // …
}

Тогда компилятору не придётся делать множество лишних проверок, как в случае, если вы напишите:

if (obj) {
  // …
}

Что должен сделать компилятор в этом случае? Проверить, что obj не является пустой строкой, false, 0 или undefined. Всё это порождает лишние проверки в байткоде. Кажется, что достаточно понимания того, что перед нами объект, а объект всегда возвращает true на toBoolean. Но, к сожалению, в нашем динамически типизируемом языке компилятору придётся следить за всем циклом жизни переменной obj, чтобы убедиться, что на вход условия действительно пришёл объект.

Первый вариант в среднем на 15% быстрее.

Дополнительная польза: когда вы будете читать свой код через год (или это будет делать кто-то другой), вам будет гораздо проще понять, что на самом деле проверяет ваше условие.

Осторожнее с && и ||

https://github.com/developit/preact/pull/610

Это пример из реального коммита в репозитории preact. Изменение логического выражения на тернарный оператор сделано для повышения производительности кода (автор сообщает об 1–5% ускорении). Но за счёт чего?

Мы помогли компилятору, указав, что значением vlen всегда будет Number. В негативном сценарии первого случая мы можем получить для vlen любой тип данных, приводимый к false, например, Boolean или null , что и приводит к деоптимизации, а так же добавляет ещё одну дорогостоящую проверку позже, чтобы убедиться, что vlen на самом деле число.

Воспользовавшись советом из первой части статьи, можно получить дополнительное ускорение:

vlen = (vchildren !== undefined) ? vchildren.length : 0

В целом, использование && или || в небулевом контексте (особенно с числами) не слишком хорошее решение, из-за семантики && и ||в JavaScript.

Ещё один интересный пример неправильного использования ||:

function foo(a, b) {
  a = a || "value";
  b = b || 4;
  // …
}

В данном случае ошибочно отсекаются допустимые значения, такие как пустая строка '' для a и 0 для b.

Не доверяйте undefined

Выше мы использовали проверку

if (obj !== undefined) {
  // …
}

Но всё ли с ней хорошо? Давайте проведём небольшой эксперимент

const isDefined = (function() {
  const undefined = 1;
  return x => x !== undefined;
})();console.log(isDefined(undefined));  // true
console.log(isDefined(1));          // false

На самом деле undefined не является ключевым словом в JavaScript. Это просто поле в глобальном объекте, и движок JS вынужден это учитывать. К счастью, в V8 уже присутствует неплохая оптимизация и если в цепочке видимости отсутствуют eval или with, компилятор не будет производить поиск значения undefined в глобальном объекте. Но вот в браузере Safari такая оптимизация отсутствует.

Защититься от подмены undefined (и немного упростить жизнь компилятору) можно путём вызова оператора void, который всегда возвращает настоящий undefined.

if (obj !== void 0) {
  // …
}

Я не призываю использовать эту конструкцию, потому что она может поставить в тупик менее опытных коллег, столкнувшихся с вашим кодом. Но в случае необходимости достижения максимальной производительности стоит подумать о таком подходе. Также void 0 пригодится, если в вашем проекте включено правило no-undefined в ESlint.

Подробнее читайте в статье «Иногда undefined это defined»

Слушайте наш подкаст на SoundCloud, читайте на Medium, контрибьютьте на GitHub, общайтесь в группе Telegram, следите в Twitter и канале Telegram, рекомендуйте в VK и Facebook.

Статья на GitHub

devSchacht

Подкаст. Переводы. Веб-разработка.

281

281 claps
Andrey Melikhov

Written by

Web-developer in big IT company Перевожу всё, до чего дотянусь. Иногда (но редко) пишу сам.

devSchacht

Подкаст. Переводы. Веб-разработка.