Private Properties in JavaScript

I saw "You Can Create Private Properties in JS (Accessor Pattern)" by Kirill Shestakov in my twitter feed, and I was hopeful that someone else had articulated the thoughts that I'd had mulling around in my head recently about new ES2015 features. Particularly about how you can use those features to encapsulate data.

Unfortunately after having read the post, I realized I still needed to articulate my thoughts on the matter, particularly around private variables and what "private" actually means.

How to Make Private Properties in JavaScript

ES2015 introduced classes, which are convenient syntactic sugar over the existing prototypal inheritance. Being able to write:

class Foo extends Bar {
  constructor (...) {
    super(...)

    ...
  }

  ...
}

is much nicer than having to write:

function Foo(...) {
  Bar.call(this, ...)

  ...
}

Foo.prototype = Object.create(Bar.prototype)
Foo.prototype.constructor = Foo
Object.assign(Foo.prototype, ...)

Having this convenient shorthand for building class relationships in an easily readable manner is very nice. Unfortunately, it didn't introduce any gains toward encapsulation.

Pseudo-private Variables

A long-standing practice in JavaScript is to prefix property names with _ to show that the property is meant to be private as a fair warning to other developers to avoid using it. In ES2015 this still works just fine:

class Angle {
  constructor (radians) {
    this._radians = radians
  }

  get degrees () {
    return this._radians * 180 / Math.PI
  }

  get radians () {
    return this._radians
  }
}

However, in cases where the object is meant to be enumerated over, these extra _ prefixed properties get in the way.

Symbol

This is where Symbol comes in. ES2015 introduced Symbol objects. Symbol objects are unique identifiers to be used for object properties that should not be enumerable. Additionally, creating two symbols with the same name produces two different symbols, which may be used to represent different properties.

This means that symbols are an excellent choice for private properties:

const radiansKey = Symbol('radians')
class Angle {
  constructor (radians) {
    this[radiansKey] = radians
  }

  get degrees () {
    return this[radiansKey] * 180 / Math.PI
  }

  get radians () {
    return this[radiansKey]
  }
}

Without access to the radiansKey symbol, other developers won't have direct access to values that they shouldn't need to access.

Privacy vs Security

It's important to distinguish the difference between privacy and security.

  • A changing room in the mall is private but not secure.

  • Bullet-proof glass is secure but not private.

The purpose of private variables is not to prevent unwanted access to those variables. The purpose is to hide implementation details from the public interface. This is why _ prefixed pseudo-private variables are an acceptable alternative to Symbol, they indicate to other developers that the value can safely be ignored when considering how to interact with an object.

This is where Kirill Shestakov took things too far and produced the overengineered "solution" to what I consider to be a non-issue.

The problem with the Symbol method is that you can still access those properties using Object.getOwnPropertySymbols. In either case, you would likely have to include bulky polyfills in production code.

Accessing the symbols on an object isn't a problem. Object.getOwnPropertySymbols is an example of reflection. Other strongly-typed languages such as C# have reflection allowing private access, and this isn't seen as a problem. The reason, again, is because preventing access isn't the reason to use private properties. Privacy is just about hiding details from plain view.

tl;dr:

If you need private variables in JavaScript, use Symbol for properties. It allows for reasonably concise encapsulation while also making good use of ES2015's class syntax.

If you need secure variables in JavaScript, do something else, because privacy and security are not the same thing.