How to implement private method in ES6 class with Traceur
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"
Related videos on Youtube
Comments
-
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
andpublic
keywords (from harmony). And ES6 class syntax does not allow to use simplevar
(orlet
) 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 withoutapply
-ing orbind
-ing it every time.So, is there any possibility to use private data in ES6 class compatible with traceur compiler?
-
Sampsa over 9 yearsHave you considered 6to5? I prefer it over traceur. I have not used this particular thing, but check out this snippet
-
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 over 5 yearsactually, this question is not an exact duplicate, as this one is about private methods, and the referenced question is about private properties/fields.
-
chitzui almost 5 yearsThere is a private key now which is
#
. See: github.com/tc39/proposal-class-fields
-
-
simonzack over 9 yearsplus one, psudo privacy is a lot cleaner than other methods atm.
-
Marc over 9 yearsAlso 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 over 8 yearsReally, 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 over 8 yearsNot 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 about 8 yearsLambdas don't bind automatically
this
. Because they don't havethis
at all (i.e. you can't bind a lambda). Source: blog.getify.com/arrow-this -
Max about 8 yearsMDN refers to this as "lexically binding to this". See: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
-
Jason Farnsworth about 8 yearsOne 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 almost 8 yearsIt 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 almost 8 yearsIf you want to use class context
(this)
inside the private method, you should usetryMe1(1).bind(this)
inside the class. But this will fail if you will use an arrow function. -
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 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 over 7 yearsYour second code example doesn't support multiple instances; there's only one instance of
myPrivateFunction
. -
John over 7 years@JacopKane But then you'd have to either assign the result of
bind(this)
to a property ofthis
(defeating the privacy), or callbind(this)
every time, which could be a performance problem -- not to mention the bound function will be slower than a normal function. -
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 over 7 yearsIs this a case of hungarian notation which everybody hates?
-
cornholio over 7 yearsSecond 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 about 7 years
export var say = Symbol();
andCat[say]()
also can access[say](){}
method -
adamsko almost 7 yearsNot working example. Although could work for 'myPrivateFunction.call(this)'
-
Maxmaxmaximus almost 7 years@novaline Private variables are not protection from hackers, but protection against accidentally overwriting the property with child classes.
-
waldgeist almost 7 yearsTo 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 almost 7 yearsThe 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 almost 7 yearsHm. But how does this answer relate to the question, then?
-
Nicola Pedretti almost 7 yearsUsing 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 over 6 yearsSame 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 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 over 5 yearsGuess what will happen if I create more than 1 instance of your
myModule
and add to it somesetter method
which will change yourtryMe2
variable that is shared among all the instances. -
Rafael Herscovici over 5 yearsThe function will be more of a
global
function, while imho the intention is to still keep the function within the class. -
chitzui almost 5 yearsThere is a private key now which is
#
. See: github.com/tc39/proposal-class-fields -
Gen1-1 over 4 years@Sergey - what??
-
User_coder almost 4 yearsThis 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 callnew
just execute it as an IIFE. Truly is a great way to write code.