private fields and methods in javascript

2020-02-05

 | 

~3 min read

 | 

590 words

When I was first learning about Javascript (so, several months ago), I found this article from Douglas Crockford about private variables and members in Javascript. In it, he begins with the assertion that “JavaScript is the world’s most misunderstood programming language” and then proceeds to claim that “JavaScript objects can have private members” and takes the rest of the article as an opportunity to demonstrate how.

More recently, however, I found an blog post from Tsh.io highlighting several of the features that landed with Node v12. Among the list was private class fields and methods.1

Private class fields are a Stage 3 proposal from TC39, which means they’re still experimental.

However:

  1. Node has support as of v12
  2. Babel has plugin support2

This proposal replaces many of the conventions the Javascript community has found in the past to emulate private methods and fields in the past such as an _ prefix (e.g., _privateVar), closure, proxies, etc. (for more of these approaches, see the resources below).

Private Fields

Let’s look at an example using a Rectangle class.

class Rectangle {
  width = 0
  #height = 0

  constructor(width, height) {
    this.width = width
    this.#height = height
  }

  area() {
    return this.width * this.#height
  }
}

Now, let’s instantiate that class and try accessing its properties:

let rect = new Rectangle(4, 5)
console.log(rect.area()) // 20
console.log(rect.width) // 4
rect.#height = 6console.log(rect.#height)

Things go swimmingly until the last two lines. If either of them are present, you’ll get an error:

SyntaxError: Private field '#height' must be declared in an enclosing class

And while dot notation doesn’t work, bracket notation does not either:

console.log(rect["#height"]) // undefined

Private Methods

The TC39 proposal also includes support for private methods:

class Rectangle {
  width = 0
  #height = 0

  constructor(width, height) {
    this.width = width
    this.#height = height
  }

  #increase(factor) {
    this.width = factor * this.width
    this.#height = factor * this.#height
  }
  area() {
    return this.width * this.#height
  }

  enlarge(by) {
    this.#increase(by)
  }
}

Note, however, that this is not yet supported in Node (as of V12.13).

Conclusion

When it comes to private fields and methods in Javascript, there are a variety of approaches and opinions. Douglas Crockford is on one side proposing we’ve have them since 2001. At the same time TC39’s proposal is pushing us toward a more declarative future.

In any case, I’m excited about the inclusion of private variables within Node 12 (and am continually grateful to the folks behind Babel for allowing the community to push forward with experimental syntax). It’s not yet clear what the final spec for private variables will look like (it’s been in committee for several years at this point), but it’s clear we’re still making progres - that alone is exciting and inspiring enough to make me want to keep learning!

Footnotes

Additional Resources

  1. Implementing Private Variables In Javascript | CSSTricks.com
  2. Marcus Noble on various methods to create private variables in Javascript. Of particular note here are Proxies, which I had not heard of before and his reminder that Typescript has support for Private/Public guarantees, however, these only apply at compile time. Therefore, if you want to rely on Typescript, you need to not ship code which Typescript alerts has errors (even if it’s valid Javascript, as it would be in this case).


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!