Производительность Javascript с закрытием - PullRequest
15 голосов
/ 07 июля 2011
var name = function(n) {
    var digits = ['one','two','three','four'];
    return digits[n];
}

var namenew = (function() {
    digits = ['one','two','three','four'];
    return function(n) {
        return digits[n];
    }
}());

Обе версии приводят к одному и тому же выводу, однако говорят, что вторая версия намного быстрее, чем первая.

Как я понимаю, первая версия выполняет функцию каждый раз, когда во второй версии хранится результат выполнения. Это то, что смущает меня как функционального / обычного программиста OOPS.

Как сохранить функцию с ее внутренним контекстом? Что происходит под капотом? Может кто-нибудь, пожалуйста, уточнить?

Ответы [ 2 ]

13 голосов
/ 07 июля 2011

Реальный ответ на этот вопрос будет около 3 страниц. Но я стараюсь сделать это как можно короче. ECMA- / Javascript это все о Execution Contexts и Object. В ECMAscript есть три основных типа контекста: Global context, Function contexts и eval contexts.

Каждый раз, когда вы вызываете функцию, ваш движок порождает ее в своем собственном function context. Кроме того, существует такой объект, который называется Activation object. Этот мистический объект является частью function context, который состоит как минимум из:

  • [[Scope chain]]
  • Объект активации
  • значение контекста "this"

Может быть больше свойств на разных движках, но эти три требуются для любой реализации ES. Однако вернемся к теме. Если вызывается контекст функции, все parent contexts (или, точнее, Activation objects из родительского контекста) копируются в свойство [[Scope]]. Вы можете думать об этом свойстве как о массиве, который содержит (Activation-) Objects. Теперь любая относящаяся к функции информация хранится в объекте активации (формальные параметры, переменные, объявления функций).

В вашем примере переменная digits хранится в объекте активации для namenew. Второй, когда создается внутренняя анонимная функция, она добавляет это Activation object в свои свойства [[Scope]]. Когда вы вызываете digits[n] там, Javascript сначала пытается найти эту переменную в своем собственном объекте активации. Если это не удается, поиск переходит в Scopechain. И вуаля, там мы нашли переменную, потому что мы скопировали АО из внешней функции.

Я уже написал слишком много для короткого ответа, но чтобы действительно дать хороший ответ на такой вопрос, вам нужно объяснить некоторые базовые знания об ES здесь. Полагаю, этого достаточно, чтобы дать вам представление о том, что на самом деле происходит «под капотом» (еще многое нужно знать, если вы хотите больше читать, я дам вам несколько ссылок).


Вы просили об этом, вы получаете это:

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

9 голосов
/ 07 июля 2011

Первая функция воссоздает digits каждый раз, когда она выполняется. Если это большой массив, это излишне дорого.

Вторая функция сохраняет digits в контексте, доступном только для namenew. Каждый раз, когда namenew выполняется, он выполняет только одну операцию: return digits[n].

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

В перспективе ООП использование замыкания подобным образом аналогично хранению данных в статической переменной.


Не забывайте, что namenew получает результат функции закрытия. Само замыкание выполняется только один раз .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...