Jump to content
  • 0

Замыкания и глобальные функции


s0rr0w
 Share

Question

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

Какой выигрыш у замыканий в больших проектах с огромным количеством разных модулей? Синтаксис становится немного более сложным...

Link to comment
Share on other sites

17 answers to this question

Recommended Posts

  • 0

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

Таким образом, чистота глобального контекста имеет смысл в том ключе, что при увеличении числа переменных увеличивается вероятность пересечения имён и как результат - получения неприятных багов.

Link to comment
Share on other sites

  • 0

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

Link to comment
Share on other sites

  • 0
теоретически и в большинстве случаев практически

Можно подробнее? При 15000 глобальных переменных у меня тесты показывали разницу меньше доступной для измерения (т.е. 0ms).

Вопрос был только в скорости загрузки подготовленного файла или времени генерации этих самых переменных.

Link to comment
Share on other sites

  • 0

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

На практике, скорее всего, эффективность этого непосредственно зависит от используемого интерпретатора.

Link to comment
Share on other sites

  • 0
Таким образом, чистота глобального контекста имеет смысл в том ключе, что при увеличении числа переменных увеличивается вероятность пересечения имён и как результат - получения неприятных багов.

Это решается примитивным префиксом перед именем функции. Для пейджера PGR_foo, для таббера TAB_foo и так далее.

Link to comment
Share on other sites

  • 0

К слову о замыканиях. Если мы используем замыкания, как способ программирования. (Замыкания) Нам всегда надо держать в уме три вещи: область действия переменных, время их жизни, смысл того где мы делаем замыкание.

Например

function createMyObject () {
var localVariable = "local";
this.alert = function () {return localVariable}
}

MyObject = new createMyObject ();

По сути дела память под конструктор не освободиться после создания объекта. Потому что мы использовали замыкание со ссылкой на переменную вне области действия анонимной функции. Все в порядке когда таких конструкторов мало. Но если их 1000 или более? то уже начинаются проблемы с памятью, выделяемой под приложение. Может наступить момент переполнения.

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

Более правильный код первого примера

function createMyObject () {
this.localVariable = "local";
this.alert = function () {return this.localVariable}
}

MyObject = new createMyObject ();

Link to comment
Share on other sites

  • 0
Какой объект занимает память в первом случае и не занимает во втором?

Для пояснения приведу расширенный пример

function createMyObject (inp) {
var localVariable = {value: inp};
this.alert = function () {return localVariable.value}
}

MyObject0 = new createMyObject ('local1');
MyObject1 = new createMyObject ('local2');
alert([MyObject0.alert(), MyObject1.alert()]);

Теперь у нас в динамической памяти два экземпляра конструктора и по одному экземпляру объекта. Итого 4 объекта. каждому нужна память, для хранения своих свойств.

function createMyObject (inp) {
this.localVariable = {value: inp};
this.alert = function () {return this.localVariable.value}
}

MyObject0 = new createMyObject ('local1');
MyObject1 = new createMyObject ('local2');
alert([MyObject0.alert(), MyObject1.alert()]);

Конструктор переходит в неактивное состояние. Память для хранения его переменных не требуется. Он отработал свое и лежит формально, пока не будет вызван.

Link to comment
Share on other sites

  • 0
Для пояснения приведу расширенный пример

function createMyObject (inp) {
var localVariable = {value: inp};
this.alert = function () {return localVariable.value}
}

MyObject0 = new createMyObject ('local1');
MyObject1 = new createMyObject ('local2');
alert([MyObject0.alert(), MyObject1.alert()]);

Теперь у нас в динамической памяти два экземпляра конструктора и по одному экземпляру объекта. Итого 4 объекта. каждому нужна память, для хранения своих свойств.

Экземпляры на то и экземпляры, чтобы быть единственными в своём роде. Конструктор (createMyObject) как был один, так и есть один, объект {value:inp}, как и метод alert создаются при каждом вызове конструктора.

Link to comment
Share on other sites

  • 0
Конструктор переходит в неактивное состояние. Память для хранения его переменных не требуется. Он отработал свое и лежит формально, пока не будет вызван.

Там у вас функция вложенная нарисована, всё, что нужно она "запомнит" непосредственно при своём создании, есть переменные выше (в контексте конструктора, в глобальном контексте...) или их вообще нет - ей не интересно, она сохраняет Scope и всё.

