Возможно ли получить доступ к частным переменным с областью конструктора из прототипа функций?

основываясь на моем понимании 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.