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 a class private even if they are set using Symbol 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.

Posted by Afshin T. Darian