Являются ли замыкания в javascript перекомпилированы
Скажем, у нас есть этот код (забудьте о прототипах на мгновение):
function A(){
var foo = 1;
this.method = function(){
return foo;
}
}
var a = new A();
- внутренняя функция, перекомпилированная каждый раз, когда выполняется функция A? Или лучше (и почему) сделать это вот так:
function method = function(){ return this.foo; }
function A(){
this.foo = 1;
this.method = method;
}
var a = new A();
Или javascript-механизмы достаточно умны, чтобы не создавать новую функцию "метод" каждый раз? В частности, Google v8 и node.js.
Кроме того, любые общие рекомендации о том, когда использовать эту технику, приветствуются. В моем конкретном примере мне действительно нравится использовать первый пример, но я знаю, что внешняя функция будет создаваться многократно.
Ответы
Ответ 1
Из того, что я понимаю, речь идет не столько о "компиляции" функции, сколько о том, что она имеет разные "области" каждый раз, когда она выполняется.
Второй метод, который вы использовали, всегда будет иметь method
из той же области.
Первый метод помещает method
в область вызова функции A()
. Таким образом, любая информация, находящаяся внутри этой области (var foo
, параметры функции и т.д.), Хранится в этом экземпляре области видимости функций. Таким образом, один и тот же функциональный код будет ссылаться каждый раз, но он будет в другой области (и, следовательно, другой "объект" ).
Ответ 2
Да, вы создаете новый объект Function при каждом экземпляре объекта A. Вы можете продемонстрировать это следующим образом:
function A(){
var foo = 1;
this.method = function(){
return foo;
}
}
var a = new A();
var b = new A();
alert(a.method == b.method); // Returns false; two different Function objects
Если вы хотите повторно использовать один и тот же объект Function, сделайте метод свойством прототипа, а не экземплярами.
function B() {
this.foo = 1;
}
B.prototype.method = function() {
return this.foo;
}
var a = new B();
var b = new B();
alert(a.method == b.method); // Returns true; it the same Function object
Изменить: насколько я знаю, нет причин делать что-то вроде первой версии, кроме создания частных переменных в объекте JavaScript. В исходном примере foo
является закрытым. Ничто не может получить к нему доступ непосредственно извне объекта. К сожалению, когда вы создаете большое количество объектов, используя эту технику, это может повлиять на производительность и объем памяти.
В моем коде я использую соглашение об именах, чтобы различать свойства "public" и "private". Я называю частные свойства с подчеркиванием в качестве первого символа. Поэтому, если я вижу что-то вроде myObject._someMethod()
, я знаю, что что-то не так.
Edit2: Из http://code.google.com/apis/v8/design.html я бы подумал, что V8 компилирует замыкание один раз, когда он создает скрытый класс, который содержит method
.
Ответ 3
Метод не перекомпилирован.
Интерпретатор Javascript создаст новый объект замыкания, содержащий внутренние методы и локальные переменные, каждый раз, когда вы вызываете внешние методы.
Точная реализация зависит от механизма Javascript.
Ответ 4
Я бы предположил, что он компилируется только один раз... потому что ключевое слово "this" относится к контексту выполнения... поэтому компилятору не нужно много знать о функции для его интерпретации.
Кроме того, когда вы объявляете переменную в нижней части функции, она все еще доступна вверху:
function test()
{
alert(hello);
// ...
var hello = 2;
}
Это то же самое с функциями. Я бы поверил с этими двумя вещами в виду, что он не перекомпилируется каждый раз, когда вызывается A.
Mike
Ответ 5
Представьте себе функцию как еще один объект, а затем, когда вы создаете новую, это просто копия старой, с измененной переменной. Вам не нужно повторно анализировать источник объекта, чтобы это произошло. Хорошая аналогия - это функциональные функции в С++, объекты функций в Lua, и я действительно не знаю многих других языков.
Ответ 6
function A(){
var foo = 1;
this.method = function(){
return foo;
}
}
не может быть скомпилирован в
function method = function(){ return this.foo; }
function A(){
this.foo = 1;
this.method = method;
}
потому что функциональность изменится. Первый пример имеет переменную "foo", которая видна только конструктору "A" и функции "метод", тогда как во втором примере доступно все, имеющее доступ к экземпляру "A".
Его можно скомпилировать в
function method = function(){ return 1; }
function A(){
this.method = method;
}
Но я не думаю, что какой-нибудь механизм javascript там зайдет так далеко, но если вы сможете предварительно обработать ваши файлы и готовы приложить дополнительные усилия, компилятор закрытия google может пойти довольно далеко, если он используется в расширенном режиме.