Skip to content

Instantly share code, notes, and snippets.

@SeanCPP
Last active March 26, 2023 04:17
Show Gist options
  • Save SeanCPP/0973c821b9ba5505afd701e5736b64b4 to your computer and use it in GitHub Desktop.
Save SeanCPP/0973c821b9ba5505afd701e5736b64b4 to your computer and use it in GitHub Desktop.
True Encapsulation in JavaScript: How to Achieve private Accessibility

True Encapsulation in JavaScript

How to achieve "private" in a dynamic language

If you're developing in a dynamically-typed language like JavaScript or Python, you may be dearly missing our friend "private" from statically-typed languages like C#, Java, and C++.

I'm sure plenty of developers know about this already, but I feel like no one ever talks about this. In all my years of studying programming, every time I've searched for how to achieve true encapsulation in JavaScript, the same answer always appears:

You can't.

Wrong!

If your dynamic-language-in-question has the concept of a class and the concept of self-referential instance variables like this or self, then you can achieve this result.

Let's cut to the chase.

JavaScript (ES6)

For this example, let's say we have a Counter component. Ideally, this would hide the actual count variable from the code that consumes it.

Public

class Counter {
    constructor (startAt = 0){
        this.count = startAt;
    }
}

This code cannot be encapsulated because now this.count is publically accessible.

We can utilize scoping rules to achieve true private accessibility. So instead, we will use let to constrain the scope of count to the constructor, and add our behaviors using this.

Private

class Counter {
    constructor (startAt = 0){
        let count = startAt;

        this.getCount = function(){
            return count;
        }
        this.increase = function(){
            ++count;
        }
        this.decrease = function(){
            --count;
        }
    }
}

You can even use the arrow-function syntax to shorten it.

class Counter {
    constructor (startAt = 0){
        let count = startAt;

        this.getCount = () => count;

        this.increase = () => ++count;
        this.decrease = () => --count;
    }
}

Finally, you could even add a convenience getter for the public-facing API

class Counter {
    constructor (startAt = 0){
        let count = startAt;

        this.getCount = () => count;

        this.increase = () => ++count;
        this.decrease = () => --count;
    }

    get count(){
        return this.getCount();
    }
}

Using this component is simple, and the underlying data is truly encapsulated.

     const counter = new Counter();
     counter.increase();
     console.log(counter.count);

Although it's a little bit more code overall, you only need to do this for the data that you want true private accessibility for.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment