155

I use Traceur Compiler to have advantage with ES6 features now.

I want to implement this stuff from ES5:

function Animal() {
    var self = this,
        sayHi;

    sayHi  = function() {
        self.hi();
    };

    this.hi = function() {/* ... */}
}

Currently traceur does not support private and public keywords (from harmony). And ES6 class syntax does not allow to use simple var (or let) statements in class body.

The only way that I am find is to simulate privates before class declaration. Something like:

var sayHi = function() {
    // ... do stuff
};

class Animal {
...

It is better then nothing but as expected you can not pass correct this to private method without apply-ing or bind-ing it every time.

So, is there any possibility to use private data in ES6 class compatible with traceur compiler?

Boris Zagoruiko
  • 12,705
  • 15
  • 47
  • 79
  • 1
    Have you considered [6to5](https://6to5.org/)? I prefer it over traceur. I have not used this particular thing, but [check out this snippet](http://es6.zloirock.ru/#class%20C%20%7B%0A%20%20private%20A%2C%20B%0A%20%20constructor(a%2Cb)%7B%0A%20%20%20%20this%3A%3AA%20%3D%20a%3B%0A%20%20%20%20this%3A%3AB%20%3D%20b%3B%0A%20%20%7D%0A%20%20log()%7B%0A%20%20%20%20log(this%3A%3AA%20%2B%20this%3A%3AB)%3B%0A%20%20%7D%0A%7D%0A%0Anew%20C(42%2C%2021).log()%3B) – Sampsa Jan 08 '15 at 20:41
  • 1
    @Sampsa It is fine tool but I can not find anything about double colon(::) syntax from your snippet. Is it from specification or draft? – Boris Zagoruiko Jan 08 '15 at 20:56
  • actually, this question is not an exact duplicate, as this one is about private methods, and the referenced question is about private properties/fields. – bvdb Oct 11 '18 at 09:17
  • 4
    There is a private key now which is `#`. See: https://github.com/tc39/proposal-class-fields – chitzui Jun 01 '19 at 12:36

9 Answers9

243

There are no private, public or protected keywords in current ECMAScript 6 specification.

So Traceur does not support private and public. 6to5 (currently it's called "Babel") realizes this proposal for experimental purpose (see also this discussion). But it's just proposal, after all.

So for now you can just simulate private properties through WeakMap (see here). Another alternative is Symbol - but it doesn't provide actual privacy as the property can be easily accessed through Object.getOwnPropertySymbols.

IMHO the best solution at this time - just use pseudo privacy. If you frequently use apply or call with your method, then this method is very object specific. So it's worth to declare it in your class just with underscore prefix:

class Animal {

    _sayHi() {
        // do stuff
    }
}
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699
alexpods
  • 47,475
  • 10
  • 100
  • 94
  • plus one, psudo privacy is a lot cleaner than other methods atm. – simonzack Feb 01 '15 at 14:43
  • Also something to note, when running Code Climate code coverage on a npm module, any methods that are prefixed with underscore don't count against your coverage score if you don't have tests for them (why would you anyway, their private). ;-) – Marc Feb 23 '15 at 07:31
  • 78
    Really, field privacy in many languages *is* pseudo privacy. Take Java - you can always get the values of an object's 'private' fields using reflection. The privacy in this sense is really just a design pattern - it gives the consumer an easily understandable, tooling/compiler enforceable, interface on what methods should not ever be called. Preceding methods with an underscore is a perfectly sensible implementation of such an interface, even if it's just a convention rather than in the language spec. – davnicwil Oct 08 '15 at 03:54
  • 3
    Not so good for any kind of iteration of functions, especially if people choose different ways of marking which functions are private. `_id`, `id_`, `p_id`, `privateId`, `s_id` .... Example of iteration on functions, `Promise.promisifyAll(SomeClass.prototype)`. – gman Feb 20 '16 at 09:33
  • It sesms private, but it is fully public. This convention might lead developers to wrongly think that a change won't count as breaking, or that tests aren't needed. Check it: http://airbnb.io/javascript/#naming--leading-underscore – Shin Kim Aug 01 '16 at 10:45
  • 17
    "Do not use _ _underbar_ as the first or last character of a name. It is sometimes intended to indicate privacy, but it does not actually provide privacy. If privacy is important, use closure. Avoid conventions that demonstrate a lack of competence." [Code Conventions for the JavaScript Programming Language](http://javascript.crockford.com/code.html) - **Douglas Crockford** – Carlos Araya Sep 08 '16 at 08:13
  • 47
    @CarlosAraya That was written before ES6. He's basically saying "Don't use common conventions for writing classes because classes are not a thing in JS." Except now they are a thing, so using their conventions now makes sense. – BlueRaja - Danny Pflughoeft Sep 23 '16 at 16:26
  • 1
    Is this a case of hungarian notation which everybody hates? – sarkiroka Dec 21 '16 at 20:46
  • @sarkiroka https://en.wikipedia.org/wiki/Hungarian_notation#Systems_vs._Apps_Hungarian what is most disliked is Systems Hungarian. That said - as a semantic variable prefix this _could_ be regarded as an Apps Hungarian style prefix. I personally prefer short variable names with semantics inferred from context (no Apps either), but I'm happy with this syntax - because it provides information which cannot even be inferred from context. Hungarian tends to be redundant. – Iiridayn Feb 05 '18 at 22:59
  • There is a private key now which is `#`. See: https://github.com/tc39/proposal-class-fields – chitzui Jun 01 '19 at 12:36
91

You can always use normal functions:

function myPrivateFunction() {
  console.log("My property: " + this.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
    myPrivateFunction.bind(this)();
  }
}

new MyClass(); // 'My property: myProp'
Max
  • 15,157
  • 17
  • 82
  • 127
  • 7
    Lambdas *don't* bind automatically `this`. Because they don't have `this` at all (i.e. you can't bind a lambda). Source: http://blog.getify.com/arrow-this/ – atoth Mar 21 '16 at 13:37
  • 1
    MDN refers to this as "lexically binding to this". See: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions – Max Mar 21 '16 at 15:42
  • 1
    One downside of the second method is that myPrivateFunction is an instance function. It's not on the prototype chain, so you're bloating up MyClass() if it's something you're going to instantiate multiples of. But good point, it does seem to work. – Jason Farnsworth May 03 '16 at 18:28
  • 6
    Your second code example doesn't support multiple instances; there's only one instance of `myPrivateFunction`. – John Oct 20 '16 at 22:03
  • 1
    Second approach doesn't work as should because `myPrivateFunction` overwrites each time constructor called and "private method" from one instance "binds" to another instance: https://jsfiddle.net/awzfuvq1/ – cornholio Jan 07 '17 at 16:01
  • 3
    Not working example. Although could work for 'myPrivateFunction.call(this)' – adamsko Jun 01 '17 at 13:27
  • 1
    Same approach as what I am using, except that I am replacing bind with apply. This will let you treat it as a function of the class (via this context) and not a global function (even though it technically is). If you're writing this in nodeJS, its not as big of a concern. If you're paranoid, you can always encase it in an IFFE. – Tigertron Oct 22 '17 at 20:50
  • The function will be more of a `global` function, while imho the intention is to still keep the function within the class. – Rafael Herscovici Nov 15 '18 at 14:58
62

Although currently there is no way to declare a method or property as private, ES6 modules are not in the global namespace. Therefore, anything that you declare in your module and do not export will not be available to any other part of your program, but will still be available to your module during run time. Thus, you have private properties and methods :)

Here is an example (in test.js file)

function tryMe1(a) {
  console.log(a + 2);
}

var tryMe2 = 1234;

class myModule {
  tryMe3(a) {
    console.log(a + 100);
  }

  getTryMe1(a) {
    tryMe1(a);
  }

  getTryMe2() {
    return tryMe2;
  }
}

// Exports just myModule class. Not anything outside of it.
export default myModule; 

In another file

import MyModule from './test';

let bar = new MyModule();

tryMe1(1); // ReferenceError: tryMe1 is not defined
tryMe2; // ReferenceError: tryMe2 is not defined
bar.tryMe1(1); // TypeError: bar.tryMe1 is not a function
bar.tryMe2; // undefined

bar.tryMe3(1); // 101
bar.getTryMe1(1); // 3
bar.getTryMe2(); // 1234
Marcelo Lazaroni
  • 9,819
  • 3
  • 35
  • 41
  • 4
    If you want to use class context ```(this)``` inside the private method, you should use ```tryMe1(1).bind(this)``` inside the class. But this will fail if you will use an arrow function. – JacopKane Aug 02 '16 at 21:07
  • @JacopKane But then you'd have to either assign the result of `bind(this)` to a property of `this` (defeating the privacy), or call `bind(this)` every time, which could be a performance problem -- not to mention the [bound function will be slower than a normal function](http://stackoverflow.com/questions/17638305/why-is-bind-slower-than-a-closure). – John Oct 20 '16 at 22:08
  • @John Completely agreed, although without context a private method is not quite useful since it's not a "method" at all without property access. It's becoming just a separate utility function. The only other way around would be passing a reference to the context as an argument I guess. Just thinking out loud. – JacopKane Oct 24 '16 at 20:16
  • Guess what will happen if I create more than 1 instance of your `myModule` and add to it some `setter method` which will change your `tryMe2 ` variable that is shared among all the instances. – Serg Oct 28 '18 at 13:37
  • @Sergey - what?? – Gen1-1 Nov 13 '19 at 16:34
25

You can use Symbol

var say = Symbol()

function Cat(){
  this[say]() // call private methos
}

Cat.prototype[say] = function(){ alert('im a private') }

P.S. alexpods is not correct. he get protect rather than private, since inheritance is a name conflict

Actually you can use var say = String(Math.random()) instead Symbol

IN ES6:

var say = Symbol()

class Cat {

  constructor(){
    this[say]() // call private
  }

  [say](){
    alert('im private')
  }

}
Maxmaxmaximus
  • 2,098
  • 1
  • 19
  • 17
  • `export var say = Symbol();` and `Cat[say]()` also can access `[say](){}` method – Lin Du May 18 '17 at 08:52
  • 7
    @novaline Private variables are not protection from hackers, but protection against accidentally overwriting the property with child classes. – Maxmaxmaximus Jun 19 '17 at 14:20
20

I hope this can be helpful. :)

I. Declaring vars, functions inside IIFE(Immediately-invoked function expression), those can be used only in the anonymous function. (It can be good to use "let, const" keywords without using 'var' when you need to change code for ES6.)

let Name = (function() {
  const _privateHello = function() {
  }
  class Name {
    constructor() {
    }
    publicMethod() {
      _privateHello()
    }
  }
  return Name;
})();

II. WeakMap object can be good for memoryleak trouble.

Stored variables in the WeakMap will be removed when the instance will be removed. Check this article. (Managing the private data of ES6 classes)

let Name = (function() {
  const _privateName = new WeakMap();
})();

III. Let's put all together.

let Name = (function() {
  const _privateName = new WeakMap();
  const _privateHello = function(fullName) {
    console.log("Hello, " + fullName);
  }

  class Name {
    constructor(firstName, lastName) {
      _privateName.set(this, {firstName: firstName, lastName: lastName});
    }
    static printName(name) {
      let privateName = _privateName.get(name);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
    printName() {
      let privateName = _privateName.get(this);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
  }

  return Name;
})();

var aMan = new Name("JH", "Son");
aMan.printName(); // "Hello, JH Son"
Name.printName(aMan); // "Hello, JH Son"
Son JoungHo
  • 201
  • 2
  • 3
13

Have you considered using factory functions? They usually are a much better alternative to classes or constructor functions in Javascript. Here is an example of how it works:

function car () {

    var privateVariable = 4

    function privateFunction () {}

    return {

        color: 'red',

        drive: function (miles) {},

        stop: function() {}

        ....

    }

}

Thanks to closures you have access to all private functions and variabels inside the returned object, but you can not access them from outside.

Nicola Pedretti
  • 4,831
  • 3
  • 36
  • 42
  • 3
    To me, this is the worst possible solution. You're not creating an actual ES6 class here, just returning a POJO. Plus, your private closure is created every time you call your factory, resulting in a higher memory footprint and slower performance. – waldgeist Jul 30 '17 at 11:09
  • 2
    The point is to avoid creating a class. In fact many js developers consider creating classes in JavaScript an anti pattern; for the nature of the language and forthe risks that the keyword 'this' introduces in js. As far as performance goes it won't really matter unless you are creating thousands and thousands of these objects at once; and in that case you would probably have performance issues even using a class. – Nicola Pedretti Jul 30 '17 at 13:53
  • Hm. But how does this answer relate to the question, then? – waldgeist Jul 31 '17 at 16:21
  • 11
    Using factory functions is a valid alternative to using classes that facilitates the creation of private/public methods. If a future reader does not find solutions using classes satisfactory, I think it will be valuable for them to know that they have an alternative that solves the problem. – Nicola Pedretti Jul 31 '17 at 16:47
11

As alexpods says, there is no dedicated way to do this in ES6. However, for those interested, there is also a proposal for the bind operator which enables this sort of syntax:

function privateMethod() {
  return `Hello ${this.name}`;
}

export class Animal {
  constructor(name) {
    this.name = name;
  }
  publicMethod() {
    this::privateMethod();
  }
}

Once again, this is just a proposal. Your mileage may vary.

jamsesso
  • 1,848
  • 2
  • 17
  • 20
5

I came up with what I feel is a much better solution allowing:

  • no need for 'this._', that/self, weakmaps, symbols etc. Clear and straightforward 'class' code

  • private variables and methods are really private and have the correct 'this' binding

  • No use of 'this' at all which means clear code that is much less error prone

  • public interface is clear and separated from the implementation as a proxy to private methods

  • allows easy composition

with this you can do:

function Counter() {
  // public interface
  const proxy = {
    advance,  // advance counter and get new value
    reset,    // reset value
    value     // get value
  }
 
  // private variables and methods
  let count=0;
    
  function advance() {
    return ++count;
  }
     
  function reset(newCount) {
    count=(newCount || 0);
  }
     
  function value() {
    return count;
  }
    
  return proxy;
}
     
let counter=Counter.New();
console.log(counter instanceof Counter); // true
counter.reset(100);
console.log('Counter next = '+counter.advance()); // 101
console.log(Object.getOwnPropertyNames(counter)); // ["advance", "reset", "value"]
<script src="https://cdn.rawgit.com/kofifus/New/7987670c/new.js"></script>

see New for the code and more elaborate examples including constructor and composition

kofifus
  • 17,260
  • 17
  • 99
  • 173
  • 1
    This pattern has always been the logical one of the bunch. Not only do you get hoisting and privates but you can turn the function into a closure and expose public methods by simply returning each function in a object . Additionally, you can leverage `this` scope by binding to methods and if you don't want to call `new` just execute it as an IIFE. Truly is a great way to write code. – User_coder Jun 30 '20 at 02:45
4

As Marcelo Lazaroni has already said,

Although currently there is no way to declare a method or property as private, ES6 modules are not in the global namespace. Therefore, anything that you declare in your module and do not export will not be available to any other part of your program, but will still be available to your module during run time.

But his example didn't show how the private method could access members of the instance of the class. Max shows us some good examples of how access instance members through binding or the alternative of using a lambda method in the constructor, but I would like to add one more simple way of doing it: passing the instance as a parameter to the private method. Doing it this way would lead Max's MyClass to look like this:

function myPrivateFunction(myClass) {
  console.log("My property: " + myClass.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
  }
  testMethod() {
    myPrivateFunction(this);
  }
}
module.exports = MyClass;

Which way you do it really comes down to personal preference.

Community
  • 1
  • 1
Patrick Graham
  • 972
  • 8
  • 18