Подтвердить что ты не робот

Производительность закрытия Javascript

Я работаю некоторое время в javascript и обычно делаю что-то вроде этого только для кэширования значений свойств функций, объявленных внутри глубокой структуры или "пространства имен"

//global scope
(function ($, lib) {
  //function scope 1
  var format = lib.format, // instead of calling lib.format all the time just call format
    touch = lib.pointer.touch, //instead of calling lib.pointer.touch each time just touch
    $doc = $(document),
    log = logger.log; //not console log...


  $doc.on('app:ready', function () {
    //function scope 2
    $doc.on('some:event', function (e) {
      //function scope 3
      //use the cached variables
      log(format('{0} was triggered on the $doc object', e.type);
    });

    $doc.on(touch, function (e) {
      //function scope 3
      log(format('this should be {1} and is... {0} ', e.type, touch);
    });
  }); 
}(jQuery, lib));

Я делал это, потому что:

  • как ленивый, как я, писать прикосновение кажутся более привлекательными, поскольку писать lib.pointer.touch, даже когда мощные IDE с фантастическим автозаполнением могут помочь в этом, коснуться короче.
  • минимизатор может преобразовать эту единственную приватную переменную в одну буквенную переменную, поэтому она также имеет смысл для меня (я знаю, я знаю, никогда не оптимизирую слишком рано, но это кажется безопасным, я думаю)

Весь написанный таким образом код, по-видимому, работает прилично на мобильных устройствах и настольных браузерах, поэтому кажется, что это безопасная практика (в "практике", каламбур). Но я с тех пор полагаюсь на замыкания, а внутренние функции должны создавать замыкание, чтобы сохранить контекст, который был объявлен мне интересно...

  • если функция не использует переменные из внешнего контекста (свободные переменные)... является ли контекст закрытия еще сохраненным? (или если дерево дерева падает в лес, и никто не слышит его, он все еще делает звук аварии? hehe) Я знаю, что это может варьироваться в зависимости от движка javascript, потому что ECMA ничего не говорит о том, требуется ли сохранить контекст или нет, когда переменные извне не доступны.

  • если указанное выражение истинно... будет ли этот блок кода более эффективным?

    //global scope
    (function ($, lib) {
      //function scope 1
      var format = lib.format,
        touch = lib.pointer.touch,
        $doc = $(document),
        log = console.log;
    
      $doc.on('app:ready', function () {
    
        (function ($doc, touch, lib, format) {
          // since all the variables are provided as arguments in this function
          // there is no need to save them to the [[scope]] of this function
          // because they are local to this self invoking function now
          $doc.on('some:event', function (e) {
            //function scope 3
            //use the cached variables
            log(format('{0} was triggered on the $doc object', e.type);
          });
    
          $doc.on(touch, function (e) {
            //function scope 3
            log(format('this should be {1} and is... {0} ', e.type, touch);
          });
        }($doc, touch, lib, format));      
    
      }); 
    
    }(jQuery, lib));
    

Является ли он более эффективным, потому что он передает эти переменные функции непосредственного вызова? будут ли затраты на создание этой новой функции какое-либо влияние на код, (отрицательный или положительный)?

  • Как я могу правильно измерить потребление памяти в моей javascript-библиотеке надежным способом? У меня есть 100x маленьких модулей javascript, которые находятся внутри непосредственных функций самозапуска, в основном, чтобы избежать переполнения переменных в глобальном контексте. Таким образом, все они завернуты в модули, очень похожие на блок кода, упомянутый выше.

  • будет ли он лучше влиять на кеширование переменных ближе, даже если это будет означать, что мне придется повторять объявления переменных ближе к тому, где они будут использоваться?

  • У меня такое чувство, что для поиска переменной не в текущем локальном контексте, механизм сначала рассмотрит родительскую область и перечислит все переменные на этом уровне... чем больше переменных на уровень, тем хуже вероятно, будет производительностью, ища переменную.

  • попытаться найти переменную undefined из внутренних замыканий будет самой дорогой, потому что по определению переменная будет первым поиском в родительской области до тех пор, пока глобальная область действия, и не найдя ее, заставит двигатель наконец, достичь глобального масштаба. Это правда? являются ли механизмы, оптимизирующие этот вид поиска?

В конце... Я знаю, что я не хочу использовать свой код в качестве второго примера в основном потому, что он сделает код более трудным для чтения, и я любезно согласен с минимальным размером финального вывода, используя первый подход. Мой вопрос мотивирован любопытством и попытаться понять немного лучше эту действительно приятную особенность javascript.

В соответствии с этим тестом...

http://jsperf.com/closures-js

Кажется, второй подход быстрее. Но это проявляется только при повторении безумного количества раз... Прямо сейчас мой код не выполняет такое количество итераций... но, вероятно, потребляет больше памяти из-за моего способа кодирования...

update: он указал мне, что этот вопрос слишком велик. Извините, я попытаюсь сломать мелкие детали. Этот вопрос был мотивирован в основном любопытством, как я сказал, производительность кажется незначительной даже на мобильных устройствах. Благодарим вас за все ваши отзывы.

4b9b3361

Ответ 1

Я думаю, что это преждевременная оптимизация. Вы уже знаете, что производительность не является проблемой в большинстве случаев. Даже в плотных петлях производительность не ухудшает это плохо. Пусть JavaScript-движок оптимизирует это самостоятельно, поскольку Chrome начал делать, удалив ненужные переменные из закрытий.

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

Если вы добавите точку останова в следующий код (в Chrome), вы увидите, что переменная world была оптимизирована из закрытия, посмотрите на "Closure" node в разделе "Переменные области" http://jsfiddle.net/4J6JP/1/

enter image description here.

(function(){
   var hello = "hello", world="world";
    setTimeout(function(){
        debugger;
        console.log(hello);
    });
})()

Обратите внимание, что если вы добавите eval в эту внутреннюю функцию, тогда все ставки будут отключены, а закрытие невозможно оптимизировать.

Ответ 2

Существует стоимость производительности при создании областей и контекстов функций, добавление лишней детской области имеет стоимость, однако в этом случае она пренебрежимо мала. Итерирование только тех объектов или переменных, которые вам нужны во время работы, будет быстрее, чем преодоление лексической области и поиск там. Хорошая статья о функциях вложенности приведена здесь: http://code.tutsplus.com/tutorials/stop-nesting-functions-but-not-all-of-them--net-22315

Если я могу получить ссылку и назначить ее var внутри лексической области, я сделаю это, но нужно быть осторожным, потому что даже после того, как родительская функция будет уничтожена, возвращаемая функция или объект могут получить доступ к этим ссылкам для "long.lib.format" с сокращенным "форматом", потому что они были объявлены внутри одной и той же лексической области, если вы больше не используете ссылку, вам нужно будет уничтожить все пути в этой области. Это печально, но в это время я понимаю, что мы не можем удалить "ссылку" в этом случае "var format", поскольку тот же самый lib ссылается в другом месте, поэтому "delete format" не разрешен, однако вы можете удалить участников на объекте /lib он ссылается. Вы можете var локальный объект для хранения ссылок и удаления их, когда они больше не нужны, а затем уничтожить этот объект, не теряя других членов лексической области или области содержимого

Обновление: недавно выяснилось, что использование delete в javascript может быть медленным, скорее назначьте члену {}, когда он больше не используется.