Возможно ли получить доступ к частным переменным с областью конструктора из прототипа функций?
основываясь на моем понимании javascript, методы прототипа не могут получить доступ к переменным, которые являются частными для области конструктора,
var Foo = function() {
var myprivate = 'I am private';
this.mypublic = 'I am public';
}
Foo.prototype = {
alertPublic: function() { alert(this.mypublic); } // will work
alertPrivate: function() { alert(myprivate); } // won't work
}
это имеет смысл, но есть ли способ обойти это, что является безопасной и хорошей практикой? Поскольку использование прототипов обеспечивает преимущество производительности в том, что функции-члены выделяются только один раз, я хотел бы достичь аналогичной функциональности, все еще имея возможность добраться до моих частных переменных. Не думаю, что это сработает. использование прототипа, но есть ли другой шаблон, такой как заводской метод или подход закрытия? Что-то вроде
var fooFactory = function() {
var _alertPrivate = function(p) { alert(p); } // bulk of the logic goes here
return function(args) {
var foo = {};
var myprivate = args.someVar;
foo.mypublic = args.someOtherVar;
foo.alertPrivate = function() { _alertPrivate(myprivate); };
return foo;
};
}
var makeFoo = new fooFactory();
var foo = makeFoo(args);
Я не уверен, создается ли новая копия _alertPrivate каждый раз, когда я создаю новый Foo, или есть ли какая-либо потенциальная выгода от производительности. Цель состоит в том, чтобы получить функциональность, подобную прототипированию (поскольку это экономит память), все еще имея доступ к частным переменным.
спасибо.
3 ответов
Я придумал следующий шаблон для решения этой проблемы, по крайней мере сейчас. Мне нужен был привилегированный сеттер, чтобы частная переменная могла быть изменена изнутри определенных функций-прототипов, но не откуда-либо еще:
var Foo = (function() {
// the bulk of the objects behavior goes here and is created once
var functions = {
update: function(a) {
a['privateVar'] = "Private variable set from the prototype";
}
};
// the objects prototype, also created once
var proto = {
Update: function() {
this.caller('update');
}
};
// special function to get private vars into scope
var hoist = function(accessor) {
return function(key) {
return functions[key](accessor());
}
}
// the constructor itself
var foo = function foo() {
var state = {
privateVar: "Private variable set in constructor",
// put more private vars here
}
this.caller = hoist(function(){
return state;
});
}
// assign the prototype
foo.prototype = proto;
// return the constructor
return foo;
})();
в основном указатель на внутреннее состояние объектов поднимается к его прототипу через закрытие над простой функцией доступа () { return state;}. Использование функции 'caller' в любом данном экземпляре позволяет вызывать функции, которые создаются только один раз, но все еще могут ссылаться на частное состояние, хранящееся в этом экземпляре. Также важно отметить, что никакие функции вне прототипа не могут получить доступ к привилегированному методу доступа, поскольку "вызывающий" принимает только ключ, который ссылается на предопределенные функции, которые находятся в области.
вот некоторые критерии этого метода, чтобы увидеть, как он сравнивает к чистому прототипирования. Эти цифры представляют создание 80 000 экземпляров объекта в цикле (обратите внимание на объект используемый для бенчмаркинга более сложный, чем приведенный выше, который был только для упрощения):
CHROME:
только закрытие-2172ms
прототипирование (выше путь) - 822ms
прототипирование (путь std) - 751ms
FIREFOX:
только закрытие-1528ms
прототипирование (выше путь) - 971ms
прототипирование (путь std) - 752ms
Как видите, этот метод почти так же быстро, как обычно прототипирование и определенно быстрее, чем просто использование обычного закрытия, которое копирует функции вместе с экземпляром.
Я нашел ответ Шона Томана очень полезным (хотя сначала трудно понять).
не похоже, что общественный сеттер может принять значение для privateVar
поэтому я сделал несколько Щипков:
изменить update
на functions
:
update: function(st, newVal) {
st['privateVar'] = newVal;
}
изменить Update
на proto
:
Update: function(newVal) {
this.caller('update', newVal);
}
изменить hoist
:
var hoist = function(accessor) {
return function(key) {
// we can't slice arguments directly because it's not a real array
var args_tail = Array.prototype.slice.call(arguments, 1);
return functions[key].apply(functions[key], [accessor()].concat(args_tail));
}
}
то, что вы просите, возможно, хотя всегда будет компромисс между производительностью (в скорости или памяти) и функциональностью.
В JavaScript возможно для достижения частного состояния на экземпляр, с обычными методами прототипа (и без централизованного, утечки, хранения поля).
проверяем статье я писал о технике: http://www.codeproject.com/KB/ajax/SafeFactoryPattern.aspx
или go непосредственно к исходному коду в:https://github.com/dcleao/private-state.