DEV Community

Joe Pea
Joe Pea

Posted on • Edited on

JavaScript's new class fields will shoot you in the foot!

TLDR, paste the following in your Chrome console:

class Animal {
    #sound = 'rustling noise in the bushes'

    get sound() { return this.#sound }
    set sound(val) { this.#sound = val }

    makeSound() {
        console.log(this.#sound)
    }
}

const a = new Animal
a.makeSound() // 3.14

class Lion extends Animal {
    sound = 'RAWR!'
}

const lion = new Lion
lion.makeSound() // BUG! Expected "RAWR!" but got "rustling noise in the bushes"

The example does not work as you may expect because the expression sound = 'RAWR!' uses [[Define]] semantics instead of [[Set]] semantics.

What are [[Define]] and [[Set]] semantics?

Let me explain using plain ES6 code without class fields.

Using [[Set]] semantics, the Lion class can be written like this:

class Lion extends Animal {
    constructor() {
        super()
        this.sound = 'RAWR!'
    }
}

That version will trigger super class getters/setters in the expression this.sound = 'RAWR!'. The result of lion.makeSound() will be "RAWR!" because that value will be stored inside of the Animal class's private #sound field.

Now, using [[Define]] semantics, we would instead "define" the property on the instance, like so:

class Lion extends Animal {
    constructor() {
        super()
        Object.defineProperty(this, 'sound', { value: 'RAWR!' })
    }
}

This version is what class fields do. Class fields define the property on this directly, so the sound field does not trigger the super class getter/setter that is inside of Animal, therefore the 'RAWR!' value will never be stored inside of Animal's private #sound field. Thus we've been shot in the foot.

Class fields do not keep inheritance as a first-class citizen in the design of JS classes.

This is one of the reasons why many people don't like the class-fields proposal, and it is unfortunate that Chrome's V8 engine is already shipping the feature despite so much push-back from the community.

Here's the design bug reported to Chrome: https://bugs.chromium.org/p/chromium/issues/detail?id=962192

Ever since ES6 features started rolling out, there hasn't been any feature more controversial and disliked than the current class-fields proposal.

If you have any thoughts on (private or public) class-fields, please leave your thoughts over in those GitHub issues. The more community input we get, the better.

Top comments (0)