0.0.6 • Published 7 years ago

es6-class-privates v0.0.6

Weekly downloads
2
License
MIT
Repository
github
Last release
7 years ago

ES6 Class Privates

Provides a method of converting underscored pseudo-private class members into truly private class members via WeakMap.

Status

Developer preview / proof of concept / idea phase.

Not production tested, yet. Interface could change drastically, and I plan on adding an admittedly hacked way of doing protected methods as well.

Requirements and Install

Currently requires node.js >= 6.0.0

npm install --save es6-class-privates

Why?

In many Javascript projects, so-called private/protected methods are actually public methods prefixed with an underscore. This may tempt even good-intending developers into violating encapsulation for the short-term gains despite the long-term fragility it creates. In fact, a central tenet of object-oriented programming is that private/protected class internals should be able to change without breaking anything other than that class itself (and for protected class members, child-classes as well).

When a private or protected class member is accessed directly outside of the class (which violates encapsulation), the dependent code will break when the private/protected class internals are removed, renamed, or otherwise change behavior. This practice therefore leads to fragility, and should be discouraged.

Eric Elliott: "Underscores are a bad idea. Newbies don't know what they mean, and advanced users think they don't apply to them. Changes break things." (1)

Joe Lencioni from Airbnb (on why underscored pseudo-privates are bad): "They give people a false sense of 'private'ness that could lead to bugs." (2)

Jordan Harband from Airbnb (same discussion): "Private means inaccessible. Your intent to privacy is irrelevant if the value is reachable, ie public. For example, npm broke node once by removing an underscore-prefixed variable." (2)

Unfortunately, once you've got a codebase filled with underscored pseudo-privates, it can be difficult to find a workable path back to true private class members.

Moving private methods into module scope and calling with a modified context (via call, apply, or the new bind operator ::) is a great approach, but some people feel it leaves these methods disconnected from the class.

Typescript has private and protected keywords, and they work great, but switching from Javascript to Typescript is a major undertaking and buy-in can be difficult to gain from stakeholders.

Symbols work well, but can feel kludgy. WeakMaps work well for private state, but are very awkward for private methods. And all of these solutions require a ton of work to implement.

This package is intended to be an easy and pragmatic solution to this all-too-common problem.

General use

const my = require('es6-class-privates').makeMine();

class HelloWorld {
  constructor() {
    my.bindAllTo(this);
    my(this).privateProperty = 'Hello world';
    my(this).privateMethod();
  }

  _privateMethod() {
    console.log(my(this).privateProperty);
  }
}

module.exports = my.restrict(HelloWorld);

API

makeMine(config : Object)

Produces a WeakMap-based method that returns a map for the Object given. If an argument is provided for the config parameter, this will be merged with the global configuration to produce a local configuration specific to this instance of my. See the "Configuration options" section below for more information on the config parameter.

Throws Error when config is invalid.

Example

For a simple example, see "General Use" section, above. A more advanced example follows:

const my = require('es6-class-privates').makeMine({
  protectedPrefix: '_',
  privatePrefix: '__',
  dropPrefix: false,
});

configure(config : Object)

Allows global reconfiguration of this package's behavior. All configuration options are optional, and excluded properties will not modify the configuration. All prefixes must be non-zero-length strings or undefined. Prefixes cannot match.

Throws Error when config is invalid.

Configuration options

  • protectedPrefix : String The characters at the beginning of a class member name indicating that it is protected. Default value is '$'.

  • privatePrefix : String The characters at the beginning of a class member name indicating that it is private. Default value is '_'.

  • dropPrefix : Boolean Whether to include the class member name prefix when restricting the class. Default value is true.

Example

require('es6-class-privates').configure({
  protectedPrefix: '_',
  privatePrefix: '__',
  dropPrefix: false,
});

Run tests

git clone git@github.com:dash-/es6-class-privates.git
cd es6-class-privates
npm test