How to implement private method in ES6 class with Traceur

247,295

Solution 1

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
    }
}

Solution 2

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'

Solution 3

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

Solution 4

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')
  }

}

Solution 5

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"
Share:
247,295

Related videos on Youtube

Glen Swift
Author by

Glen Swift

I dare you, i double dare you, say what again

Updated on July 08, 2022

Comments

  • Glen Swift
    Glen Swift almost 2 years

    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?

    • Sampsa
      Sampsa over 9 years
      Have you considered 6to5? I prefer it over traceur. I have not used this particular thing, but check out this snippet
    • Glen Swift
      Glen Swift over 9 years
      @Sampsa It is fine tool but I can not find anything about double colon(::) syntax from your snippet. Is it from specification or draft?
    • bvdb
      bvdb over 5 years
      actually, this question is not an exact duplicate, as this one is about private methods, and the referenced question is about private properties/fields.
    • chitzui
      chitzui almost 5 years
      There is a private key now which is #. See: github.com/tc39/proposal-class-fields
  • simonzack
    simonzack over 9 years
    plus one, psudo privacy is a lot cleaner than other methods atm.
  • Marc
    Marc over 9 years
    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). ;-)
  • davnicwil
    davnicwil over 8 years
    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.
  • gman
    gman over 8 years
    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).
  • atoth
    atoth about 8 years
    Lambdas don't bind automatically this. Because they don't have this at all (i.e. you can't bind a lambda). Source: blog.getify.com/arrow-this
  • Max
    Max about 8 years
    MDN refers to this as "lexically binding to this". See: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
  • Jason Farnsworth
    Jason Farnsworth about 8 years
    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.
  • Shin Kim
    Shin Kim almost 8 years
    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: airbnb.io/javascript/#naming--leading-underscore
  • JacopKane
    JacopKane almost 8 years
    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.
  • Carlos Araya
    Carlos Araya over 7 years
    "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 - Douglas Crockford
  • BlueRaja - Danny Pflughoeft
    BlueRaja - Danny Pflughoeft over 7 years
    @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.
  • John
    John over 7 years
    Your second code example doesn't support multiple instances; there's only one instance of myPrivateFunction.
  • John
    John over 7 years
    @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.
  • JacopKane
    JacopKane over 7 years
    @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.
  • sarkiroka
    sarkiroka over 7 years
    Is this a case of hungarian notation which everybody hates?
  • cornholio
    cornholio over 7 years
    Second approach doesn't work as should because myPrivateFunction overwrites each time constructor called and "private method" from one instance "binds" to another instance: jsfiddle.net/awzfuvq1
  • slideshowp2
    slideshowp2 about 7 years
    export var say = Symbol(); and Cat[say]() also can access [say](){} method
  • adamsko
    adamsko almost 7 years
    Not working example. Although could work for 'myPrivateFunction.call(this)'
  • Maxmaxmaximus
    Maxmaxmaximus almost 7 years
    @novaline Private variables are not protection from hackers, but protection against accidentally overwriting the property with child classes.
  • waldgeist
    waldgeist almost 7 years
    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.
  • Nicola Pedretti
    Nicola Pedretti almost 7 years
    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.
  • waldgeist
    waldgeist almost 7 years
    Hm. But how does this answer relate to the question, then?
  • Nicola Pedretti
    Nicola Pedretti almost 7 years
    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.
  • Tigertron
    Tigertron over 6 years
    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.
  • Iiridayn
    Iiridayn over 6 years
    @sarkiroka en.wikipedia.org/wiki/… 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.
  • Serg
    Serg over 5 years
    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.
  • Rafael Herscovici
    Rafael Herscovici over 5 years
    The function will be more of a global function, while imho the intention is to still keep the function within the class.
  • chitzui
    chitzui almost 5 years
    There is a private key now which is #. See: github.com/tc39/proposal-class-fields
  • Gen1-1
    Gen1-1 over 4 years
    @Sergey - what??
  • User_coder
    User_coder almost 4 years
    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.