I've got a question regarding public and private variables in a Javascript object. Here's the simple code I've been playing with to get my head around variable scope as well as private and public properties.
var fred = new Object01("Fred");
var global = "Spoon!";
function Object01(oName) {
var myName = oName;
this.myName = "I'm not telling!";
var sub = new subObject("underWorld");
this.sub = new subObject("Sewer!");
Object01.prototype.revealName = function() {
return "OK, OK, my name is: " + myName + ", oh and we say " + global;
}
Object01.prototype.revealSecretName = function() {
console.log ("Private: ");
sub.revealName();
console.log("Public: ");
this.sub.revealName();
}
}
function subObject(oName) {
var myName = oName;
this.myName = "My Secret SubName!";
subObject.prototype.revealName = function() {
console.info("My Property Name is: " + this.myName);
console.info("OK, my real name is: " + myName + ", yeah and we also say: " + global);
}
}
The funny thing I've observed so far is within my objects, a plain var is treated as private (obviously, since they are in a function block), and a this
version is public. But I've noticed that the a variable with the same name with this.xxx
seems to be considered a different variable. So, in the example above, my object fred
will report something different for this.myName
compared with my function to pull my var myName
.
But this same behavior isn't the same for a sub-object I create. In the case of var sub
vs this.sub
both above use a new subObject
call to supposedly make two subObjects. But it seems both this.sub
and var sub
return the Sewer!
version.
Som I'm a bit confused about why if I use Strings for this.myName
and var myName
I get two different results, but my attempt to do the same with another object doesn't produce a similar result? I guess it could be that I'm using them wrong, or not understanding the differences between a this
and var
version.
You goofed. Constructors should not change the prototype. Either:
Or:
There's no private or public, there's variables and object properties.
Variables and object properties are different in many more ways than the one of variables having a variable scope and object properties not having a variable scope. Variable scope is not the same as private property of an object, because it's not a property but a variable.
Variables do not belong to any object but they can be sustained through closures. You can invoke those closures as a property of any object or without any object at all and the supposed private properties will work:
You are redefining functions in a prototype object every time the constructor is called. The whole point of prototypes is that you only have to create certain function objects just once. You are assigning methods to the prototype object inside a function, so every time that function is called, the functions are recreated and form new closures that refer to specific state.
I showed above that closures, because they hold state in the closed over variables, don't care about how they are invoked. So when you assign a closure as a property to the prototype, all the instances you have refer to the latest closure assigned, and you are getting its state.
I recommend using the standard way of defining "classes" in JS and not mixing it up with closures:
You can find a good tutorial here: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Details_of_the_Object_Model
Actually, I advocate using a non-standard approach to defining javascript classes. The following coding convention makes code easy to read and understand for anyone with an object-oriented background; it is also very easy to maintain unlike the
Method.prototype=function(){};
method which sucks anytime you want to rename a class, add more methods, understand the hierarchy of a class or even re-interpret what your own code is doing.Instead, you can declare object-oriented structures using the following architecture:
Now with the above classes declared: Animal, Dog extends Animal, and Cat extends Animal... We get the following:
I provide this javascript coding convention solely as a means to maintain organized object-oriented structures. I do not boast the performance of such coding style over other conventions, but if you want performance from your code I strongly suggest using Google's Closure Compiler which will optimize the same.
I have derived this javascript coding style from many years of coding experience on my own and the assimilation of critiquing other's code. I swear by it's robustness and modularity and welcome any comments regarding otherwise.
Your biggest problem here isn't actually the difference between
this
-based object properties andvar
-declared variables.Your problem is that you're trying to make prototype act as a wrapper that will give you protected class properties which are available to sub-classes, let alone instances of your main class.
prototype
can not work on"private"
members of a class at all (that being the variables defined within the scope of the constructor function, rather than being properties added to the constructed object you're returning).The point of the
prototype
string is either to provide methods which operate on the publicly-accessible properties of your objects (like if you wanted to change the value ofthis.name
, but you'd forever lose the hiddenscoped_name
reference)......or if you want ALL of the same kind of object to have access to the SAME value.
prototype
has defined a default value forschool
, forStudent
s who aren't given their own.prototype
also provided functions which can be shared between objects, because the functions usethis
to access the actual properties of the object.Any internal variables of the function can ONLY be accessed by properties or methods which are defined INSIDE of the function.
So in this case, the
prototype
methods can NEVER accessid
, except throughthis.showID
, becausethis.showID
is a reference to theshowIDCard
function, which is created for each and every single student, who has their own uniqueid
, and their own copy of that function has a reference to their own unique copy of that argument.My suggestion for applying large-scale "class" methodology to JS is to go with a style which favours composition of objects. If you're going to sub-class, make each sub-class a module, with its own public-facing interface, and its own privately-scoped vars, and then make that module the property of whatever you were trying to make, rather than trying to get chains of inheritance working.
That is way, way too much work in JS, if you're anticipating doing something like inheriting from a base-class, and then extending it 8 or 10 generations. It will just end in tears, and complaints that JS isn't "OOP" (in the style you'd like it to be).
Blake's answer inspired me, but I found it not doing everything that I wanted, so I hacked away at it until I have something that covers most of the OOP features of C++ in a simple and elegant syntax.
The only things not supported at the moment (but it's a matter of implementing them):
See the github repo for examples and a serious readme: