How to implement private method in ES6 class with Traceur [duplicate]
How to implement private method in ES6 class with Traceur [duplicate]
This question already has an answer here:
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.
private
public
var
let
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.
this
apply
bind
So, is there any possibility to use private data in ES6 class compatible with traceur compiler?
This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.
@Sampsa It is fine tool but I can not find anything about double colon(::) syntax from your snippet. Is it from specification or draft?
– Glen Swift
Jan 8 '15 at 20:56
9 Answers
9
There are no private
, public
or protected
keywords in current ECMAScript 6 specification.
private
public
protected
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.
private
public
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
.
WeakMap
Symbol
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:
apply
call
class Animal {
_sayHi() {
// do stuff
}
}
Thank you for clarification.
– Glen Swift
Jan 9 '15 at 8:13
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 8 '15 at 3:54
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 9:33
_id
id_
p_id
privateId
s_id
Promise.promisifyAll(SomeClass.prototype)
"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
– Carlos Araya
Sep 8 '16 at 8:13
@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
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'
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– atoth
Mar 21 '16 at 13:37
this
this
MDN refers to this as "lexically binding to this". See: developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
– Max
Mar 21 '16 at 15:42
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 3 '16 at 18:28
Your second code example doesn't support multiple instances; there's only one instance of
myPrivateFunction
.– John
Oct 20 '16 at 22:03
myPrivateFunction
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– Sad Luigi
Jan 7 '17 at 16:01
myPrivateFunction
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)
test.js
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
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 2 '16 at 21:07
(this)
tryMe1(1).bind(this)
@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.– John
Oct 20 '16 at 22:08
bind(this)
this
bind(this)
@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
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
var say = String(Math.random())
IN ES6:
var say = Symbol()
class Cat {
constructor(){
this[say]() // call private
}
[say](){
alert('im private')
}
}
export var say = Symbol();
and Cat[say]()
also can access [say](){}
method– novaline
May 18 '17 at 8:52
export var say = Symbol();
Cat[say]()
[say](){}
@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
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.
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"
Some detail about why that solution works would be helpful. Just providing code while might be correct doesn't really answer the question as well as it could. I recommend editing your answer to include more detail about why that solution works and explain the code you suggested.
– Charlie Fish
Aug 13 '16 at 16:09
I'm sorry for that. I'm editing it
– Son JoungHo
Aug 13 '16 at 16:18
Still could use a little bit of work.
– Charlie Fish
Aug 13 '16 at 16:42
I hope it can be satisfied.
– Son JoungHo
Aug 13 '16 at 17:15
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.
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
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
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
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.
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"]
https://cdn.rawgit.com/kofifus/New/7987670c/new.js
see New for the code and more elaborate examples including constructor and composition
Have you considered 6to5? I prefer it over traceur. I have not used this particular thing, but check out this snippet
– Sampsa
Jan 8 '15 at 20:41