492

Is it possible to create private properties in ES6 classes?

Here's an example. How can I prevent access to instance.property?

class Something {
  constructor(){
    this.property = "test";
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
Kirill Fuchs
  • 12,706
  • 3
  • 36
  • 70
d13
  • 9,421
  • 11
  • 33
  • 39
  • 7
    There is actually stage 3 proposal for this feature - https://tc39.github.io/proposal-class-fields/ https://github.com/tc39/proposal-class-fields – arty Jul 24 '18 at 08:36
  • @arty I've provided an answer to this with examples: https://stackoverflow.com/a/52237988/1432509 – Alister Sep 11 '18 at 13:20
  • All solutions for private properties / methods, ES5 / ES6+, all fail to provide real privacy since Chrome based browsers can always reveal the entire execution context in the `[Scopes]` object of any prototype. Some things just have to be coded outside the browser scope. In my test, no method will hide anything from Chrome. – thednp Feb 06 '21 at 13:47

39 Answers39

301

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; }
}
kmiklas
  • 11,204
  • 17
  • 55
  • 84
MetalGodwin
  • 3,453
  • 2
  • 12
  • 10
  • 3
    I like this solution the best. I agree it shouldn't be used for scaling but it's perfect for classes that will usually only be instantiated once per include. – Blake Regalia Oct 12 '15 at 20:49
  • Should also be noted, if you're using ES6 style classes then there's a good chance you're supporting ES5 get/set too. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Defining_getters_and_setters – Shawn Khameneh Nov 02 '15 at 08:31
  • 2
    Also you are redefining every single component of this class each time a new is created. – Quentin Roy Nov 30 '15 at 20:15
  • @ShawnKhameneh, how does this closure trick work with setter and getter in ECMA 6? I only came up with a solution with `defineProperty` available in ECMA 5, which is way more ugly than the syntax in ECMA 6. – Franklin Yu May 29 '16 at 21:07
  • 11
    This is so weird! In ES6 you are creating more "closure pyramids" than before ES6! Defining functions WITHIN a constructor looks uglier than it did in the above ES5 example. – Kokodoko Nov 14 '16 at 11:24
  • 1
    Since the OP specifically asks about ES6 classes, I personally think this is a poor solution, even though it technically works. The major limitation is that now every class method that uses private variables must be declared inside the constructor, severely undermining the advantages of having `class` syntax in the first place. – NanoWizard Dec 29 '17 at 12:42
  • 14
    All this does is introduce indirection. Now how do you make the `getName` and `setName` properties private? – aij Apr 12 '18 at 16:10
  • 2
    @aij So name one language that does not do the same. You can easily see that he could just comment out the setter or the getter or both and that `_name` is truly private. – Stijn de Witt Jan 15 '19 at 22:57
  • @NanoWizard I have to agree with your criticism. This technique works, but only having access to the private variables in the constructor is very limiting. It can be easily solved though. All you need is a way to associate data with an object without creating a field on that object... This can be achieved with a WeakMap... See my package [prv](https://npmjs.com/package/prv) for details. – Stijn de Witt Jan 15 '19 at 23:05
  • There is a new library out that supports private data while still allowing for the use of ES6 class syntax, i.e. the class functions can see the data on the `this` context, but nothing else can. https://github.com/anywhichway/privatize. It only requires adding one line of code to existing classes, the line that declares which data is private. Even class functions defined outside the constructor have access to the data, but foreign entities do not. – AnyWhichWay Feb 11 '19 at 02:40
  • if there are getters and setters for the private variables, then can't other code just use the setter and do the same thing `obj.setName("bar") `as if they are doing `obj.name = "bar"`? – nonopolarity Jan 16 '20 at 21:00
  • @aij, Since when Getters and Setters are private ?? – Rotem Orbach May 10 '21 at 19:19
230

Private fields (and methods) are being implemented in the ECMA standard. You can start using them today with babel 7 and stage 3 preset.

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }

  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return this.#property;
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> test
Alister
  • 21,405
  • 8
  • 36
  • 31
  • I'm wondering how can those class fields work. You can't currently use `this` in constructor before you call `super()`. Yet babel puts them before super. – seeker_of_bacon Oct 03 '18 at 16:23
  • How to configure ESLint to allow `#privateCrap` syntax? – Marecky Oct 12 '18 at 08:07
  • @Marecky Use babel-eslint :) – jhpratt Nov 03 '18 at 20:55
  • If you can't use Babel, you can try my package [prv](https://npmjs.com/package/prv) which works in older browsers. – Stijn de Witt Jan 15 '19 at 22:59
  • 6
    And what about eslint? I got parser error at equal sign. Babel is working, just eslint can't parse this new js syntax. – martonx Feb 04 '19 at 21:55
  • 9
    Wow this is very ugly. Hashtag is a valid character. The property is not really private, or? .. I checked it in TypeScript. Private members are not compiled in private or read-only (from outside). Just declared like another (public) property. (ES5). – Dominik Feb 05 '19 at 09:49
  • 1
    [By now](https://www.youtube.com/watch?v=c0oy0vQKEZE), this solution is also officially supported in Google Chrome and Node.js v12. Private getters and setters are in development. – Eyk Rehbein May 10 '19 at 15:56
  • 3
    How do you write _private methods_ with this? Can I do this: `#beep() {}`; and this: `async #bzzzt() {}`? – Константин Ван Jun 04 '19 at 21:58
  • @seeker_of_bacon you can use this word in constructor when you are already in a super class (classes extending nothing). super() syntax required only in extender (sub, child etc.) classes. – İsmail Ceylan Jun 05 '19 at 21:02
  • so ES6 won't support it natively but babel 7 and stage 3 preset can transpile it to be private? – nonopolarity Jan 08 '20 at 04:02
  • Those don't work with `static` currently. Why, ECMAScript. Why. – Vsevolod Golovanov Apr 30 '20 at 15:10
  • You can see the browser support for private fields [here](https://caniuse.com/#feat=mdn-javascript_classes_private_class_fields). At the time of writing this the only major browsers that don't support them are Firefox and Safari. – Donald Duck Aug 25 '20 at 09:38
  • It seems those don't support class composition in any way. – Oleg V. Volkov Sep 02 '20 at 14:08
  • This ES stage 3 proposal is implemented in [TypeScript version 4.3](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3-beta/#ecmascript-private-class-elements) as well and members starting with `#` (not just `private` modifier) are private at runtime as well, not just at compile time (if compiled to >= ES 2015). I created [sample code in TypeScript playfeild](https://cutt.ly/ucbmge5) to demonstrate, that it is truly private (ESNext target works with Chrome, that has implemented support for ES private fields proposal) – atsu85 Apr 04 '21 at 11:12
213

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.

twhb
  • 3,383
  • 2
  • 14
  • 21
  • 8
    The first example snippet ("scoped variables") is a total antipattern - each returned object will have a different class. Don't do that. If you want privileged methods, create them in the constructor. – Bergi Nov 05 '15 at 00:35
  • 1
    Wrapping a class inside a function seems to defeat the whole purpose of using classes in the first place. If you already use the function to create an instance, you might as well place all your private/public members inside that function as well, and forget about the whole class keyword. – Kokodoko Dec 22 '15 at 13:59
  • 2
    @Bergi @Kokodoko I edited the scoped variables approach to be slightly faster and not break `instanceof`. I admit I was thinking of that approach as included only for completeness' sake and should have given more thought to how much it is actually capable of. – twhb Dec 23 '15 at 17:50
  • your `this.greet` in the first example should use an arrow function so that it inherits lexical `this` automatically – Alnitak Jun 12 '16 at 08:01
  • 2
    Excellent explanation! I'm still surprised that ES6 actually made it harder to simulate a private variable, where in ES5 you could just use var and this inside a function to simulate private and public. – Kokodoko Nov 14 '16 at 11:29
  • 2
    @Kokodoko If you dispense with the class and just put everything in the function, you will also have to revert to implementing inheritance using the prototype method. Using extend on classes is by far a cleaner approach, so using a class inside a function is totally acceptable. – AndroidDev May 24 '17 at 06:47
  • Just to note, you'll need a polyfill if using Babel to target ES5 for WeakMap solution.. https://babeljs.io/docs/usage/polyfill/ or https://polyfill.io/v2/docs/features/ – Drenai Sep 14 '17 at 21:33
  • Wouldn't passing `this` to weakmap prevent the instance from getting garbage collected? and also, all private properties won't ever get deleted when the instance gets garbage collected if not passing `this` to the weakmap. Isn't that a memory issue? For the Symbol issue with `Object.getOwnPropertySymbols`, you can wrap the object with a Proxy that has an `ownKeys` handler which traps both `getOwnProperties` and `getOwnPropertySymbols` where you can return an array of the Object properties minus the Symbol. – Ramtin Soltani Nov 27 '17 at 17:56
  • @RamtinSoltani no, it would be true of Map, but those two memory issues are what WeakMap is designed to solve. See "Why WeakMap?" [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap). – twhb Nov 27 '17 at 22:41
  • @TristanBerger That’s good to know. Thanks for pointing it out. With those issues no longer existing, this would be the best method to store private data. – Ramtin Soltani Nov 28 '17 at 09:11
  • I just want to point out that there are a few articles out there stating the garbage collection issue of WeakMaps which are simply not true. – Ramtin Soltani Nov 28 '17 at 09:16
  • 1
    IMHO, the __scoped symbols__ is the right answer - it gives the desired level of privacy (defending from unintended usage, not otherwise, which is the only concern the private properties should serve) in a least complex/hackish way – GullerYA Apr 02 '18 at 08:47
  • @Drenai You only need the polyfill *in theory*. In practice, all browsers support `WeakMap` and have been doing so for some time. – Stijn de Witt Jan 15 '19 at 23:10
119

Update: A proposal with nicer syntax is on its way. Contributions are welcome.


Yes, there is - for scoped access in objects - ES6 introduces Symbols.

Symbols are unique, you can't gain access to one from the outside except with reflection (like privates in Java/C#) but anyone who has access to a symbol on the inside can use it for key access:

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, can only access with access to the Symbol
Mahmoud
  • 3
  • 3
Benjamin Gruenbaum
  • 246,787
  • 79
  • 474
  • 476
  • 7
    Can't you use `Object.getOwnPropertySymbols`? ;) – Qantas 94 Heavy Mar 04 '14 at 05:16
  • 41
    @BenjaminGruenbaum: Apparently Symbols no longer ensure true privacy: http://stackoverflow.com/a/22280202/1282216 – d13 Mar 19 '14 at 14:39
  • @Qantas94Heavy that's reflection :) – Benjamin Gruenbaum Sep 08 '14 at 09:21
  • wouldn't this be similar to just using numberic constants as member names? const myPrivateMethod = 1; Something.prototype[myPrivateMethod] = function () { ... } new Something()[myPrivateMethod](); – Benja Oct 01 '14 at 13:52
  • 1
    @Benja well - one difference is that using `Object.keys` and `Object.getOwnPropertyNames` would show it and external users would simply be able to do `(new Something())[1]()` to run the function. Also note that all object keys in JavaScript (including array keys) are effectively strings. – Benjamin Gruenbaum Oct 01 '14 at 13:59
  • How is it private? Can't you just loop through all the keys of the object and find it? – trusktr Oct 26 '14 at 00:51
  • 29
    @trusktr through thre keys? No. Through the symbols? Yes. Very much like how you can use reflection in languages like C# and Java to access private fields. Access modifiers aren't about security - they're about clarity of intent. – Benjamin Gruenbaum Oct 26 '14 at 07:20
  • 10
    It seems like using Symbols is similar to doing `const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();`.This isn't really privacy, it's obscurity, in the sense of traditional JavaScript. I would consider "private" JavaScript to mean using closures to encapsulate variables. Those variables are hence not accessible through reflection. – trusktr Oct 26 '14 at 08:54
  • 13
    Also, I feel that using the `private` and `protected` keywords would be so much cleaner than `Symbol` or `Name`. I prefer dot notation rather than bracket notation. I'd like to keep using a dot for private things. `this.privateVar` – trusktr Oct 26 '14 at 08:54
  • We have static, but what's the target for implementing public, private and protected? Also, here's one solution using WeakMap, but it's a bit hairy. http://philipwalton.com/articles/implementing-private-and-protected-members-in-javascript/ – Nolo May 26 '15 at 02:18
  • @benjamin-gruenbaum symbol is used as a workaround. It creates a variable with a name that cannot be figured out by the user. You could as well use var property = "you will never find out how to access -- property" and suffix it with a random number. But within your class, you always have to use this[property] and can never use this["property"] or this.property. This is likely to make your code pretty hard to debug (because, sometimes, you will add the quotes). – widged Jul 06 '15 at 02:54
  • 4
    Why not just prefix the private data with `_`? If it can't be enforced, let's be at least open about the intent. E.g: `this._key = "123";` – VitalyB Jul 09 '15 at 14:35
  • If I need module level private functions then I think it should be enough to declare a normal function and not to export it, wdyt? – CodeYogi Aug 14 '16 at 07:29
  • I can't see how this is remotely private? The property symbol is externally visible. You don't even need to use `getOwnPropertySymbols()`, you can externally access the so-called "private" property, simply by doing `instance[property]`! I guess the best you could say is that its visibility is private-ish (in that it takes a little extra work to query the interface), but its accessibility is public! – Dan King Mar 07 '17 at 15:38
  • 1
    @DanKing private/public (without weak maps) is a matter of communicating with other developers and not a matter of security. Symbols have become a pretty common way to communicate a property is private without actually limiting it's accessibility. – Benjamin Gruenbaum Mar 07 '17 at 16:04
  • 1
    @BenjaminGruenbaum Seems to me like a lot of effort for no real benefit. Why not just use (e.g.) `this._myPrivateProperty`? – Dan King Mar 07 '17 at 16:33
  • @DanKing I use `_myPrivateProperty` myself, but it's a convention I've seen used very often. – Benjamin Gruenbaum Mar 07 '17 at 16:48
  • There is a stage 3 proposal that fixes this, will update the answer – Benjamin Gruenbaum Jul 13 '18 at 09:38
  • @BenjaminGruenbaum yes Symbols do work, however are not fully cross browser as they are not supported by Internet Explorer – Mickey Puri Oct 17 '18 at 09:26
34

The answer is "No". But you can create private access to properties like this:

(The suggestion that Symbols could be used to ensure privacy was true in an earlier version of the ES6 spec but is no longer the case:https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html and https://stackoverflow.com/a/22280202/1282216. For a longer discussion about Symbols and privacy see: https://curiosity-driven.org/private-properties-in-javascript)

Community
  • 1
  • 1
d13
  • 9,421
  • 11
  • 33
  • 39
  • 6
    -1, this does not answer your question really. (You can use closures with IIFEs in ES5 too). Private properties are enumerable through reflection in most languages (Java, C#, etc). The point of private properties is to convey intent to other progrmamers and not to enforce security. – Benjamin Gruenbaum Mar 19 '14 at 20:19
  • 1
    @BenjaminGruenbaum, I know, I wish I had a better answer, I'm not happy with it either. – d13 Mar 20 '14 at 01:42
  • I think symbols are still a valid way to achieve inaccessible members while in the programming environment. Yes, they can still be found if you reallllyyy want, but that's not the point is it? You shouldn't store sensitive information in it, but you shouldn't do that anyway in client-side code. But it works for the purpose of hiding a property or method from an outside class. – Kokodoko May 24 '17 at 09:11
  • 1
    Using variables scoped at the level of a module as substitute for private properties in a class will lead to a singleton.behavior or behavior similart to statitc properties.Instances of vars will get shared. – Adrian Moisa Dec 24 '17 at 08:40
30

The only way to get true privacy in JS is through scoping, so there is no way to have a property that is a member of this that will be accessible only inside the component. The best way to store truly private data in ES6 is with a WeakMap.

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };        
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

Obviously this is a probably slow, and definitely ugly, but it does provide privacy.

Keep in mind that EVEN THIS isn't perfect, because Javascript is so dynamic. Someone could still do

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

to catch values as they are stored, so if you wanted to be extra careful, you'd need to capture a local reference to .set and .get to use explicitly instead of relying on the overridable prototype.

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };        
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}
loganfsmyth
  • 135,356
  • 25
  • 296
  • 231
  • 3
    As a suggestion, you can avoid using one weak map per property by using an object as value. This way you can also reduce the number of map's `get` to one per method (e.g. `const _ = privates.get(this); console.log(_.privateProp1);`). – Quentin Roy Nov 30 '15 at 20:21
  • Yup, that's totally an option too. I mostly went with this since it maps more directly to what a user would have written when using real properties. – loganfsmyth Nov 30 '15 at 21:01
  • @loganfsmyth `const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"` that means that your property is private or not? – Barbu Barbu May 26 '17 at 12:04
  • 2
    For that to work, the code accessing the property would need access to the WeakMap object, which would normally be scoped inside of a module and inaccessible – loganfsmyth May 26 '17 at 14:05
22

For future reference of other on lookers, I'm hearing now that the recommendation is to use WeakMaps to hold private data.

Here is a more clear, working example:

function storePrivateProperties(a, b, c, d) {
  let privateData = new WeakMap;
  // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
  let keyA = {}, keyB = {}, keyC = {}, keyD = {};

  privateData.set(keyA, a);
  privateData.set(keyB, b);
  privateData.set(keyC, c);
  privateData.set(keyD, d);

  return {
    logPrivateKey(key) {
      switch(key) {
      case "a":
        console.log(privateData.get(keyA));
        break;
      case "b":
        console.log(privateData.get(keyB));
        break;
      case "c":
        console.log(privateData.get(keyC));
        break;
      case "d":
        console.log(privateData.set(keyD));
        break;
      default:
        console.log(`There is no value for ${key}`)
      }
    }
  }
}
Community
  • 1
  • 1
  • Why are your keys arrays? I know `WeakMap` will accept arrays as keys but you're instantiating 2 different arrays when you call `PRIVATE.get` and `PRIVATE.set`. Your `property` getter will return `undefined` every time. – Sethi Jan 21 '15 at 15:55
  • I apologize, I've modified my example. Note that depending on the transpiler, it will not preserve block scoping with `let` declarations. –  Jan 24 '15 at 18:52
  • 20
    Be aware that these properties are static. – Michael Theriot Jan 26 '15 at 23:35
  • 8
    I didn't downvote you but your weakmap example is completely wrong. – Benjamin Gruenbaum Apr 02 '15 at 21:51
  • 4
    Namely - You're sharing the data between all class instances and not per instance - may I at least fix it? – Benjamin Gruenbaum Apr 04 '15 at 13:25
  • 1
    Indeed, the weakmap needs to be attached to a given instance. See http://fitzgeraldnick.com/weblog/53/ for an example. – widged Jul 06 '15 at 03:16
  • 2
    According to MDN, primitive data types such as Symbols are not allowed as a WeakMap key. [MDN WeakMap Documentation](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap#Description) – leepowell Sep 30 '15 at 09:27
  • This answer is completely wrong. (a) it doesn't run (in Babel or anywhere), (b) Instance.prop1 accesses key 'prop1', not prop1, (c) the last line cannot access prop1 because it is not in scope, (d) the last line cannot access Something because it is not in scope, (e) you cannot use a Symbol as a key on a WeakMap, and (f) the WeakMap is global, causing private variables of different instances to clobber each other. – twhb Nov 03 '15 at 18:23
  • 1
    I've corrected the example above to (a) demonstrate the usage correctly (b) work when copying and pasting anywhere. @twhb regarding (f) The previous example used block scoping, which kept the WeakMap scoped within the block, unfortunately it seems that in V8 that the class could not also escape block scoping. –  Jan 14 '16 at 21:08
12

Depends on whom you ask :-)

No private property modifier is included in the Maximally minimal classes proposal which seems to have made it into the current draft.

However, there might be support for private names, which does allow private properties - and they probably could be used in class definitions as well.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
  • 3
    It's _highly_ unlikely that private names will make it into ES6, though they're thinking of some form of private thing for ES7. – Qantas 94 Heavy Mar 04 '14 at 05:16
  • @Qantas94Heavy both private names and unique string values have been superseded by Symbols from what I understand. – Benjamin Gruenbaum Mar 04 '14 at 05:43
  • Yeah, it probably will become Symbols. However, afaik the "symbols" currently contained in the spec are only used to describe internal properties like [[prototype]], and there is no way to create and use them in user code. Do you know some docs? – Bergi Mar 04 '14 at 13:19
  • I just realized that modules can be used to set privacy. Combined with Symbols that might be all you'd ever need ...? – d13 Mar 05 '14 at 01:18
  • They are in the latest spec, you can find them on Jason's copy – Benjamin Gruenbaum Mar 09 '14 at 00:00
  • @d13 How would you do it. Could you link a small example snippet? – trusktr Oct 26 '14 at 08:59
  • @trusktr: For modules, just export any property that you want to be public. The other properties are private by default. http://www.2ality.com/2014/09/es6-modules-final.html. I was wrong about using Symbols for privacy. – d13 Oct 27 '14 at 14:36
  • @d13 One of the issues with private variables inside a module is that they can't be extended by inheriting classes, as far as I know. I still wonder if there's a way to make variables that a class can inherit and use internally, but that aren't available externally (i.e. "protected" one might say). – trusktr Nov 02 '14 at 04:25
  • This seems crazy to me. If I have to add `var sym = Symbol();` I need to put my whole module/file in a closure, otherwise `sym` exists globally -- not so ECMA6-esque! Not to mention, `Object.getOwnPropertySymbols`. See http://code.tutsplus.com/tutorials/ecmascript-6-power-tutorial-class-and-inheritance--cms-24117 – Cody Sep 04 '15 at 20:11
  • 1
    @Cody: Your whole *module* code does have its own scope in ES6 anyway, no need for an IEFE. And yes, symbols are purposed for uniqueness (collision-avoidance), not privacy. – Bergi Sep 04 '15 at 21:52
  • Thanks Bergi. So you're saying I can avoid globals/closures with something like `class C { var sym = Symbol(); constructor () {...} ... }` ? Obviously, I'm totally new to the ECMA 6 standards -- I appreciate all your help :) Also, it looks like Ceane Lamerez's answer has everything in a just a block (`{}`) -- guess that's legal syntax, aye? – Cody Sep 05 '15 at 00:43
  • No, you cannot place the symbol in your class, but you can just place it next to it - variables declared in module scopes are not global. – Bergi Sep 05 '15 at 08:04
10

Using ES6 modules (initially proposed by @d13) works well for me. It doesn't mimic private properties perfectly, but at least you can be confident that properties that should be private won't leak outside of your class. Here's an example:

something.js

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

Then the consuming code can look like this:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

Update (Important):

As @DanyalAytekin outlined in the comments, these private properties are static, so therefore global in scope. They will work well when working with Singletons, but care must be taken for Transient objects. Extending the example above:

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c
Johnny Oshika
  • 45,610
  • 33
  • 151
  • 234
  • 4
    Good for `private static`. – Danyal Aytekin Mar 23 '16 at 15:05
  • @DanyalAytekin: that's a very good point. These private properties are static so global in scope. I've updated my answer to reflect this. – Johnny Oshika Mar 26 '16 at 19:06
  • The more I learn about functional programming (especially Elm and Haskell) the more I believe that JS programmers would benefit from a module-based approach to "modularity" rather than an OOP class-based one. If we think of ES6 modules as the foundations for building applications, and forget about classes entirely, I believe we may end up with much better applications overall. Could any experienced Elm or Haskell users comment on this approach? – d13 Apr 09 '16 at 22:04
  • 1
    In the update, the second `a.say(); // a` should be `b.say(); // b` – grokky Oct 02 '17 at 08:04
  • tried `let _message = null` way, not so cool, when call constructor multiple times, it mess up. – Littlee Mar 30 '18 at 13:35
  • @Littlee You're right. Please see my update in the answer. This technique is really only acceptable for singletons. – Johnny Oshika Mar 30 '18 at 16:31
9

Yes - you can create encapsulated property, but it's not been done with access modifiers (public|private) at least not with ES6.

Here is a simple example how it can be done with ES6:

1 Create class using class word

2 Inside it's constructor declare block-scoped variable using let OR const reserved words -> since they are block-scope they cannot be accessed from outside (encapsulated)

3 To allow some access control (setters|getters) to those variables you can declare instance method inside it's constructor using: this.methodName=function(){} syntax

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

Now lets check it:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value
Nikita Kurtin
  • 4,811
  • 3
  • 41
  • 44
  • 1
    This is (for now) the only one solution to this problem despite the fact that all methods declared in the constructor are redeclared for each instance of the class. This is pretty a bad idea regarding performance and memory usage. Class methods should be declared outside of the constructor scope. – Freezystem Feb 10 '16 at 22:23
  • @Freezystem First: **First** those are instance methods (not Class methods). **Second** OP question was: _ How can I prevent access to instance.property?_ and my answer is: _an example of how..._ **Third** if you have any better idea - let's hear it – Nikita Kurtin Feb 11 '16 at 09:49
  • 1
    I was not saying you were wrong, I said that your solution was the best compromise to achieve private variable despite the fact that a copy of each instance methods is created each time you call `new Something();` because your methods are declared in the constructor to have access to these private variables. That may cause a lot of memory consumption if you create a lot of instance of your class, so performance issues. Methods should have been declared outside of constructor scope. My comment was more an explanation of your solution drawbacks than a criticism. – Freezystem Feb 11 '16 at 10:28
  • 1
    But isn't it bad practice do define your entire class inside the constructor? Aren't we just "hacking" javascript now? Just look at any other OOP programming language, and you'll see that the constructor is not meant to define a class. – Kokodoko Nov 14 '16 at 11:26
  • @Kokodoko I'm guessing that by _OOP languages_ you meant _Class-based_ languages (Java, C#, etc...) where you use access modifiers to achieve properties encapsulation. And Yes you can say it's kind of hacky way, also as you can see in my answer (and comments above), the idea was to give an example of how to achieve properties encapsulation using ES6 where access modifiers as in Java or C# are unavailable. – Nikita Kurtin Nov 14 '16 at 12:26
  • 1
    Yes that's what I meant, and your solution works! I'm just saying that in general I am surprised that ES6 added a 'class' keyword, but removed the elegant solution of working with var and this, to achieve encapsulation. – Kokodoko Nov 15 '16 at 10:54
9

Completing @d13 and the comments by @johnny-oshika and @DanyalAytekin:

I guess in the example provided by @johnny-oshika we could use normal functions instead of arrow functions and then .bind them with the current object plus a _privates object as a curried parameter:

something.js

function _greet(_privates) {
  return 'Hello ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sunny day!');

const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();

console.log(message1 === 'Hello Sunny day!');  // true
console.log(message2 === 'Hello Cloudy day!');  // true

// the followings are not public
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');

const message3 = something2.say();

console.log(message3 === 'Hello another Sunny day!'); // true

Benefits I can think of:

  • we can have private methods (_greet and _updateMessage act like private methods as long as we don't export the references)
  • although they're not on the prototype, the above mentioned methods will save memory because the instances are created once, outside the class (as opposed to defining them in the constructor)
  • we don't leak any globals since we're inside a module
  • we can also have private properties using the binded _privates object

Some drawbacks I can think of:

A running snippet can be found here: http://www.webpackbin.com/NJgI5J8lZ

efidiles
  • 181
  • 2
  • 2
7

A different approach to "private"

Instead of fighting against the fact that private visibility is currently unavailable in ES6, I decided to take a more practical approach that does just fine if your IDE supports JSDoc (e.g., Webstorm). The idea is to use the @private tag. As far as development goes, the IDE will prevent you from accessing any private member from outside its class. Works pretty well for me and it's been really useful for hiding internal methods so the auto-complete feature shows me just what the class really meant to expose. Here's an example:

auto-complete showing just public stuff

Lucio Paiva
  • 13,507
  • 6
  • 71
  • 90
  • 3
    The problem is, that we don't wan't to access the private variables over the Editor, we wan't to protect the private variables from outside - And that is, what public/private does. If your code is finished, you can access (and the important thinks: **override**) these variables from outside the class. Your `@private` comment can not prevent these, it's only a **Feature** for documentation-generation and you'r IDE. – Adrian Preuss Apr 21 '18 at 17:42
  • Yes, I am aware of that. It's just that that's enough for me and may be enough for other people out there. I know it's not really making my variables private; it's only warning me not to try and access it from outside (only, of course, if my team and I are all using an IDE that supports this feature). Javascript (and other languages, like Python) was not designed with access levels in mind. People do all sorts of stuff to somehow implement that functionality, but in the end we end up just hacking the language to achieve that. I decided to go with a more "natural" approach, if you will. – Lucio Paiva Apr 21 '18 at 18:19
7

Oh, so many exotic solutions! I usually don't care about privacy so I use "pseudo privacy" as it's said here. But if do care (if there are some special requirements for that) I use something like in this example:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

Another possible implementation of function (constructor) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}
Sergey
  • 4,403
  • 3
  • 28
  • 40
6

WeakMap

  • supported in IE11 (Symbols are not)
  • hard-private (props using Symbols are soft-private due to Object.getOwnPropertySymbols)
  • can look really clean (unlike closures which require all props and methods in the constructor)

First, define a function to wrap WeakMap:

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

Then, construct a reference outside your class:

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // it's easy to set a private variable
  }

  getAge() {
    return p(this).age; // and get a private variable
  }
}

Note: class isn't supported by IE11, but it looks cleaner in the example.

kevlened
  • 9,314
  • 4
  • 18
  • 15
5

I think Benjamin's answer is probably the best for most cases until the language natively supports explicitly private variables.

However, if for some reason you need to prevent access with Object.getOwnPropertySymbols(), a method I've considered using is attaching a unique, non-configurable, non-enumerable, non-writable property that can be used as a property identifier to each object on construction (such as a unique Symbol, if you don't already have some other unique property like an id). Then just keep a map of each object's 'private' variables using that identifier.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "I'm hidden"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "I'm public";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // A clean up method of some kind is necessary since the
    // variables won't be cleaned up from memory automatically
    // when the object is garbage collected
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

The potential advantage of this approach over using a WeakMap is faster access time if performance becomes a concern.

NanoWizard
  • 1,859
  • 1
  • 16
  • 30
  • 1
    Correct me if I am wrong, but wouldn't this code contain memory leaks since privateVars will still store an object's private variables even if the object is already destroyed? – Russell Santos Dec 28 '17 at 05:47
  • @RussellSantos you are correct, assuming the objects will need to be garbage collected at some point. Thank you for pointing that out. In my example I've added a `destroy()` method which should be called by the using code whenever an object needs to be removed. – NanoWizard Dec 29 '17 at 12:33
5

Personally I like the proposal of the bind operator :: and would then combine it with the solution @d13 mentioned but for now stick with @d13 's answer where you use the export keyword for your class and put the private functions in the module.

there is one more solution tough which hasn't been mentioned here that follows are more functional approach and would allow it to have all the private props/methods within the class.

Private.js

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

comments on it would be appreciated.

Robin F.
  • 905
  • 9
  • 17
  • Generally I like the approach. Feedback: 1. you will need a different private.js module for each class to prevent clashing. 2. I dislike the potential of making the constructor really long by inline-defining each of your private methods. 3. It would be nice if all of the class methods were in one file. – Doug Coburn Jun 16 '17 at 19:28
5

I came across this post when looking for the best practice for "private data for classes". It was mentioned that a few of the patterns would have performance issues.

I put together a few jsperf tests based on the 4 main patterns from the online book "Exploring ES6":

http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes

The tests can be found here:

https://jsperf.com/private-data-for-classes

In Chrome 63.0.3239 / Mac OS X 10.11.6, the best performing patterns were "Private data via constructor environments" and "Private data via a naming convention". For me Safari performed well for WeakMap but Chrome not so well.

I don't know the memory impact, but the pattern for "constructor environments" which some had warned would be a performance issue was very performant.

The 4 basic patterns are:

