Как создать шаблон создания Javascript с помощью методов многократного использования и частных свойств?

Учитывая шаблоны создания объектов с частными свойствами, один из способов:

function MyStack (){
    var list = [],
        index = 0;

    this.push =  function(val){ 
        return list[index++] = val;
    };
    this.pop = function(){// ...}
}

var stack1 = new MyStack();       stack1.push(5);
var stack2 = new MyStack();       stack2.push(11);

Проблема с этим: у каждого экземпляра Stack есть своя копия методов push и pop.

Другой способ реализации метода-конструктора:

function MyStack(){ 
    this.list = []; 
    this.index = 0;
}
MyStack.prototype = {
    insert: function(val){
            return this.list[this.index++] = val;
        },
    pop:function(){//...}
}

Проблема здесь: мы теряем конфиденциальность списка и индекса.

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

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

Ответ 1

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

function MyStack(){ 
    var list = []; 
    var index = 0;

    this.getIndex = function(){
        return index;
    }
    this.setIndex = function(val){
        index = val;
    }
    this.list = function(val){
        if(val){
           // setter if a value was provided. Illustrating how you can control
           // index, which I assume is the point of having these things private 
           // to begin with
           return list[this.setIndex(this.getIndex() + 1)] = val;
        }

        // always return list - acts like a getter
        return list;
    }
}
MyStack.prototype = {
    insert: function(val){
            return this.list(val);
        },
    pop:function(){}
}

var stack1 = new MyStack();       
stack1.insert(5);
var stack2 = new MyStack();       
stack2.insert(11);

Ответ 2

Вы должны проверить John Resig Простой наследование Javascript. Это отличное чтение, и оно было расширено, чтобы обеспечить поддержку для рядовых, точно названных Privates.js;

Ответ 3

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

var MyClass = function() {
    var instanceObj = this;
    var proxyObj = {
        myPublicMethod: function() {
            return instanceObj.myPublicMethod.apply(instanceObj, arguments);
        }
    }
    return proxyObj;
};
MyClass.prototype = {
    _myPrivateMethod: function() {
        ...
    },
    myPublicMethod: function() {
        ...
    }
};

Самое приятное, что создание прокси-сервера может быть автоматизировано, если мы определим соглашение для именования защищенных методов. Я создал небольшую библиотеку, которая делает именно это: http://idya.github.com/oolib/

Ответ 4

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

Обычно мы переходим ко второму подходу, о котором вы говорили, когда когда-либо хотим расширить свойства "MyStack" до какого-либо другого класса.

Скажем, я хочу расширить свой класс MyStack до MyTest, как показано ниже

var dummy = function();
dummy.prototype = MyStack.prototype;
var MyTest = function(){
};
MyTest.prototype = new dummy(); // Assigning MyStack properties to MyTest
var obj = new MyTest();