Blog

Front End Development topics with a touch of humor

A privacy hack

Useful uses of closures

October 14, 2020


It is one thing to say that functions execute using the variable scope in which they were defined, not the variable scope in which they are executed, but quite another to find a use for this information.

(If you don't know what that sentence means, read my earlier couple of posts, in which we talk about closures. I promise both parts of that sentence will make sense then!)

The most common scenario is that of an inner function returned by an outer function, and the inner function "remembering" or having access to local variable of the outer function. That seems more of a quirk or weird peculiarity of JavaScript than a feature, yet it's one quite beloved of JavaScript developers because it can be used to create privacy.

Before we hack out some privacy, we might ask in what way is JavaScript not private?

In the following code, I make an object.

              const vault = {
                  gold: 10000,
                  electrum: 500
                  //electrum is half-gold, half-silver!
              }
          

The object is a storehouse of valuables. But there's a problem: just anyone can take those valuables by accessing the properties directly.

              vault.gold = 9000;
              //9000
              vault.electrum = 400;
              //400
          

Oh noes! Someone has skimmed off some gold and electrum. Do you think it was a pirate? Or maybe the bookkeeper--I need to have a talk with her...

One way to solve this problem is to use Object.freeze().

              Object.freeze(vault);
              vault.gold = 8000;
              //8000
              console.log(vault.gold);
              //9000
          

Interesting... It looked as if you could still change the value, but when you check--it's back to what it was. A tricky security measure, to be sure.

There is a problem, though. You can't unfreeze an object. So now our vault is closed forever, inaccessible even to legitimate users! Well, this is awkward...

We have made our vault immutable. That's good practice in many cases, but not here. Let's reboot the experiment and forget all about Object.freeze() for the moment. I submit to you that what we desire here, is not immutability but privacy. So we come to the topic of this post, how to achieve privacy using closures.

The inner function combined with its variable scope is called a closure.

            function bankVault() {
                let gold = 9000;
                return function writeGold() {
                    console.log(gold);
                }
            }
            const goldWriter = bankVault();
            goldWriter();
            //9000
          

Here we have made a function that returns a function. The inner function, writeGold, we assign to goldWriter in the global namespace. When we invoke it, as it has access to bankVault's variables, it helpfully informs us of the value.

However, if we try to directly access gold, what happens?

            VM1625:1 Uncaught ReferenceError: gold is not defined
            at <anonymous>:1:1
          

No soup for you!

That's pretty cool, but let's say we wanted to do a bit more with our gold. After all, it's nice to know how much we have, but what good is being rich, rich, rich! if you can't spend it? Let's modify our function.

          function bankVault() {
              let gold = 9000;
              return {
                  writeGold() {
                      console.log(gold);
                  },
                  getGold() {
                      gold-=1000;
                  }
                }
              }
            

Notice this is a slightly different thing. Instead of returning a function, I'm returning an object with two methods. But due to the magic of closures, the same thing should work: private access to the outer function's variables.

              const goldGetter = bankVault();
              goldGetter.writeGold();
              //9000
              goldGetter.getGold();
              goldeGetter.writeGold();
              //8000
          

And it works! We assign the returned object to the variable goldGetter, then access its methods normally. Those methods, even when invoked in the global namespace, are able to read and modify the variables inside the outer function.

Behold the magic of closures! Tell the mob we have their gold!