What really happens is that the Dog constructor function gets called, with ‘this’ set to the prototype of the constructor. The prototype is simply another object, which defaults to a plain old Object, but can be specified in code, eg.
1 2 3 4
The effect of this is that the
dog object will be able to use members
which exist in the prototype object, so
dog.bark() will work.
So how do we get ‘private’ members in objects? The trick is to use the closure property of the constructor function itself, and define some accessor methods within it:
1 2 3 4 5 6
So now your dogs can be given a new name when they are created, but the name cannot be directly accessed or changed from the outside.
1 2 3
If you want to make a private variables which are not arguments to the constructor, you can do that by simply declaring them at the top of the constructor function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
So we could continue in this way, and declare all the methods of the Dog inside the constructor function, and enjoy the benefits of having encapsulated properties. That would work fine, but the downside is that these functions are being re-created every time the constructor is called. This is a waste of memory.. not sure how much of an issue this is in reality unless you’re making thousands of them, but it is something to avoid if possible.
Preventing Memory Loss (What memory problem?)
While it is not possible to completely avoid some duplicate functions, the memory used can be mitigated as follows: if you make getters/setters for all your properties that you want to be private, then you can define the rest of the methods on the prototype to use the getters/setters. This way the only duplicated functions are the getters/setters, which should be very lightweight (couple lines each), and shouldn’t result in memory issues, unless you’re making a particle system or something.
Here is an example of this:
1 2 3 4 5
So this way you have the protection of encapsulated state, and avoid duplicating all your methods in every instance, thanks to prototypal inheritance. Now getters & setters may be distateful to you, but it is the best solution to this issue I can see, and it has the additional benefit of adding a layer of abstraction to your code, so you could change the way the state values are implemented, without changing a bunch of references to properties throughout the class.
As always, for inheritance to work with non-empty constructors, it is important that you explicitly call the prototype’s constructor:
1 2 3 4 5 6
The Behaviour Encapsulation Dilemma
So we have private variables which can only be accessed by the get/set functions we declared in the constructor. But what about private functions? They too can be declared in the constructor function as inner functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
The limitation here is that while such inner functions can be called by other functions which are defined within the constructor, they cannot be called by prototype methods. You could put all methods which need to access the private methods also inside your constructor, but then you’re back to the memory issue.
A way around this is to wrap the entire definition of the constructor and prototype in a closure function, which contains the private methods. This is basically the module pattern, and indeed if you are defining a constructor function inside a module, you can take advantage of the hiding effect of the module.
But there is an important gotcha here: these methods (and any other vars they access) are static: they are shared between all instances created with the constructor function contained within the same module. So you need to be aware of this. This is not a problem if you have static constants and ‘pure functions’ without side-effects encapsulated in your module. But it is potentially dangerous that one instance could call a method which could affect other instances… a kind of state contamination which is best avoided. Static and stateful just don’t go well together.
There’s also the issue that the module pattern may be too effective in preventing behaviour modification, and undermine the dynamic nature of the language.
Perhaps perhaps private instance methods are not absolutely necessary, and may be detrimental. AFAIK Smalltalk does not have them either, and takes a similar approach to that outlined here, of encapsulating state by only exposing methods (strictly speaking, ‘messages’, in Smalltalk), not properties. And Smalltalk is the archetypal Object Oriented language.