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 в браузере. В настоящее время он проживает в Лос-Анджелесе, штат Калифорния, в нескольких минутах езды на мотоцикле от пляжа Доквейлер.