简体   繁体   中英

JavaScript ES6 class with getters only

I would like to ask, in ES6 how can I use getters only without setters (readOnly) properties? Why is Webstorm telling me that this is an error?

Here is my code:

class BasePunchStarter {

constructor(id,name,manufacturer,description,genres,targetPrice) {
    if (new.target==BasePunchStarter) {
        throw new TypeError("BasePunchStarter class cannot be instantiated directly!");
    }
    if (typeof id =="number") {
        // noinspection JSUnresolvedVariable
        this.id = id;
    } else throw new TypeError("ID must be a number!");
    if (typeof name=="string") {
        // noinspection JSUnresolvedVariable
        this.name = name;
    } else throw new TypeError("Name must be a string!");
    if(typeof manufacturer=="string") {
        // noinspection JSUnresolvedVariable
        this.manufacturer = manufacturer;
    } else throw new TypeError("Manufacturer must be a string!");
    if (typeof description=="string") {
        // noinspection JSUnresolvedVariable
        this.description = description;
    } else throw new TypeError("Description must be a string!");
    if(typeof genres=="Object") {
        // noinspection JSUnresolvedVariable
        this.genres=genres;
    } else new TypeError("Genres must be an Array of strings!");
    if (typeof targetPrice=="number") {
        // noinspection JSUnresolvedVariable
        this.targetPrice = targetPrice;
    } else new TypeError("Target price must be a number!");
    this.accumulatedMoney=0;
}
get accumulatedMoney() {
    return this._accumulatedMoney;
}
set accumulatedMoney(money) {
    this._accumulatedMoney=money;
}
get id() {
    return this._id;
}
get name() {
    return this._name;
}
get manufacturer() {
    return this._manufacturer;
}
get description() {
    return this._description;
}
get genres() {
    return this._genres;
}
get targetPrice() {
    return this._targetPrice;
}

I had put //noinspection JSUnresolvedVariable to suppress the warning. But there should be better solution than this.

It seems that you're assigning the values on the constructor to the getters instead of the backing fields prefixed with underscore.

constructor(id,name,manufacturer,description,genres,targetPrice){
    if(new.target==BasePunchStarter){
        throw new TypeError("BasePunchStarter class cannot be instantiated directly!");
    }
    if(typeof id =="number") {
        // use the backing field instead.
        this._id = id;
[..]

In case you're not doing it already, you should declare your backing fields before using them.

Your code is not idiomatic JS. The language is loosely typed and its philosophy is based on duck typing . What you do in your constructor is awful and should be avoided. JavaScript is not Java. If you want strong and static typing, use Flow or TypeScript .

Getters and setters are easy to use in ES6 classes, just like getters and setters in object literals.

If you want read-only properties, you can use the _ coding convention and simply avoid setters. If we take the simple example from the documentation, we have the following result:

 class Person { constructor(firstname, lastname) { this._firstname = firstname; this._lastname = lastname; } get firstname() { return this._firstname; } get lastname() { return this._lastname; } } let person = new Person('John', 'Doe'); console.log(person.firstname, person.lastname); // John Doe // This is ignored person.firstname = 'Foo'; person.lastname = 'Bar'; console.log(person.firstname, person.lastname); // John Doe 

In JavaScript, this solution is just fine. But if, for some reason, you really want true encapsulation, this is not the way to go. Indeed, internal properties with the _ prefix can still be accessed directly:

 class Person { constructor(firstname, lastname) { this._firstname = firstname; this._lastname = lastname; } get firstname() { return this._firstname; } get lastname() { return this._lastname; } } let person = new Person('John', 'Doe'); console.log(person.firstname, person.lastname); // John Doe // This is NOT ignored person._firstname = 'Foo'; person._lastname = 'Bar'; console.log(person.firstname, person.lastname); // Foo Bar 

The best solution for full encapsulation consists in using an IIFE to create a local scope and Object.freeze() on instances to prevent unwanted changes.

With getters, it works:

 let Person = (() => { let firstname, lastname; class Person { constructor(first, last) { firstname = first; lastname = last; } get firstname() { return firstname; } get lastname() { return lastname; } } return Person; })(); let person = new Person('John', 'Doe'); Object.freeze(person); console.log(person.firstname, person.lastname); // John Doe // This is ignored person.firstname = 'Foo'; person.lastname = 'Bar'; console.log(person.firstname, person.lastname); // John Doe 

Without getters, it does not work:

 let Person = (() => { let firstname, lastname; class Person { constructor(first, last) { firstname = first; lastname = last; } } return Person; })(); let person = new Person('John', 'Doe'); Object.freeze(person); console.log(person.firstname, person.lastname); // undefined undefined // This is ignored person.firstname = 'Foo'; person.lastname = 'Bar'; console.log(person.firstname, person.lastname); // undefined undefined 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM