Protecting object properties against overwriting from console

Viewed 80

I'm trying to protect a property of an object from being overwritten by the console. e.g. I have a person object, with a list of allergies as a property. The list of allergies should be able to be modified, however it should always be a list and an error should be thrown if a user tries writing 'person.allergies = "nonsense" '

I've tried looking into Object.freeze() and Object.seal() but cannot get these to work for this purpose, as I don't believe there is a way to unfreeze an object.

class Person {
    constructor(name){
        this.name = name
        this.allergies = []
    }

    addAllergy(allergy){
        this.allergies.push(allergy)
        return allergy
    }
}

ben = new Person('Ben')     //  Creating a new Object
ben.addAllergy('Dairy')     //  Giving the object a new allergy
ben.allergies               //  Should output ['Dairy']
ben.allergies = 'Soy'       //  Should make no changes to ben object.
2 Answers

You could make allergies a non writable property with Object.defineProperty:

class Person {
    constructor(name){
        this.name = name
        Object.defineProperty(this, 'allergies', {
            value: [],
            writable: false
          });
    }

    addAllergy(allergy){
        this.allergies.push(allergy)
        return allergy
    }
}

ben = new Person('Ben')     //  Creating a new Object
ben.addAllergy('Dairy')     //  Giving the object a new allergy
console.log(ben.allergies)  //  Should output ['Dairy']
ben.allergies = 'Soy'       //  Should make no changes to ben object.
ben.addAllergy('Peanutes')
console.log(ben.allergies)  // still array

writable defaults to false so you don't need to explicitly set it, but I think it makes the intention clearer. configurable also defaults to false which means you can't redefine the property with another call to Object.defineProperty().

Use private properties:

class Person {
    #allergies;

    constructor(name){
        this.name = name
        this.#allergies = []
    }

    addAllergy(allergy){
        this.#allergies.push(allergy)
        return allergy
    }

    get allergies() {
      // you might want to deep clone it here
      // to fully protect from changes the array behind
      return this.#allergies; 
    }

    set allergies(value) {
      throw new Error('haha, I got you!');
    }
}

Private fields are being implemented in the ECMA standard. You can start using them today with babel 7 and stage 3 preset

Source

Related