Javascript не имеет репутации «быстрого» языка программирования. В отличие от компилируемых языков, таких как Java и C++, производительность javascript традиционно считалась недостаточной для ресурсоемких приложений. Современные движки Javascript, такие как Chrome V8 и Mozilla SpiderMonkey, делают возможным беспрецедентное повышение производительности javascript, но только если разработчики пишут код, соответствующий правильным «стандартам»:

1. Сохраняйте согласованность классов объектов

Одним из самых больших ограничений производительности для Javascript традиционно было отсутствие настоящих классов объектов. Обычные языки ООП используют переменные экземпляра, хранящиеся в фиксированных смещениях памяти для классов, тогда как доступ к свойствам объекта Javascript обычно осуществляется через поиск в динамическом словаре, что требует вычисления хэша каждый раз при доступе к свойству.

Современные движки Javascript, такие как V8, создают «скрытые классы» для решения этой проблемы. Когда инициализируется новый объект или конструктор, V8 создает новый оптимизированный «скрытый класс» и использует его для хранения переменных способом, подобным тому, который использовал бы Java. Это делает поиск свойств НАМНОГО быстрее, но есть одна загвоздка: свойства объекта инициализируются в другом порядке, и скрытый класс приходится «выбрасывать» и заменять более медленным динамическим поиском. Решение: «мономорфный» код, в котором свойства объекта всегда инстанцируются в конструкторе, последовательно типизируются и назначаются в одном и том же порядке.

// Bad
function Cat() {
  this.personality = 'evil';
  this.favoritePasstime = 'napping';
}
const siamese = new Cat();
siamese.color = 'black and white';
siamese.personality = 9;

// Good
function Dog() {
  this.awesome = true;
  this.favoritePasstime = 'frisbee';
  this.group;
}
const golden = new Dog();
golden.group = 'working';

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

2. Не заполняйте большие массивы заранее

Массивы Javascript чрезвычайно гибки — они могут хранить как примитивы, так и ссылки на объекты и другие массивы, а также могут иметь переменную длину. Ради оптимизации памяти V8 реализует массивы двумя разными способами. Небольшие плотно заполненные массивы (называемые «быстрыми элементами») хранятся в линейных «фрагментах» памяти, известных как линейные буферы хранения. И наоборот, большие разреженные массивы (называемые «элементами словаря») реализованы в виде хэш-таблиц. Правила для различной оптимизации каждой формы массива. Быстрые элементы должны быть предварительно заполнены, чтобы зарезервировать необходимое место в памяти. Напротив, элементы словаря не должны быть предварительно заполнены, так как это приводит к пустой трате памяти и препятствует оптимизации алгоритма хеширования.

// Bad
const bigArray = new Array(200);
for(let i = 1; i < 1000; i += 6) {
  bigArray.push(i);
}
// Good
const bigArray = [];
for(let i = 1; i < 1000; i += 6) {
  bigArray.push(i);
}

В заключение, Javascript — исключительно гибкий язык, но, как и в любых отношениях, чрезмерное использование гибкости вашего партнера имеет свои издержки.

Алекс Патч является создателем Sabertooth, ведущей библиотеки Javascript для работы с устройствами Bluetooth в браузере. В настоящее время он проживает в Лос-Анджелесе, штат Калифорния, в нескольких минутах езды на мотоцикле от пляжа Доквейлер.