Шаблон модуля Javascript и новое ключевое слово

Я понимаю основы паттерна модуля и его использование закрытия, позволяющего использовать частные члены, НО я не могу понять, почему приведенный ниже код делает то, что он делает:

var Calculator = function() {
    var priv = 0;

    return {
        changePriv: function() { priv++;},
        printPriv: function() { console.log(priv);}
    }
}

var myCalc = Calculator();
myCalc.printPriv();
myCalc.changePriv();
myCalc.printPriv();

var myOtherCalc = Calculator();
myCalc.printPriv();

Вывод консоли

0
1
1

Таким образом, преднамеренно опустив здесь ключевое слово new, первый вызов устанавливает myCalc в объект Calculator. Он начинается со значения priv, равного 0, увеличивает его, а затем выводит новое значение priv, равное 1.

Но а) ПОЧЕМУ следующий вызов Calculator() в конечном итоге возвращает ссылку на тот же самый объект (о чем свидетельствует вторая «1»)? Я знаю, что могу использовать здесь new и избежать этого, но не понимаю, почему я должен это делать. Разве эта функция не использует синтаксис литерала объекта, чтобы по существу создать новый объект, а затем вернуть его? б) Поскольку кажется, что он использует одно и то же пространство стека функций (правильно ли думать об этом в JS?), почему он не обнуляет переменную priv в процессе, прежде чем возвращать ссылку на тот же объект?

РЕДАКТИРОВАТЬ: исправлена ​​неаккуратная/глупая ошибка (спасибо scessor), которая теперь выводит новый/отдельный объект калькулятора даже без использования ключевого слова new. Итак, это проясняет а) и б). Мой результирующий вопрос был бы таким: «Имеет ли значение, использую ли я new или нет при вызове конструктора шаблона модуля. Ответ, я думаю, не имеет значения (?). (Джозеф: см. http://jsfiddle.net/MvMvy/5/ ... оператор instanceof просто не работает с шаблоном модуля в любом случае.)


person Community    schedule 18.04.2012    source источник
comment
По соглашению функции, начинающиеся с заглавной буквы, зарезервированы для конструкторов (то есть функций, которые должны вызываться с помощью new). Функция Calculator явно возвращает объект, поэтому вызов ее с помощью new не имеет никакого значения для того, что она возвращает.   -  person RobG    schedule 18.04.2012
comment
Спасибо РобГ. Это именно то, на что я надеялся получить подтверждение.   -  person    schedule 18.04.2012


Ответы (3)


Вы не выводите другой калькулятор myOtherCalc: если вы хотите их сравнить, замените третий myCalc.printPriv(); на:

myOtherCalc.printPriv();

Тогда вывод:

0
1
0
person scessor    schedule 18.04.2012

В вашем случае не нужно использовать new.

Обычно, если вы используете new, вы ожидаете, что получите экземпляр того конструктора, который вы вызвали. В вашем случае этого не произойдет, потому что вы вручную вернули объект. Это не имело бы смысла и позже вызовет проблемы, если вы запутаете использование. Вскоре вы, возможно, будете "экземплярно тестировать" свои объекты и столкнетесь с этим "несоответствием".

и у вас есть опечатка в вашем коде:

var myCalc = Calculator();       //create calculator
myCalc.printPriv();              //"myCalc" private is 0
myCalc.changePriv();             //increment
myCalc.printPriv();              //"myCalc" private is 1

var myOtherCalc = Calculator();  //another calculator
myCalc.printPriv();              ///but you printed "myCalc" again
person Joseph    schedule 18.04.2012
comment
не должны и не должны быть разными утверждениями. Я бы сказал, что в его конкретном примере он не должен, однако... Существуют версии шаблона модуля, которые хорошо сочетаются с new и не сталкиваются с проблемой instanceof, которую вы описываете. См.: carldanley.com/js-module-pattern. - person Frug; 03.05.2014

Нет ничего общего с «новым» оператором... Здесь вы получите хорошо объясненную тему о proto/constructor: http://en.wikibooks.org/wiki/JavaScript/Access_Control

Однако это бессмысленный пример, вы можете сделать это, поэтому вы можете получить доступ к priv только с помощью методов getter и setter:

function Calculator2() {
var priv = 0;
this.public = 0;
this.getPriv = function(){
    return  priv;
}
this.setPriv = function(val){
    priv = val;
}
}
Calculator2.prototype.changePriv = function(){
this.setPriv(this.getPriv()+1);
}
Calculator2.prototype.printPriv = function(){
    console.log("priv = " + this.getPriv());
}
Calculator2.prototype.changePublic = function(){
    this.public++;
}
Calculator2.prototype.printPublic = function(){
    console.log(this.public);
}

В этом случае var priv всегда доступен через геттер и сеттер,

В следующем примере у вас есть частная переменная className и другая общедоступная переменная __className:

<div id = "outputDiv" style="width:600px;height:400px;border:solid 1px #000"></div>
<script type="text/javascript">
    //<![CDATA[

// скрипт: var SomeClass = function(className) {

    var __className__ = className;
    this.__className__ = "\"public default className\"";
    var someString = new String("");

    this.setScopeText = function() { // void
        someString = "A new instance of \"private [__classname__] : " +
        __className__ + "\"" +
        " has been created. Refering to [__className__]<br />" +
        "A new instance of " +
        this.__className__ +
        " has been created. Refering to [this.__className__]";
        return someString;
    };

    this.getScopeText= function (){
        return someString;
    }

    this.setOutput = function(elementId, someString){
        var outputPane = this.getSomePane(elementId);
        outputPane.innerHTML += "<p>" + someString + "</p>";
    }

    this.getSomePane = function(elementId){
        var outputP = document.getElementById(elementId);
        return outputP;
    }

}

SomeClass.prototype.changeClassNameVariable = function( str ){
    this.__className__  = str;
}

// конец объявления.

//тесты:

var sc = new SomeClass("foo");

sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());
sc.setOutput("outputDiv",sc.getSomePane("outputDiv"));

sc.__className__ = "\"Some name\"";
sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());

sc.changeClassNameVariable("bar");
sc.setOutput("outputDiv",sc.__className__);
sc.setOutput("outputDiv",sc.setScopeText());

// завершает раздел javascript и CDATA

//]]>
</script>

Вывод в "div:outputDiv":

"общедоступное имя класса по умолчанию"

Создан новый экземпляр "private [имя_класса]: foo". Ссылаясь на [имя_класса], был создан новый экземпляр "общедоступного имени класса по умолчанию". Ссылаясь на [this.className]

[объект HTMLDivElement]

"Какое-то имя"

Создан новый экземпляр "private [имя_класса]: foo". Ссылаясь на [className] Создан новый экземпляр «Some name». Ссылаясь на [this.className]

бар

Создан новый экземпляр "private [имя_класса]: foo". Ссылаясь на [className], был создан новый экземпляр bar. Ссылаясь на [this.className]

-> имя_класса, объявленное в конструкторе, никогда не меняется! -> this.className или SomeClass.prototype.className является общедоступным и может быть изменен.

Я надеюсь, что это может помочь понять цепочку и мой комментарий более четко...

person tatactic    schedule 18.04.2012
comment
В вашем примере не разрешены частные методы, что является точкой шаблона модуля. (getPriv доступен везде — см. jsfiddle.net/VjnGq/1) - person ; 18.04.2012
comment
Вот почему я сказал, что это бессмысленный пример. Он был только для того, чтобы показать, как это работает... getPriv доступен, а priv - нет, если не вызывается метод getPriv()... - person tatactic; 27.04.2012