Link to comment
Share on other sites

  • 0
Экземпляры на то и экземпляры, чтобы быть единственными в своём роде. Конструктор (createMyObject) как был один, так и есть один, объект {value:inp}, как и метод alert создаются при каждом вызове конструктора.

Zeroglif, конечно я могу ошибиться. Поправьте меня если что-то не так.

Функция изначально до ее вызова существует формально. Как только к ней идет обращение, происходит выделение памяти под ее локальные переменные, копирование значений входных параметров или передача их по ссылке. далее происходит выполнение операций внутри тела функции и возвращение результата. После того как функция отработала ее переменные обнуляются и удаляются из динамической памяти. Функция переходит в формальное состояние. И так до следующего вызова. То есть function () {} это на самом деле шаблон, по которому в процессе выполнения скрипта создаются экземпляры функции.

Что я вижу в приведенном коде. Я вызываю конструктор. Директива new создает пустой объект. Вызывается функция сreateMyObject. ей передается по значению параметр inp и this - как указатель на пустой объект. В функции сreateMyObject локальная переменная функции localVariable приводиться автоматически к типу Object. в этот локальный объект внутри функции свойству value присваивается значение inp. Далее у пустого объекта (созданного директивой new) создается метод alert по принципу замыкания и это замыкание указывает на локальный объект функции сreateMyObject (причем вполне конкретный). Собственно все- объект MyObject0 создан. И размещен в памяти. Но экземпляр сreateMyObject не может быть удален из памяти, потому что на него ссылается MyObject0, точнее на локальную переменную localVariable функции сreateMyObject с конкретным значением.

То же самое происходит во второй раз. Итого 2 + 2 = 4 объекта в памяти. Или я не прав?

Link to comment
Share on other sites

  • 0
Там у вас функция вложенная нарисована, всё, что нужно она "запомнит" непосредственно при своём создании, есть переменные выше (в контексте конструктора, в глобальном контексте...) или их вообще нет - ей не интересно, она сохраняет Scope и всё.

Не. Еслиб так было.... В Java Script насколько я помню в 99% случаев параметр передается по ссылке, а не по значению.

function createMyObject (inp) {
this.localVariable = {value: inp};
this.alert = function () {return this.localVariable.value}
}

MyObject0 = new createMyObject ('local1');
MyObject1 = new createMyObject ('local2');
alert([MyObject0.alert(),MyObject1.alert()]);
MyObject1.localVariable.value = "valueNext";
alert([MyObject0.alert(),MyObject1.alert()]);

Link to comment
Share on other sites

  • 0
В JavaScript насколько я помню в 99% случаев параметр передается по ссылке, а не по значению.

Параметр передаётся по значению.

Но экземпляр сreateMyObject не может быть удален из памяти, потому что на него ссылается MyObject0, точнее на локальную переменную localVariable функции сreateMyObject с конкретным значением.

Функция 'createMyObject' живёт сама по себе, её уже держит несколько объектов - прототипы созданных объектов (через свойство 'constructor'), variable object того контекста, где этот конструктор создан (через window.createMyObject). Наличие переменных или параметров внутри внешней функции не влияет на то, как вложенная функция "запоминает" Scope, она это делает в любом случае, пусто там или не пусто.

Edited by Zeroglif
Link to comment
Share on other sites

  • 0

2 Zeroglif, да точно. спасибо. заодно пересмотрел манулы. мой пример не в тему.

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

Link to comment
Share on other sites

  • 0
2 Zeroglif, да точно. спасибо. заодно пересмотрел манулы. мой пример не в тему

Он может быть в тему, если с другой стороны взглянуть - с точки зрения внимательного отношения к вложенным функциям, пережившим контекст в котором они были созданы. Часто бывает, что во внешней функции куча локальных данных, но замыканию нужна только часть из них, остальное удерживается просто так, заодно, т.к. замыкание "помнит" не значения, не отдельные свойства, а Scope целиком. Трудно сказать, что движки делают с "как-бы-не-нужными" замыканию свойствами, оптимизируют или нет.

Link to comment
Share on other sites

  • 0
Трудно сказать, что движки делают с "как-бы-не-нужными" замыканию свойствами, оптимизируют или нет.

Скорее всего ничего не делают. Это нарушает целостность данных.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. See more about our Guidelines and Privacy Policy