liveBook · Manning

Private variable

As explained in Secrets of the JavaScript Ninja, Second Edition

A variable intended to be inaccessible from outside an abstraction boundary, such as an object or function scope.

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

How Secrets of the JavaScript Ninja, Second Edition covers it

How deep it goes
Works through constructor-local state, per-instance lexical environments, and transferred methods; treats privacy as an approximation rather than a native object property.
Examples
Uses a constructor-local counter, a getter transferred to another object, a closure-backed skill property, and a mouse-event module.
What it assumes
Assumes basic familiarity with functions, constructors, new, and this; develops lexical environments to explain how methods retain access.
What you can do after
Build it from scratch with constructor-local variables and closure-backed methods, while recognizing why transferring a method does not transfer its state.
How it connects
Connects private variables to closures, lexical environments, getters and setters, Object.defineProperty, and the module pattern.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Secrets of the JavaScript Ninja, Second Edition. The section links point to the parts of the book it draws on.