10 лет назад Amazon поделилась примером, когда каждые 100 миллисекунд задержки стоили им 1% их продаж, или около 1,6 миллиарда долларов за каждую дополнительную секунду времени загрузки в течение года. Аналогичным образом Google обнаружил, что увеличение времени создания страницы поиска на 500 миллисекунд сократит трафик на 20% и снизит потенциальный доход от рекламы на пятую часть.
Немногие из нас могут справиться с такой большой игрой, как Google и Amazon, однако те же принципы применяются в меньшем масштабе, более быстрый код приводит к лучшему взаимодействию с пользователем и более выгоден для бизнеса. Особенно в веб-разработке скорость может быть ключевым фактором в конкуренции с конкурентами. Каждая миллисекунда, потерянная в более быстрой сети, усиливается в более медленной сети.
В этой статье мы рассмотрим 13 практических советов по ускорению JavaScript независимо от того, пишете ли вы серверный код на основе Node.js или клиентский код JavaScript. Я предоставил на основеjsperf.comСозданы тест-кейсы производительности. Если вы хотите проверить эти советы самостоятельно, обязательно перейдите по этим ссылкам.
Do It Less
Самый быстрый код — это код, который никогда не запускается.
1. Удалите бесполезные функции
Легко начать оптимизировать код, который уже написан, но часто самый большой прирост производительности достигается, если сделать шаг назад и спросить себя, почему наш код должен быть здесь.
Прежде чем продолжить работу по оптимизации, спросите себя, действительно ли ваш код должен делать то, что он делает сейчас. Необходимы ли компоненты или функции в этой функции? Если нет, удалите его. Этот шаг очень важен для повышения скорости кода, но его легко проигнорировать.
2. Избегайте бесполезных шагов
Ориентиры:JSP и F.com/ненужные…
В меньшем масштабе полезен ли каждый шаг, выполняемый во время выполнения функции? Например, не попали ли ваши данные в ненужный круг для достижения максимального эффекта? Следующий пример может быть упрощен, однако он может отображать проблемы, которые трудно обнаружить в больших объемах кода.
'incorrect'.split('').slice(2).join(''); // converts to an array
'incorrect'.slice(2); // remains a string
Даже в простых примерах разница в производительности огромна, запуск некоторого кода происходит намного медленнее, чем запуск кода вообще! Хотя немногие совершат описанные выше ошибки, легко добавить несколько бесполезных шагов, прежде чем получить результат в условиях более длинного и сложного кода. Постарайтесь избежать этого!
Do It Less often
Если вы не можете удалить код, спросите себя, можете ли вы делать это реже? Одна из причин, по которой код настолько мощный, заключается в том, что он может легко использовать нас для выполнения повторяющихся операций, но также легче заставить наш код выполняться больше раз, чем необходимо. Вот некоторые особые случаи, о которых следует знать.
3. Чем раньше вы выйдете из петли, тем лучше
Ориентиры:JSP и F.com/break-loops…
Найдите ситуации в цикле, которые не требуют выполнения итераций. Например, если вы ищете особое значение и нашли его, остальные итерации не нужны. вы должны использоватьbreak
оператор, чтобы разорвать исполняемый цикл:
for (let i = 0; i < haystack.length; i++) {
if (haystack[i] === needle) {
break;
}
}
Или, если вам нужно работать только с определенными элементами в цикле, вы можете использоватьcontinue
оператор, чтобы пропустить операции над другими элементами.continue
завершит оператор выполнения в текущей итерации и немедленно перейдет к следующему оператору:
for (let i = 0; i < haystack.length; i++) {
if (!haystack[i] === needle) {
continue;
}
doSomething();
}
Стоит отметить, что вы также можете пройтиbreak
илиcontinue
Пропустить вложенные циклы:
loop1: for (let i = 0; i < haystacks.length; i++) {
loop2: for (let j = 0; j < haystacks[i].length; j++) {
if (haystacks[i][j] === needle) {
break loop1;
}
}
}
4. Инициализировать только один раз
Ориентиры:JSP и F.com/afraid of heat-compute…(Переводчики тестируют себя под Mac, используют/не используют замыкания, используют/не используют глобальные переменные и в настоящее время мало влияют на производительность)
В нашем приложении мы будем вызывать следующие методы несколько раз:
function whichSideOfTheForce1(name) {
const light = ['Luke', 'Obi-Wan', 'Yoda'];
const dark = ['Vader', 'Palpatine'];
return light.includes(name) ? 'light' : dark.includes(name) ? 'dark' : 'unknown';
}
whichSideOfTheForce1('Luke');
whichSideOfTheForce1('Vader');
whichSideOfTheForce1('Anakin');
В этом коде каждый раз, когда мы вызываемwhichSideOfTheForce1
, массив пересоздается 2 раза, требуя перераспределения памяти для нашего массива при каждом его вызове.
Значение предоставленного массива фиксировано, поэтому лучшее решение — определить его один раз, а затем вызвать его ссылку в функции. Хотя мы могли бы также определить эти две переменные массива глобально, это позволило бы изменять их за пределами нашей функции. Лучшее решение — использовать замыкание, что означает, что оно возвращает функцию:
function whichSideOfTheForceClosure1(name) {
const light = ['Luke', 'Obi-Wan', 'Yoda'];
const dark = ['Vader', 'Palpatine'];
return (name) => (light.includes(name) ? 'light' : dark.includes(name) ? 'dark' : 'unknown');
}
const whichSideOfTheForce2 = whichSideOfTheForceClosure1();
Теперь наш массив будет инициализирован только один раз. Взгляните на следующий пример:
function doSomething(arg1, arg2) {
function doSomethingElse(arg) {
return process(arg);
};
return doSomethingElse(arg1) + doSomethingElse(arg2);
}
каждый запускdoSomething
Когда вы начинаете создавать вложенную функцию с самого началаdoSomethingElse
. Замыкания обеспечивают решение, если мы возвращаем функцию,doSomethingElse
Все еще частный, но созданный только один раз:
function doSomething(arg1, arg2) {
function doSomethingElse(arg) {
return process(arg);
};
return (arg1, arg2) => doSomethingElse(arg1) + doSomethingElse(arg2);
}
5. Контролируйте порядок выполнения кода, чтобы обеспечить минимальное количество запусков
Ориентиры:JSP и F.com/choosing-diva…
Если вы внимательно продумаете порядок выполнения каждого шага в функции, это также может помочь нам повысить эффективность выполнения кода. Предположим, у нас есть массив для хранения фиксированных цен (центов), нам нужна функция для суммирования цен товаров и возврата результата (долларов):
const cents = [2305, 4150, 5725, 2544, 1900];
У этой функции есть две функции: конвертировать единицы и суммировать, но важен порядок этих действий. Если мы расставим приоритеты единиц преобразования, наша функция будет выглядеть так:
function sumCents(array) {
return '$' + array.map(el => el / 100).reduce((x, y) => x + y);
}
В этом методе нам нужно разделить каждый элемент массива, и если мы изменим порядок выполнения, нам нужно будет сделать только одно деление:
function sumCents(array) {
return '$' + array.reduce((x, y) => x + y) / 100;
}
Ключом к оптимизации производительности является обеспечение того, чтобы функции выполнялись в наилучшем порядке.
6. Понимание временной сложности кода O(n)
Знание временной сложности вашего кода — один из лучших способов понять, почему одни методы работают быстрее и используют меньше памяти, чем другие. Например, вы можете с первого взгляда понять, почему бинарный поиск является одним из самых эффективных алгоритмов поиска и почему быстрая сортировка часто является наиболее эффективным алгоритмом сортировки с использованием временной сложности. Пожалуйста, узнайте самивременная сложность
Do It Faster
Преимущества оптимизации скорости кода часто относятся к первым двум категориям. В этом разделе мы обсудим несколько способов увеличить скорость вашего кода, они больше связаны с оптимизацией кода, чем с его отбраковкой или уменьшением количества прогонов.
Конечно, эти оптимизации также уменьшают размер кода или делают его более удобным для компилятора, но на первый взгляд вы просто меняете код.
7. Используйте встроенные функции
Ориентиры:JSP вместо F.com/prefer - не IL...
Это очевидно для тех, кто имеет опыт работы с компиляторами и базовым языком. Однако, если упомянуть это здесь как основное правило, если в JavaScript есть встроенная функция, используйте ее.
Код компилятора разработан с оптимизацией производительности для методов или типов объектов. Кроме того, базовым языком для встроенных методов является C++. Если ваш вариант использования не является особенно специфичным, ваш собственный код JavaScript редко работает быстрее, чем существующий встроенный код.
Чтобы проверить это, давайте сами реализуем метод карты.
function map(arr, func) {
const mapArr = [];
for (let i = 0; i < arr.length; i++) {
const result = func(arr[i], i, arr);
mapArr.push(result);
}
return mapArr;
}
Давайте создадим массив, содержащий 100 случайных чисел (1-100).
const arr = [...Array(100)].map(e=>~~(Math.random()*100));
Давайте выполним несколько простых операций (умножение чисел на 2), чтобы сравнить различия:
map(arr, el => el * 2); // Our JavaScript implementation
arr.map(el => el * 2); // The built-in map method
В моих тестах мы реализовали собственныеmap
метод, чем роднойArray.prototype.map
на 65% медленнее.
8. Выберите лучший тип данных
Контрольный показатель 1:set.add()
vs array.push()
JSP и F.com/adding-to-ah...
Контрольный показатель 2:map.set()
vs object['xx']
JSP и F.com/adding-flatting-…
Точно так же наилучшая производительность может быть достигнута при выборе соответствующих встроенных типов данных. В JavaScript встроено гораздо больше типов данных, чем:Number
,String
,Function
,Object
. Многие необычные типы данных дадут очень значительные преимущества, если их использовать в правильной сцене.
Set
а такжеMap
Существует явное преимущество в производительности в ситуациях, когда элементы добавляются и удаляются часто.
Знание встроенных типов объектов и попытка использовать тот, который лучше всего соответствует вашим потребностям, может быть очень полезным для повышения производительности вашего кода.
9. Не забывайте о памяти
Как язык высокого уровня, JavaScript обрабатывает множество низкоуровневых деталей за вас. Управление памятью является одним из них. JavaScript использует систему, называемую сборкой мусора (GC), для освобождения памяти, которая может быть освобождена автоматически без явных инструкций от разработчика.
Хотя управление памятью в JavaScript осуществляется автоматически, это не означает, что оно идеально. Есть и другие шаги, которые вы можете предпринять, чтобы управлять памятью и уменьшить вероятность утечек памяти.
Например,Set
а такжеMap
Есть вариантыWeakSet
а такжеWeakMap
, они содержат "слабую" ссылку на объект. Они гарантируют отсутствие утечек памяти, гарантируя, что объекты внутри них запускают сборку мусора, когда на них не ссылаются другие объекты.
После ES2017 можно пройтиTypedArray
объект для лучшего управления распределением памяти. Например,Int8Array
Можно ставить значения от -128 до 127, занимая всего один байт. Однако стоит отметить, что использованиеTypedArray
Прирост производительности, вероятно, невелик: сравнение обычного массива с Uint32Array дает небольшое улучшение производительности записи, но почти никакого улучшения производительности чтения.
Базовое понимание лежащего в основе языка программирования может помочь вам писать более быстрый и качественный код JavaScript.
10. Используйте мономорфизм везде, где это возможно
Контрольный показатель 1:JSP и F.com/monomorphic…
Контрольный показатель 2:JSP и f.com/impact-of-...
Если мы установимconst a = 2
, переменную a можно считать полиморфной (можно изменять). И наоборот, если мы используем 2 напрямую, его можно считать мономорфным (его значение фиксировано).
Конечно, установка переменной полезна, если нам нужно использовать ее несколько раз. Однако, если вы используете переменную только один раз, будет немного быстрее избежать полной установки переменной. Возьмите простую функцию умножения:
// 函数定义
function multiply(x, y) {
return x * y;
}
если мы побежимmultiply(2, 3)
, что на 1% быстрее, чем непосредственное выполнение следующего кода:
// 定义2个变量作为multiply的参数
let x = 2, y = 3;
multiply(x, y);
Это маленькая победа, а прирост производительности большого кода, как правило, состоит из множества маленьких побед.
Точно так же использование параметров в функциях обеспечивает гибкость, но снижает производительность. Если они вам не нужны, вы можете превратить его в константу и поместить в функцию, и это немного улучшит производительность. следовательно,multiply
Более быстрая версия выглядит так:
// 如果3是固定不变的时候,则直接作为函数中的一部分
function multiplyBy3(x) {
return x * 3;
}
В сочетании с приведенными выше оптимизациями прирост производительности в моих тестах составил около 2%. Хотя точка изменения относительно невелика, стоит подумать, можно ли сделать это улучшение несколько раз в большой кодовой базе.
Примечание переводчика, исходный текст здесь слишком запутан, давайте посмотрим на комментарии в коде, чтобы понять
а) вводите параметры функции только тогда, когда значение должно быть динамическим, в противном случае записывайте их как переменные внутри функции;
б) вводите переменные только тогда, когда значение используется несколько раз, в противном случае записывайте значение напрямую;
11. Избегайте использованияdelete
Контрольный показатель 1:JSP и F.com/removing-VA…
Контрольный показатель 2:JSP и F.com/delete-vs-no...
delete
Функция ключевого слова — удалить свойство в объекте. Вы можете найти это полезным для своего приложения, но я надеюсь, что вы постараетесь не использовать его. В двигателе v8,delete
ключевое слово удаленоhidden class
Преимущество превращения объекта в «медленный объект».
скрытый класс: поскольку JavaScript является динамическим языком программирования, свойства могут добавляться и удаляться динамически, что означает, что свойства объекта могут изменяться.Большинство движков JavaScript (V8) вводят тип объектов и переменные для их отслеживания.Концепция скрытые классы. Во время выполнения V8 создает скрытые классы, которые присоединяются к каждому объекту, чтобы отслеживать его форму/макет. Это оптимизирует время доступа к атрибуту
В зависимости от ваших потребностей вы можете просто установить свойства, которые вам не нужны.undefined
достаточно.
const obj = { a: 1, b: 2, c: 3 };
obj.a = undefined;
Я видел несколько предложений в Интернете, которые используют следующую функцию для копирования объектов, кроме указанных свойств:
const obj = { a: 1, b: 2, c: 3 };
const omit = (prop, { [prop]: _, ...rest }) => rest;
const newObj = omit('a', obj);
Однако в моих тестах указанная выше функция работает быстрее, чемdelete
Ключевые слова еще медленнее. Кроме того, это очень читабельно.
В качестве альтернативы вы можете рассмотреть возможность использованияMap
вместоObject
,потому что,Map.prototype.delete
Сравниватьdelete
Также гораздо быстрее.
Do It Later
Если вы не можете оптимизировать 3 вышеуказанных аспекта, вы также можете попробовать четвертый тип оптимизации, даже если время выполнения будет таким же, вы почувствуете, что код работает быстрее. Это включает в себя рефакторинг кода, чтобы менее монолитные или ресурсоемкие задачи не блокировали выполнение самого важного кода.
12. Используйте асинхронный код, чтобы избежать блокировки потока
По умолчанию JavaScript является однопоточным и выполняет код синхронно. (На самом деле код браузера может запускать несколько потоков для захвата событий и обработчиков срабатывания, но что касается написания кода JavaScript, он является однопоточным.)
Синхронное исполнение в основном является кодом JavaScript, но если нам нужно выполнить код, займет много времени, мы не хотим блокировать другое более важное выполнение кода.
Нам нужно использовать асинхронный код. картинаfetch()
илиXMLHttpRequest()
Эти встроенные методы принудительно выполняются асинхронно. Стоит отметить, что любую синхронную функцию можно сделать асинхронной: если вы выполняете трудоемкую синхронную операцию, такую как работа с каждым элементом в большом массиве, вы можете сделать этот код асинхронным, чтобы он не блокировал выполнение другого кода. .
Кроме того, в NodeJ многие модули имеют как синхронные, так и асинхронные методы, например,fs.writeFile()
а такжеfs.writeFileSync()
. В обычных условиях используйте асинхронные методы по умолчанию.
13. Используйте разделение кода
Если вы пишете JavaScript в своем браузере, вы должны указать приоритет, чтобы убедиться, что ваша страница отображается, тем лучше. «Первый рендеринг экрана» - это ключевой индикатор для измерения первого действительного времени интерфейса вашего браузера.
Лучший способ улучшить это — разделить код JavaScript. Вместо того, чтобы упаковывать весь код вместе, его можно разделить на более мелкие фрагменты, чтобы можно было предварительно загрузить меньше кода JavaScript. В зависимости от используемого вами движка метод разделения кода отличается.
Tree-Shaking
это стратегия отсеивания бесполезного кода из кодовой базы, вы можете прочитать эту статьюtree-shakingпознакомиться с ним.
Суммировать
Лучший способ убедиться, что ваши стратегии оптимизации работают, — это протестировать их, что я и использую в статье.Чтобы проверить производительность, вы также можете попробовать:
- jsben.ch/
- jsbench.me/
-
console.time
а такжеconsoele.timeEnd
Панели «Производительность» и «Сеть» в инструментах разработчика Chrome — отличный инструмент для проверки производительности веб-приложений, и я также рекомендую использоватьLightHouse.
В конце концов, хотя скорость и важна, дело не только в хорошем коде. Читабельность и ремонтопригодность также важны, и если для поиска ошибки и ее исправления для небольшого прироста производительности требуется больше времени, оно того не стоит.
обо мне
Я занимаюсь переносом кода без эмоций. Каждую неделю я буду обновлять от 1 до 2 статей, связанных с интерфейсом. Если вам интересно, вы можете отсканировать приведенный ниже QR-код, чтобы подписаться или выполнить поиск непосредственно в WeChat.前端补习班
Сфокусируйся на.
Трудно быть опытным во фронтенде, давайте делать уроки вместе!
Хорошо, перевод завершен, оригинальная ссылка здесь13 Tips to Write Faster, Better-Optimized JavaScript.