This question stems from a problem I was trying to solve regarding the ability to have "private" instance variables in JavaScript. You may want to read this, prior to my question.
For the sake of completeness, I have illustrated my entire problem, before asking the question. My hope is that this will provide a complete example of how to implement instance members and methods correctly in JavaScript, and for any developer who lands here, to understand the pitfalls of the various implementations.
Consider the following JavaScript object:
var MessageBox = (function() {
function MessageBox(message) {
this.message = message;
}
MessageBox.prototype.Show = function() {
alert(this.message);
}
})();
This object was modelled using TypeScript, and can be used as follows:
var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
But I can still call:
msg1.message; // "Hello World"
msg2.message; // "Bye World"
So clearly this.message
is NOT private.
Now consider the following JavaScript object:
var MessageBox = (function() {
return function MessageBox(message) {
var message = message;
MessageBox.prototype.Show = function() {
alert(message);
}
}
})();
This is just a modified version of the TypeScript based MessageBox
object.
var msg1 = new MessageBox("Hello World");
msg1.Show(); // alerts "Hello World"
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
but wait...I'm about to throw a spanner in the works!
var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Bye World" ... wait, what!?
msg1.message // undefined
msg2.message // undefined
So I can no longer gain access to the message variable, but now, every new instance overwrites the last instances message.
Bear with me, this is the last JavaScript object to consider:
var MessageBox = (function() {
return function MessageBox(message) {
var message = message;
this.Show = function() {
alert(message);
}
}
}();
The above object no longer implements Show() on the prototype, so now I can:
var msg1 = new MessageBox("Hello World");
var msg2 = new MessageBox("Bye World");
msg2.Show(); // alerts "Bye World"
msg1.Show(); // alerts "Hello World"
msg1.message // undefined
msg2.message // undefined
Great! Now I have private variables, and they don't overwrite each other!
So, finally the question: What is the difference between:
MessageBox.prototype.Show = function() {
}
and
this.Show = function() {
}
The question that you eventually get to has a simple answer: setting the function on the prototype means it can get called from any instance, while setting the function on the instance means it can be called only from that instance. Either way can access properties on the instance, but the thing that you're finding complicated is that either way the function only has access to local variables in the scope where it was declared or in containing scopes.
The following is the first way that came to mind to provide both private instance and private prototype variables.:
var MessageBox = (function() {
var privateProtoVar = "Hello";
function MessageBox(message) {
var privateInstanceVar = message;
this.instanceMethod = function() {
alert(privateInstanceVar); // Can access private instance var
alert(privateProtoVar); // Can access private prototype var
}
}
MessageBox.prototype.Show = function() {
alert(privateProtoVar); // Can access private proto var
// but can't access privateInstanceVar
}
return MessageBox;
})();
var msg1 = new MessageBox("1"),
msg2 = new MessageBox("2");
msg1.instanceMethod(); // "1", "Hello"
msg2.instanceMethod(); // "2", "Hello"
msg1.Show(); // "Hello"
msg2.Show(); // "Hello"
The variable privateInstanceVar
is accessible by any function declared inside the inner MessageBox()
function, but not from functions on the prototype
unless they're declared within that same scope (which I don't do in the above example).
If you add additional methods to instances later, i.e., outside the above structure then those methods do not have access to the private var because they're declared in a different scope:
msg1.newMethod = function() {
alert(privateInstanceVar);
}
msg1.newMethod(); // error