Search code examples
javascriptclassecmascript-6private-members

How can I use WeakMaps to store multiple different private values?


I encountered a problem while working with private members of classes in javascript, which I cannot solve myself.

"use strict";
var privateData = new WeakMap();


class Fruit{
    constructor(name){
        privateData.set(this, {name: name});
    }

    name(){
        return privateData.get(this).name;
    }
}

class Orange extends Fruit{
    constructor(){
        super("Orange");
    privateData.set(this, {color: "blue"});
    }

    color(){
        return privateData.get(this).color;
    }
}

var fruit = new Fruit("Apple");
alert(fruit.name());
var orange = new Orange();
alert(orange.name());

First Output: Apple Second Output: undefined

My guess is, I'm overwriting 'this', because if I delete

return privateData.get(this).color;

it works.


Solution

  • Yes, objects are unique, and you are using the this instance as a key twice. The second set call will overwrite the value from the first call, and only leave the object with the .color property.

    The easiest solution would be to use one weak map per attribute:

    var privateNames = new WeakMap();
    class Fruit {
        constructor(name) {
            privateNames.set(this, name);
        }
        get name() {
            return privateNames.get(this);
        }
    }
    
    var privateColors = new WeakMap();
    class Orange extends Fruit {
        constructor() {
            super("Orange");
            privateColors.set(this, "blue");
        }
        get color() {
            return privateColors.get(this);
        }
    }
    

    Alternatively, you will have to mutate the stored data object:

    var privateData = new WeakMap();
    class Fruit {
        constructor(name) {
            privateData.set(this, {name: name});
        }
        get name() {
            return privateData.get(this).name;
        }
    }
    
    class Orange extends Fruit {
        constructor() {
            super("Orange");
            privateData.get(this).color = "blue";
        }
        get color() {
            return privateData.get(this).color;
        }
    }