В JavaScript нам следует как можно чаще использовать локальные переменные вместо глобальных. Все знают это предложение, но кто сказал его первым? Зачем это делать? Есть ли для этого какое-либо основание? Если вы этого не сделаете, насколько сильно это повлияет на производительность? В этой статье будут рассмотрены ответы на эти вопросы и фундаментальное понимание того, какие факторы связаны с производительностью чтения и записи переменных.
【Исходное】Производительность переменных JavaScript
【Автор】Николас С. Закас
[Перевод] Почему в JavaScript мы должны использовать локальные переменные, когда это возможно?
[Переводчик] Mingda
Ниже приводится перевод оригинального текста:
По вопросу о том, как улучшить производительность JavaScript, наиболее часто слышимым предложением является использование локальных переменных вместо глобальных. Это совет, который запомнился мне и никогда не подвергался сомнению за девять лет работы в веб-разработке, и он основан на методе обработки области видимости и идентификатора (разрешения идентификаторов) в JavaScript.
Прежде всего, нам нужно прояснить, что функции в JavaScript воплощаются в виде объектов. Процесс создания функции на самом деле является процессом создания объекта. Каждый объект функции имеет внутреннее свойство под названием [[Scope]], которое содержит информацию об области действия на момент создания функции. Фактически атрибут [[Scope]] соответствует списку объектов (Variable Objects), и к объектам в списке можно получить доступ изнутри функции. Например, если мы создаем глобальную функцию A, то внутреннее свойство [[Scope]] A содержит только один глобальный объект (Global Object), а если мы создаем новую функцию B в A, то атрибут B [[Scope]] содержит два объекта: объект активации функции A находится спереди, а глобальный объект (глобальный объект) — сзади.
При выполнении функции автоматически создается исполняемый объект (Объект выполнения) и привязывается к цепочке областей действия (Цепочка областей действия). Цепочка областей будет установлена с помощью следующих двух шагов разрешения идентификатора.
1. Сначала скопируйте объекты во внутренних свойствах функционального объекта [[Scope]] в цепочку областей по порядку.
2. Во-вторых, при выполнении функции будет создан новый объект объекта активации. Этот объект содержит его определения, параметры (аргументы) и локальные переменные (включая именованные параметры). . Передняя часть доменной цепочки.
Во время выполнения кода JavaScript, когда встречается идентификатор, он будет искаться в цепочке контекста выполнения (контекст выполнения) на основе имени идентификатора. Начиная с первого объекта в цепочке областей (объект активации функции), если он не найден, выполните поиск следующего объекта в цепочке областей и так далее, пока не будет найдено определение идентификатора. Если последний объект в области видимости, который является глобальным объектом, не найден после поиска, будет выдано сообщение об ошибке, сообщающее пользователю, что переменная не определена. Это модель выполнения функции и процесс разрешения идентификаторов (Identifier Разрешение), описанный в стандарте ECMA-262. Оказывается, большинство механизмов JavaScript действительно реализованы таким образом. Следует отметить, что ECMA-262 не требует использования этой структуры, а лишь описывает эту часть функции.
Поняв процесс разрешения идентификаторов (Разрешение идентификаторов), мы можем понять, почему локальные переменные разрешаются быстрее, чем переменные в других областях, главным образом потому, что процесс поиска значительно сокращается. Но насколько это будет быстрее? Чтобы ответить на этот вопрос, я смоделировал серию тестов для проверки производительности переменных на разной глубине области видимости.
Первый тест — записать в переменную простейшее значение (здесь используется буквальное значение 1). Результат показан на рисунке ниже, что очень интересно:
Из результатов нетрудно увидеть, что когда процесс синтаксического анализа идентификатора требует глубокого поиска, произойдет потеря производительности, причем степень потери производительности будет увеличиваться с увеличением глубины идентификатора. Неудивительно, что Internet Explorer показал худшие результаты (но, честно говоря, в IE 8 были некоторые улучшения). Стоит отметить, что здесь есть некоторые исключения: Google Chrome и последняя полночная версия WebKit имеют очень стабильное время доступа к переменным и не увеличиваются с увеличением глубины области видимости. Конечно, это следует отнести к используемым ими движкам JavaScript следующего поколения: V8 и SquirrelFish. Эти механизмы выполняют оптимизацию при выполнении кода, и очевидно, что эти оптимизации делают доступ к переменным быстрее, чем когда-либо прежде. Opera также показала хорошие результаты: она намного быстрее, чем IE, Firefox и текущая версия Safari, но медленнее, чем браузеры на базе V8 и Squirrelfish. Производительность Firefox 3.1 Beta 2 немного неожиданна. Эффективность выполнения локальных переменных очень высока, но по мере увеличения количества уровней области действия эффективность значительно снижается. Следует отметить, что здесь я использую настройки по умолчанию, а это означает, что в Firefox не включена функция Trace.
Приведенные выше результаты были получены путем выполнения операций записи над переменными. На самом деле мне было любопытно, будет ли ситуация отличаться при чтении переменных, поэтому я провел следующий тест. Было обнаружено, что скорость чтения немного выше скорости записи, но тенденция изменения производительности постоянна.
Как и в предыдущем тесте, Internet Explorer и Firefox по-прежнему были самыми медленными, а Opera показала очень привлекательную производительность. Точно так же Chrome и последняя версия Webkit Midnight Edition продемонстрировали тенденции производительности, которые не имеют ничего общего с глубиной области видимости. Да, время доступа к переменной в Firefox 3.1 Beta 2 все еще имеет странный скачок с глубиной.
В ходе теста я обнаружил интересное явление, заключающееся в том, что Chrome будет иметь дополнительные потери производительности при доступе к глобальным переменным. Время доступа к глобальным переменным не имеет ничего общего с уровнем области видимости, но оно будет на 50% больше, чем время доступа к локальным переменным того же уровня.
Какое просветление могут принести нам эти два испытания? Первый — проверить старую точку зрения, которая заключается в максимальном использовании локальных переменных. Во всех браузерах доступ к локальным переменным происходит быстрее, чем доступ к переменным в разных областях, включая глобальные переменные. Следующие моменты должны отражать опыт, полученный в ходе этого теста:
* Тщательно проверьте все переменные, используемые в функции. Если есть переменная, которая не определена в текущей области видимости и используется более одного раза, то мы должны сохранить эту переменную в файле. локальная переменная, используйте эту локальную переменную для выполнения операций чтения и записи. Это может помочь нам уменьшить глубину поиска переменных вне области видимости до 1. Это особенно важно для глобальных переменных, поскольку глобальные переменные всегда ищутся в последней позиции цепочки областей видимости.
* Избегайте использования оператора with. Потому что он изменит цепочку областей контекста выполнения (контекст выполнения) и добавит объект (объект переменной) спереди. Это означает, что во время выполнения with фактические локальные переменные перемещаются на вторую позицию в цепочке областей видимости, что приведет к потере производительности.
* Если вы уверены, что какой-то фрагмент кода обязательно выдаст исключение, избегайте использования try-catch, поскольку ветвь catch обрабатывается в той же цепочке областей действия, что и with. Однако в коде ветки try нет потери производительности, поэтому все равно рекомендуется использовать try-catch для перехвата непредсказуемых ошибок.
Если вы хотите подробнее обсудить эту тему, я выступил с небольшим докладом на встрече JavaScript в Маунтин-Вью в прошлом месяце. Вы можете скачать слайды на SlideShare или посмотреть полное видео вечеринки, которое начинается примерно на 11-й минуте моего выступления.
Примечание переводчика:
Если при чтении этой статьи у вас возникнут какие-либо сомнения, советую вам прочитать следующие две статьи:
* «Объектная модель JavaScript — модель выполнения», написанная Ричи.
* «ECMA-262 Третье издание», в основном смотрите главу 10, которая представляет собой контекст выполнения. Там подробно объясняются термины, упомянутые в этой статье.
В конце Николас упомянул о встрече по JavaScript в Маунтин-Вью. Веб-сайт Meetup на самом деле представляет собой веб-сайт организации для различных реальных мероприятий. Чтобы получить к нему доступ, нужно обойти брандмауэр. Жизнь в Калифорнии — это действительно благословение. мероприятия для участия. хе-хе.