Using ES6 Symbols for quasi-private methods & properties
Here are two examples that demonstrate how to use the new ES6 Symbol primitive to create quasi-private methods and properties.
Update: After discussions on reddit and Twitter, it’s clear that
Object.getOwnPropertySymbols()
takes away the ability to truly make members of aclass
private even if they are set usingSymbol
instances. So take this post with a grain of salt. You can mask things from consumers of your APIs, but you can’t completely block access to them.
First, we can use Symbol
instances to create private instance methods and properties. If we keep a single class
per file (and share it by using the export
statement), then its symbols will only be accessible within that class
(except through the use of Object.getOwnPropertySymbols()
, which means that a determined user will still be able to
access it).
// File: foo.js
const quasiPrivateMethod = Symbol();
const quasiPrivateProperty = Symbol();
export default class Foo {
constructor() {
this[quasiPrivateProperty] = 5;
this[quasiPrivateMethod] = () => {
return this[quasiPrivateProperty];
};
}
publicMethod() {
return this[quasiPrivateMethod]();
}
}
// File: bar.js
import Foo from './foo';
const foo = new Foo();
console.log(foo.publicMethod());
// Output: 5
If we want to create a quasi-private prototype
method, rather than creating instance methods, we can use
computed object property names and destructuring
to assign it.
// File: foo.js
const quasiPrivateMethod = Symbol();
const quasiPrivateProperty = Symbol();
export default class Foo {
constructor() {
this[quasiPrivateProperty] = 5;
}
[quasiPrivateMethod]() {
return this[quasiPrivateProperty];
}
publicMethod() {
return this[quasiPrivateMethod]();
}
}
// File: bar.js
import Foo from './foo';
const foo = new Foo();
console.log(foo.publicMethod());
// Output: 5
Addendum: foiled again!
As I noted in the update at the beginning of this post, this is not true privacy. Because a user can iterate through all
of the symbols in an Object
instance, she can still access a property or method that created in this way. But, it
will not be named, its purpose will not be obvious, and reflection techniques are necessary to find out what it does.
It’s not true privacy, but it’s not the same as a well-exposed public API.
I’m leaving this post up so that others who may have thought they had true privacy for ES6 methods and properties might stumble onto it.