admin管理员组

文章数量:1336568

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.

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.

Share edited Oct 1, 2019 at 12:20 Marius Mucenicu 1,7932 gold badges17 silver badges26 bronze badges asked Feb 17, 2017 at 18:49 Grozdan AndonovGrozdan Andonov 331 silver badge5 bronze badges 5
  • 1 If you're using ES6 classes, at the very least you can use ===, too. Also, as a point of solid programming, making a constructor throw is a really bad plan. That's not the responsibility of a constructor. If you have type requirements, either make sure you're passing in already validated data (which you should do anyway), or use something like TypeScript so you get type safety baked in. – Mike 'Pomax' Kamermans Commented Feb 17, 2017 at 18:55
  • 1 Off topic, but @Mike'Pomax'Kamermans, why shouldn't you make a constructor throw? That seems the safest, most natural way to signal errors (which may or may not be the fault of the caller). – A. L. Flanagan Commented Feb 17, 2017 at 19:31
  • Possibly unrelated error: in if(typeof genres=="Object"){, it should be "object", lowercase. – A. L. Flanagan Commented Feb 17, 2017 at 19:34
  • 1 @A.L.Flanagan things like stackoverflow./a/77797/740553 explain it better than I can in a ment. – Mike 'Pomax' Kamermans Commented Feb 17, 2017 at 22:01
  • 1 Thanks @Mike'Pomax'Kamermans, that actually makes a lot of sense. – A. L. Flanagan Commented Mar 10, 2017 at 19:10
Add a ment  | 

2 Answers 2

Reset to default 6

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

本文标签: ecmascript 6JavaScript ES6 class with getters onlyStack Overflow