Класс JavaScript ES6 только с геттерами

Я хотел бы спросить, как в ES6 можно использовать геттеры только без свойств сеттеров (readOnly)? Почему Webstorm сообщает мне, что это ошибка?

Вот мой код:

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;
    }

}

Я поставил //noinspection JSUnresolvedVariable, чтобы подавить предупреждение. Но должно быть лучшее решение, чем это.


person Grozdan Andonov    schedule 17.02.2017    source источник
comment
Если вы используете классы ES6, по крайней мере, вы также можете использовать ===. Кроме того, с точки зрения твердого программирования, бросок конструктора - действительно плохой план. Это не ответственность конструктора. Если у вас есть требования к типу, убедитесь, что вы передаете уже проверенные данные (что вы все равно должны сделать), или используйте что-то вроде TypeScript, чтобы обеспечить безопасность типов.   -  person Mike 'Pomax' Kamermans    schedule 17.02.2017
comment
Не по теме, но @ Mike'Pomax'Kamermans, почему бы тебе не сделать бросок конструктора? Это кажется наиболее безопасным и естественным способом сообщить об ошибках (которые могут быть, а могут и не быть ошибкой вызывающего абонента).   -  person A. L. Flanagan    schedule 17.02.2017
comment
Возможно не связанная ошибка: в if(typeof genres=="Object"){ должно быть "object" в нижнем регистре.   -  person A. L. Flanagan    schedule 17.02.2017
comment
Такие вещи @ A.L.Flanagan, как stackoverflow.com/a/77797/740553, объясняют это лучше, чем я могу в комментарии.   -  person Mike 'Pomax' Kamermans    schedule 18.02.2017
comment
Спасибо @ Mike'Pomax'Kamermans, в этом есть большой смысл.   -  person A. L. Flanagan    schedule 10.03.2017


Ответы (2)


Похоже, что вы присваиваете значения в конструкторе геттерам вместо полей поддержки с префиксом подчеркивания.

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;
[..]

Если вы этого еще не сделали, вам следует объявить свои резервные поля перед их использованием.

person Leonardo Chaia    schedule 17.02.2017
comment
Я поддержал ваш ответ, но в Javascript нет причин объявлять свойство перед назначением ему. - person jfriend00; 18.02.2017

Ваш код не является идиоматическим JS. Язык типизирован слабо, и его философия основана на утином вводе. То, что вы делаете в своем конструкторе, ужасно, и этого следует избегать. JavaScript - это не Java. Если вам нужна строгая и статическая типизация, используйте Flow или TypeScript.

Геттеры и сеттеры легко использовать в классах ES6, как и геттеры и сеттеры в объектных литералах.

Если вам нужны свойства только для чтения, вы можете использовать _ соглашение о кодировании и просто избегать сеттеров. Если мы возьмем простой пример из документации, мы получим следующий результат:

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

В JavaScript это решение подходит. Но если по какой-то причине вам действительно нужна настоящая инкапсуляция, это не выход. Действительно, к внутренним свойствам с префиксом _ по-прежнему можно обращаться напрямую:

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

Лучшее решение для полной инкапсуляции состоит в использовании IIFE для создания локальной области видимости и Object.freeze() в экземплярах для предотвращения нежелательных изменений.

С геттерами это работает:

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

Без геттеров не работает:

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

person Badacadabra    schedule 09.05.2017
comment
Третий пример не работает, поскольку все экземпляры класса ссылаются на одни и те же переменные в IIFE, поэтому let p1 = new Person( 'a', 'b' ); let p2 = new Person( 'c', 'd' ); console.log(p1.firstname, p1.lastname); выводит c d, а не a b. - person MT0; 01.10.2017