Using setters in Javascript to "override" properties

Viewed 362

The MDN page about set seems to state that an

[ECMAScript 2015] setter must not appear in an object literal ... with a data entry for the same property.

However when using the super keyword this no longer seems to apply.

class Foo {
    constructor(bar){
        this.bar = bar
    }

    set bar(newBar){
        if (!newBar.match(/\w+/))
            throw Error("Invalid bar value")
        // I can use super despite not being a derived class.
        return super.bar = newBar
    }
}
const baz = new Foo("Baz")
baz.bar = "new value" // No recursion

This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.

But the set syntax is a bit of a black-box and I can't tell what it's actually doing.

Am I breaking something here or is it okay to use?

Also what is super referencing here?

1 Answers

This seems like a useful feature as the property doesn't have to be "hidden" by prefixing it with an underscore or something. Plus I don't have to mess with the property enumerability to avoid the "hidden" version from showing in a loop or serialization.

No, it's not useful. It's a hack at best, and doesn't do what you expect.

There is nothing hidden here at all. You are creating a new property with the name bar on the instance itself, shadowing any getters/setters you had defined on the prototype. The second assignment does not get your setter caller. Also the instance property is a normal enumerable property, so it will show up in for in loops and serialisation.

Also what is "super" referencing here?

The super keyword refers to the prototype of the object that the method (or setter) is defined on, i.e. Object.getPrototypeOf(Foo.prototype). This is the Object.prototype in your case, since your class Foo doesn't extend anything.

The .foo access will be looked up on that prototype, and would normally find a method that you inherited from your parent class or something. When using that, the property reference super.foo will however make the receiver of the operation (i.e. what would the this keyword in a method invocation) be the current this instance, not the prototype.

In your case, it's not a method call but an assignment. This could run a setter inherited from the parent class, but in your case there is no Object.prototype.foo property so it will fall back to standard assignment on the target - and that target is the baz instance itself, where a new own property will be created.

So no, it is not okay to use.

Related