Private properties in JavaScript ES6 classes Private properties in JavaScript ES6 classes javascript javascript

Private properties in JavaScript ES6 classes


Short answer, no, there is no native support for private properties with ES6 classes.

But you could mimic that behaviour by not attaching the new properties to the object, but keeping them inside a class constructor, and use getters and setters to reach the hidden properties. Note that the getters and setters gets redefine on each new instance of the class.

ES6

class Person {    constructor(name) {        var _name = name        this.setName = function(name) { _name = name; }        this.getName = function() { return _name; }    }}

ES5

function Person(name) {    var _name = name    this.setName = function(name) { _name = name; }    this.getName = function() { return _name; }}


Private class features is in Stage 3 proposal. The majority of its features are supported by all major browsers.

class Something {  #property;  constructor(){    this.#property = "test";  }  #privateMethod() {    return 'hello world';  }  getPrivateMessage() {      return this.#property;  }}const instance = new Something();console.log(instance.property); //=> undefinedconsole.log(instance.privateMethod); //=> undefinedconsole.log(instance.getPrivateMessage()); //=> test


To expand on @loganfsmyth's answer:

The only truly private data in JavaScript is still scoped variables. You can't have private properties in the sense of properties accessed internally the same way as public properties, but you can use scoped variables to store private data.

Scoped variables

The approach here is to use the scope of the constructor function, which is private, to store private data. For methods to have access to this private data they must be created within the constructor as well, meaning you're recreating them with every instance. This is a performance and memory penalty, but some believe the penalty is acceptable. The penalty can be avoided for methods that do not need access to private data by adding them to the prototype as usual.

Example:

function Person(name) {  let age = 20; // this is private  this.name = name; // this is public  this.greet = function () {    // here we can access both name and age    console.log(`name: ${this.name}, age: ${age}`);  };}let joe = new Person('Joe');joe.greet();// here we can access name but not age

Scoped WeakMap

A WeakMap can be used to avoid the previous approach's performance and memory penalty. WeakMaps associate data with Objects (here, instances) in such a way that it can only be accessed using that WeakMap. So, we use the scoped variables method to create a private WeakMap, then use that WeakMap to retrieve private data associated with this. This is faster than the scoped variables method because all your instances can share a single WeakMap, so you don't need to recreate methods just to make them access their own WeakMaps.

Example:

let Person = (function () {  let privateProps = new WeakMap();  class Person {    constructor(name) {      this.name = name; // this is public      privateProps.set(this, {age: 20}); // this is private    }    greet() {      // Here we can access both name and age      console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);    }  }  return Person;})();let joe = new Person('Joe');joe.greet();// here we can access joe's name but not age

This example uses an Object to use one WeakMap for multiple private properties; you could also use multiple WeakMaps and use them like age.set(this, 20), or write a small wrapper and use it another way, like privateProps.set(this, 'age', 0).

The privacy of this approach could theoretically be breached by tampering with the global WeakMap object. That said, all JavaScript can be broken by mangled globals. Our code is already built on the assumption that this isn't happening.

(This method could also be done with Map, but WeakMap is better because Map will create memory leaks unless you're very careful, and for this purpose the two aren't otherwise different.)

Half-Answer: Scoped Symbols

A Symbol is a type of primitive value that can serve as a property name. You can use the scoped variable method to create a private Symbol, then store private data at this[mySymbol].

The privacy of this method can be breached using Object.getOwnPropertySymbols, but is somewhat awkward to do.

Example:

let Person = (function () {  let ageKey = Symbol();  class Person {    constructor(name) {      this.name = name; // this is public      this[ageKey] = 20; // this is intended to be private    }    greet() {      // Here we can access both name and age      console.log(`name: ${this.name}, age: ${this[ageKey]}`);    }  }  return Person;})();let joe = new Person('Joe');joe.greet();// Here we can access joe's name and, with a little effort, age. ageKey is// not in scope, but we can obtain it by listing all Symbol properties on// joe with `Object.getOwnPropertySymbols(joe)`.

Half-Answer: Underscores

The old default, just use a public property with an underscore prefix. Though not a private property in any way, this convention is prevalent enough that it does a good job communicating that readers should treat the property as private, which often gets the job done. In exchange for this lapse, we get an approach that's easier to read, easier to type, and faster.

Example:

class Person {  constructor(name) {    this.name = name; // this is public    this._age = 20; // this is intended to be private  }  greet() {    // Here we can access both name and age    console.log(`name: ${this.name}, age: ${this._age}`);  }}let joe = new Person('Joe');joe.greet();// Here we can access both joe's name and age. But we know we aren't// supposed to access his age, which just might stop us.

Conclusion

As of ES2017, there's still no perfect way to do private properties. Various approaches have pros and cons. Scoped variables are truly private; scoped WeakMaps are very private and more practical than scoped variables; scoped Symbols are reasonably private and reasonably practical; underscores are often private enough and very practical.