Search code examples
javascriptclassecmascript-6

Why do I need a WeakMap to store private data, as opposed to just closing over a variable with limited scope?


I'm trying to understand why I need to use WeakMaps to create private class members, instead of just using a normal variable. They both create encapsulation with closures, and module imports.

(function encapsulation() {
  const my_var = 'My secret info';
  const my_var2 = new WeakMap();

  class Test {
    constructor() {
      my_var2.set(this, 'My secret info 2');
      console.log(my_var); // My secret info
      console.log(my_var2.get(this)); // My secret info 2
    }
  }

  const t = new Test();
})();


console.log(my_var); // undefined
console.log(my_var2); // undefined

// Same result!

Solution

  • The problem with an ordinary variable like my_var is that it will only save data for a single instantiation of the class:

    const Test = (function encapsulation() {
      let my_var = 'My secret info';
    
      class Test {
        constructor(param) {
          my_var = param;
        }
        getInfo() {
          return my_var;
        }
      }
      return Test;
    })();
    
    const t1 = new Test('foo');
    const t2 = new Test('bar');
    console.log(t1.getInfo());
    // the above returns 'bar'... uh oh, but we passed in 'foo' to `t1`! Our data is lost!
    console.log(t2.getInfo()); // 'bar'

    Thus, the need for a WeakMap, to hold separate data for each instantiation:

    const Test = (function encapsulation() {
      const my_var2 = new WeakMap();
    
      class Test {
        constructor(param) {
          my_var2.set(this, param);
        }
        getInfo() {
          return my_var2.get(this);
        }
      }
      return Test;
    })();
    
    const t1 = new Test('foo');
    const t2 = new Test('bar');
    console.log(t1.getInfo()); // 'foo', as expected
    console.log(t2.getInfo()); // 'bar', as expected