Suppose an object tracks a count, but callers should not be able to change it arbitrarily. A private variable hides that state behind methods that decide how it can be read or updated. In the JavaScript approach shown here, the variable is local to a function rather than a property on the object; closures provide the access boundary. section 5.2.1
Keep a count behind methods
function Counter() { var count = 0; this.getCount = function() { return count; }; this.increment = function() { count++; }; } var counter = new Counter(); counter.increment(); // counter.count is undefined; counter.getCount() is 1. The methods can use count, but outside code cannot read it directly as an object property. section 5.2.1
Why does the count survive after the constructor returns? Each constructor call creates a lexical environment for its local variables. Functions created during that call retain a reference to that environment. Each instance therefore gets its own methods closing over its own count. section 5.6.1
There is a catch: the state belongs to the closure, not securely to the object. Assign a getter to another object, and that getter can still read the variable it originally closed over. This is useful information hiding, not a true private object property. section 5.6.2
Choose the public access you need:
- Use explicit getter and setter methods when reads or writes need a controlled path, such as logging, validation, or a UI update. section 8.1
- Use Object.defineProperty inside the constructor to expose property-style get and set access to a local variable. Class or object-literal accessors alone are not created in that variable’s function scope. section 8.1.1
- For a module, keep variables inside an immediately invoked function and return public methods that close over them. A separate module extension cannot access those private variables. section 11.1.1
Listing 5.12. Private variables are accessed through functions, not through objects!
function Ninja() {
var feints = 0;
this.getFeints = function(){
return feints;
};
this.feint = function(){
feints++;
};
}
var ninja1 = new Ninja();
ninja1.feint();
var imposter = {};
imposter.getFeints = ninja1.getFeints; #A
assert(imposter.getFeints() === 1, #B
"The imposter has access to the feints variable!"); #B
The transferred-getter test makes the key limitation visible: access follows the function’s closure even when the function is attached to another object. section 5.6.2