Private data via constructor environments

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private data via constructor environments 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private data via a naming convention

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private data via WeakMaps

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Private data via symbols

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
MarkM
  • 91
  • 1
  • 3
4

I believe it is possible to get 'best of both worlds' using closures inside constructors. There are two variations:

All data members are private

function myFunc() {
   console.log('Value of x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Enhanced value of x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(internal);
      
      this.myFunc = myFunc.bind(internal);
   }
};

Some members are private

NOTE: This is admittedly ugly. If you know a better solution, please edit this response.

function myFunc(priv, pub) {
   pub.y = 3; // The Test object now gets a member 'y' with value 3.
   console.log('Value of x: ' + priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // The Test object now gets a member 'z' with value 3.
   console.log('Enhanced value of x: ' + (priv.x + 1));
}

class Test {
   constructor() {
      
      let self = this;

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);
      
      this.myFunc = myFunc.bind(null, internal, self);
   }
};
JSInitiate
  • 41
  • 1
4

In fact it is possible using Symbols and Proxies. You use the symbols in the class scope and set two traps in a proxy: one for the class prototype so that the Reflect.ownKeys(instance) or Object.getOwnPropertySymbols doesn't give your symbols away, the other one is for the constructor itself so when new ClassName(attrs) is called, the instance returned will be intercepted and have the own properties symbols blocked. Here's the code:

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // public
    this[pet] = 'dog'; // private 
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // private
      return 'Hi there!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` The pet is a ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']


// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

Reflect.ownKeys() works like so: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj)) that's why we need a trap for these objects.

Francisco Neto
  • 289
  • 2
  • 3
4

Even Typescript can't do it. From their documentation:

When a member is marked private, it cannot be accessed from outside of its containing class. For example:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

But transpiled on their playground this gives:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

So their "private" keyword is ineffective.

Michael Franzl
  • 1,174
  • 11
  • 18
  • 2
    Well, it's still effective because it prevents "bad" programming, while in the IDE. It shows you which members you should and shouldn't use. I think that's the main reason for using private and public. (For example, when you compile C# to machine code, will private still be private? who knows?). When reading the other answers, it seems that using @Symbol can also make a member inaccessible. But even Symbols can still be found from the console. – Kokodoko May 24 '17 at 09:02
  • Does the TypeScript error occur during the transpile of TypeScript to JavaScript? (Like the type checking happens at transpite time. Rather than some runtime private mechanism.) – Eljay May 13 '18 at 11:11
  • Note that this response talks about TypeScript specific private *modifier*, but TypeScript 4.3 introduced additional support for [ECMAScript #private Class Elements](https://devblogs.microsoft.com/typescript/announcing-typescript-4-3-beta/#ecmascript-private-class-elements) that unlike private modifier isn't just private at compile-time, but also at runtime (if compiled to >= ES 2015). I created [sample code in TypeScript playfeild](https://cutt.ly/ucbmge5) to demonstrate, that it is truly private (ESNext target works with Chrome, that has implemented support for ES private fields proposal) – atsu85 Apr 04 '21 at 11:04
4

Coming very late to this party but I hit the OP question in a search so... Yes, you can have private properties by wrapping the class declaration in a closure

There is an example of how I have private methods in this codepen. In the snippet below, the Subscribable class has two 'private' functions process and processCallbacks. Any properties can be added in this manner and they are kept private through the use of the closure. IMO Privacy is a rare need if concerns are well separated and Javascript does not need to become bloated by adding more syntax when a closure neatly does the job.

const Subscribable = (function(){

  const process = (self, eventName, args) => {
    self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};

  const processCallbacks = (self, eventName, args) => {
    if (self.callingBack.get(eventName).length > 0){
      const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
      self.callingBack.set(eventName, callingBack);
      process(self, eventName, args);
      nextCallback(...args)}
    else {
      delete self.processing.delete(eventName)}};

  return class {
    constructor(){
      this.callingBack = new Map();
      this.processing = new Map();
      this.toCallbacks = new Map()}

    subscribe(eventName, callback){
      const callbacks = this.unsubscribe(eventName, callback);
      this.toCallbacks.set(eventName,  [...callbacks, callback]);
      return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience

    unsubscribe(eventName, callback){
      let callbacks = this.toCallbacks.get(eventName) || [];
      callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
      if (callbacks.length > 0) {
        this.toCallbacks.set(eventName, callbacks)}
      else {
        this.toCallbacks.delete(eventName)}
      return callbacks}

    emit(eventName, ...args){
      this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
      if (!this.processing.has(eventName)){
        process(this, eventName, args)}}}})();

I like this approach because it separates concerns nicely and keeps things truly private. The only downside is the need to use 'self' (or something similar) to refer to 'this' in the private content.

Paul Whipp
  • 13,812
  • 4
  • 37
  • 47
4

Yes totally can, and pretty easily too. This is done by exposing your private variables and functions by returning the prototype object graph in the constructor. This is nothing new, but take a bit of js foo to understand the elegance of it. This way does not use global scoped, or weakmaps. It is a form of reflection built into the language. Depending on how you leverage this; one can either force an exception which interrupts the call stack, or bury the exception as an undefined. This is demonstarted below, and can read more about these features here

class Clazz {
  constructor() {
    var _level = 1

    function _private(x) {
      return _level * x;
    }
    return {
      level: _level,
      public: this.private,
      public2: function(x) {
        return _private(x);
      },
      public3: function(x) {
        return _private(x) * this.public(x);
      },
    };
  }

  private(x) {
    return x * x;
  }
}

var clazz = new Clazz();

console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //1
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error
1-14x0r
  • 1,598
  • 1
  • 14
  • 18
3
class Something {
  constructor(){
    var _property = "test";
    Object.defineProperty(this, "property", {
        get: function(){ return _property}
    });
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"
  • 2
    It's best to avoid code only answers. It would be better if you could explain how your code answers the OP's question – Stewart_R May 01 '16 at 22:23
  • This is really how to make a readonly variable more than a private variable. A private variable should not be accessible to the outside. `console.log(instance.property)` should throw or give you undefined, not give you back "test". – oooyaya Jul 13 '17 at 19:11
3

Another way similar to the last two posted

class Example {
  constructor(foo) {

    // privates
    const self = this;
    this.foo = foo;

    // public interface
    return self.public;
  }

  public = {
    // empty data
    nodata: { data: [] },
    // noop
    noop: () => {},
  }

  // everything else private
  bar = 10
}

const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined
Jayesbe
  • 83
  • 1
  • 6
3

Reading the previous answer i thought that this example can summarise the above solutions

const friend = Symbol('friend');

const ClassName = ((hidden, hiddenShared = 0) => {

    class ClassName {
        constructor(hiddenPropertyValue, prop){
            this[hidden] = hiddenPropertyValue * ++hiddenShared;
            this.prop = prop
        }

        get hidden(){
            console.log('getting hidden');
            return this[hidden];
        }

        set [friend](v){
            console.log('setting hiddenShared');
            hiddenShared = v;
        }

        get counter(){
            console.log('getting hiddenShared');
            return hiddenShared;
        }

        get privileged(){
            console.log('calling privileged method');
            return privileged.bind(this);
        }
    }

    function privileged(value){
        return this[hidden] + value;
    }

    return ClassName;
})(Symbol('hidden'), 0);

const OtherClass = (() => class OtherClass extends ClassName {
    constructor(v){
        super(v, 100);
        this[friend] = this.counter - 1;
    }
})();

UPDATE

now is it possible to make true private properties and methods (at least on chrome based browsers for now).

The syntax is pretty neat

class MyClass {
    #privateProperty = 1
    #privateMethod() { return 2 }
    static #privateStatic = 3
    static #privateStaticMethod(){return 4}
    static get #privateStaticGetter(){return 5}

    // also using is quite straightforward
    method(){
        return (
            this.#privateMethod() +
            this.#privateProperty +
            MyClass.#privateStatic +
            MyClass.#privateStaticMethod() +
            MyClass.#privateStaticGetter
        )
    }
}

new MyClass().method()
// returns 15

Note that for retrieving static references you wouldn't use this.constructor.#private, because it would brake its subclasses. You must use a reference to the proper class in order to retrieve its static private references (that are available only inside the methods of that class), ie MyClass.#private.

asdru
  • 895
  • 7
  • 15
2

Most answers either say it's impossible, or require you to use a WeakMap or Symbol, which are ES6 features that would probably require polyfills. There's however another way! Check out this out:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

I call this method accessor pattern. The essential idea is that we have a closure, a key inside the closure, and we create a private object (in the constructor) that can only be accessed if you have the key.

If you are interested, you can read more about this in my article. Using this method, you can create per object properties that cannot be accessed outside of the closure. Therefore, you can use them in constructor or prototype, but not anywhere else. I haven't seen this method used anywhere, but I think it's really powerful.

guitarino
  • 337
  • 1
  • 7
  • The question was about how to achieve this in ES6 classes. – Michael Franzl Apr 18 '17 at 21:08
  • You can use the exact same method in ES6 classes. ES6 classes is mainly just sugar on top of functions like I presented in my example. It's quite possible that the original poster is using a transpiler, in which case WeakMaps or Symbols will still require polyfills. My answer is valid regardless. – guitarino Apr 18 '17 at 21:17
2

See this answer for a a clean & simple 'class' solution with a private and public interface and support for composition

kofifus
  • 11,635
  • 8
  • 73
  • 114
2

Actually it is possible.
1. First, create the class and in the constructor return the called _public function.
2. In the called _public function pass the this reference (to get the access to all private methods and props), and all arguments from constructor (that will be passed in new Names())
3. In the _public function scope there is also the Names class with the access to this (_this) reference of the private Names class

class Names {
  constructor() {
    this.privateProperty = 'John';
    return _public(this, arguments);
  }
  privateMethod() { }
}

const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind

function _public(_this, _arguments) {
  class Names {
    constructor() {
      this.publicProperty = 'Jasmine';
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

    somePublicMethod() {
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

  }
  return new Names(..._arguments);
}
Paweł
  • 3,261
  • 2
  • 14
  • 31
2

I found a very simple solution, just use Object.freeze(). Of course the problem is you can't add nothing to the object later.

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode
Nikola Andreev
  • 566
  • 3
  • 16
2

I use this pattern and it's always worked for me

class Test {
    constructor(data) {
        class Public {
            constructor(prv) {

                // public function (must be in constructor on order to access "prv" variable)
                connectToDb(ip) {
                    prv._db(ip, prv._err);
                } 
            }

            // public function w/o access to "prv" variable
            log() {
                console.log("I'm logging");
            }
        }

        // private variables
        this._data = data;
        this._err = function(ip) {
            console.log("could not connect to "+ip);
        }
    }

    // private function
    _db(ip, err) {
        if(!!ip) {
      console.log("connected to "+ip+", sending data '"+this.data+"'");
   return true;
  }
        else err(ip);
    }
}



var test = new Test(10),
  ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined
Yami Teru
  • 29
  • 1
2

You can try this https://www.npmjs.com/package/private-members

This package will save the members by instance.

const pvt = require('private-members');
const _ = pvt();

let Exemplo = (function () {    
    function Exemplo() {
        _(this).msg = "Minha Mensagem";
    }

    _().mensagem = function() {
        return _(this).msg;
    }

    Exemplo.prototype.showMsg = function () {
        let msg = _(this).mensagem();
        console.log(msg);
    };

    return Exemplo;
})();

module.exports = Exemplo;
2

This code demonstrates private and public, static and non-static, instance and class-level, variables, methods, and properties.

https://codesandbox.io/s/class-demo-837bj

class Animal {
    static count = 0 // class static public
    static #ClassPriVar = 3 // class static private

    constructor(kind) {
        this.kind = kind // instance public property
        Animal.count++
        let InstancePriVar = 'InstancePriVar: ' + kind // instance private constructor-var
        log(InstancePriVar)
        Animal.#ClassPriVar += 3
        this.adhoc = 'adhoc' // instance public property w/out constructor- parameter
    }

    #PawCount = 4 // instance private var

    set Paws(newPawCount) {
        // instance public prop
        this.#PawCount = newPawCount
    }

    get Paws() {
        // instance public prop
        return this.#PawCount
    }

    get GetPriVar() {
        // instance public prop
        return Animal.#ClassPriVar
    }

    static get GetPriVarStat() {
        // class public prop
        return Animal.#ClassPriVar
    }

    PrintKind() {
        // instance public method
        log('kind: ' + this.kind)
    }

    ReturnKind() {
        // instance public function
        return this.kind
    }

    /* May be unsupported

    get #PrivMeth(){  // instance private prop
        return Animal.#ClassPriVar + ' Private Method'
    }

    static get #PrivMeth(){  // class private prop
        return Animal.#ClassPriVar + ' Private Method'
    }
    */
}

function log(str) {
    console.log(str)
}

// TESTING

log(Animal.count) // static, avail w/out instance
log(Animal.GetPriVarStat) // static, avail w/out instance

let A = new Animal('Cat')
log(Animal.count + ': ' + A.kind)
log(A.GetPriVar)
A.PrintKind()
A.Paws = 6
log('Paws: ' + A.Paws)
log('ReturnKind: ' + A.ReturnKind())
log(A.adhoc)

let B = new Animal('Dog')
log(Animal.count + ': ' + B.kind)
log(B.GetPriVar)
log(A.GetPriVar) // returns same as B.GetPriVar. Acts like a class-level property, but called like an instance-level property. It's cuz non-stat fx requires instance.

log('class: ' + Animal.GetPriVarStat)

// undefined
log('instance: ' + B.GetPriVarStat) // static class fx
log(Animal.GetPriVar) // non-stat instance fx
log(A.InstancePriVar) // private
log(Animal.InstancePriVar) // private instance var
log('PawCount: ' + A.PawCount) // private. Use getter
/* log('PawCount: ' + A.#PawCount) // private. Use getter
log('PawCount: ' + Animal.#PawCount) // Instance and private. Use getter */
johny why
  • 1,434
  • 6
  • 19
  • 44
1

Here, the myThing variable is private and is part of the closure:

class Person {
  constructor() {

    var myThing = "Hello World";

    return {
      thing: myThing,
      sayThing: this.sayThing
    }
  }

  sayThing() {
    console.log(this.thing);
  }
}

var person = new Person();

console.log(person);
tverghis
  • 917
  • 7
  • 30
1

It is possible to have private methods in classes using WeakMap.

According to MDN web docs:

The WeakMap object is a collection of key/value pairs in which the keys are objects only and the values can be arbitrary values.

The object references in the keys are held weakly, meaning that they are a target of garbage collection (GC) if there is no other reference to the object anymore.

And this is an example of creating Queue data structure with a private member _items which holds an array.

const _items = new WeakMap();

class Queue {    
    constructor() {
        _items.set(this, []);
    }

    enqueue( item) {
        _items.get(this).push(item);
    }    

    get count() {
        return _items.get(this).length;        
    }

    peek() {
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray[0];
    }

    dequeue() {        
        const anArray = _items.get(this);
        if( anArray.length == 0)
            throw new Error('There are no items in array!');

        if( anArray.length > 0)
            return anArray.splice(0, 1)[0];
    }    
}

An example of using:

const c = new Queue();
c.enqueue("one");
c.enqueue("two");
c.enqueue("three");
c.enqueue("four");
c.enqueue("five");
console.log(c);

Private member _items is hided and cannot be seen in properties or methods of an Queue object:

enter image description here

However, private member _items in the Queue object can be reached using this way:

const anArray = _items.get(this);
Community
  • 1
  • 1
StepUp
  • 27,357
  • 12
  • 66
  • 120
1

I realize there are dozens of answers here. I want to share my solution, which ensures true private variables in ES6 classes and in older JS.

var MyClass = (function() {
    var $ = new WeakMap();
    function priv(self) {
       var r = $.get(self);
       if (!r) $.set(self, r={});
       return r;
    }

    return class { /* use priv(this).prop inside your class */ } 
}();

Privacy is ensured by the fact that the outside world don't get access to $.

When the instance goes away, the WeakMap will release the data.

This definitely works in plain Javascript, and I believe they work in ES6 classes but I haven't tested that $ will be available inside the scope of member methods.

frodeborli
  • 1,087
  • 11
  • 23
0

As we know there is no native support for private properties with ES6 classes.

Below is just what I use (might be helpful). Basically I'm wrapping a class inside the factory.

function Animal(name) {
    const privateData = 'NO experiments on animals have been done!';

    class Animal {
        constructor(_name) {
            this.name = _name;
        }
        getName() {
            return this.name
        }
        getDisclamer() {
            return `${privateData} Including ${this.name}`
        }
    }
    return new Animal(name)
}

I'm a beginner so happy to hear if this is a bad approach.

Alex
  • 61
  • 7
0

I have developed a module that helps you use the access restriction in the JavaScript class called Capsulable. (Private & Protected Static)

If you're interested, check out my package below. https://github.com/hmmhmmhm/capsulable

const Capsulable = require('capsulable')
const Field = Capsulable()

class A {
    constructor(_field){
        // Configure data fields.
        Field(this, _field)

        // The code below provides access to
        // the data fields when creating
        // functions within the class.
        Field(this).private
        Field(this).protected
        Field(this).protectedStatic
    }
}

module.exports = A
Bhargav Rao
  • 41,091
  • 27
  • 112
  • 129
hmmhmmhm
  • 31
  • 6
0

I have a workaround that works woo and it pretty simple... although performance is prob not the pest... but it works and works well.

The trick is that until private properties and functions are established and standardized/adopted work arounds are required and this is another workaround...

class ClassPrivateProperties {
    constructor(instance) {
        const $this = instance;
        let properties = {};
        this.prop = (key, value = undefined) => {
            if (!value) {
                return properties[key];
            } else {
                properties[key] = value;
            }
        };
        this.clear = instance => {
            if ($this === instance) {
                properties = {};
                return true;
            } else {
                return false;
            }
        }
    }
}

This is a sample usage that can be what ever (also if you use the above feel free to make it better)

class Test {
    constructor() {
        this._privateProps = new ClassPrivateProperties(this);
    }
    property(key, value = undefined) {
        if (!value) {
            return this._privateProps.prop(key);
        } else {
            this._privateProps.prop(key, value);
        }
    }
    clear() { return this._privateProps.clear(this); }
}
const test = new test;
test.property('myKey','some value here');
console.log(test.property('myKey'));

Like I mentioned that this prob not the best of the best but it works and makes properties truly private.

Jesse Fender
  • 161
  • 11
0

we can emulate a private property of a class using getter and setter.

eg 1

class FootballClub {
    constructor (cname, cstadium, ccurrentmanager) {
        this.name = cname;
        this._stadium  = cstadium;  //  we will treat this prop as private and give getter and setter for this.
        this.currmanager = ccurrentmanager;
    }

    get stadium( ) {
        return this._stadium.toUpperCase();
    }

}

let club = new FootballClub("Arsenal", "Emirates" , "Arteta")
console.log(club);
//FootballClub {
//    name: 'Arsenal',
//    _stadium: 'Emirates',
//    currmanager: 'Arteta'
//  }
console.log( club.stadium ); // EMIRATES
club.stadium = "Highbury"; // TypeError: Cannot set property stadium of #<FootballClub> which has only a getter

In the above example we have not given a setter method for stadium and thus we are not able to set a new value for this. In the next eg a setter is added for stadium

eg 2

class FootballClub {
    constructor (cname, cstadium, ccurrentmanager) {
        this.name = cname;
        this._stadium  = cstadium;  //  we will treat this prop as private and give getter and setter for this.
        this.currmanager = ccurrentmanager;
    }

    get stadium( ) {
        return this._stadium.toUpperCase();
    }

    set stadium(val) {
       this._stadium = val;
    }
}

let club = new FootballClub("Arsenal", "Emirates" , "Arteta")
console.log(club.stadium); // EMIRATES
club.stadium = "Emirates Stadium";
console.log(club.stadium); // EMIRATES STADIUM
Community
  • 1
  • 1
user260778
  • 53
  • 1
  • 4