JavaScript ES6类,仅包含getter方法

3
我希望问一下,在ES6中,如何使用只有getter没有setter(只读)的属性?为什么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来抑制警告,但应该有更好的解决方案。


1
如果您正在使用ES6类,至少也可以使用===。另外,作为一个坚实的编程点,使构造函数抛出异常是一个非常糟糕的计划。那不是构造函数的责任。如果您有类型要求,请确保传入已经验证过的数据(无论如何都应该这样做),或者使用像TypeScript这样的东西,以便获得内置的类型安全性。 - Mike 'Pomax' Kamermans
1
离题了,但是@Mike'Pomax'Kamermans,为什么你不应该让构造函数抛出异常呢?这似乎是最安全、最自然的方法来表示错误(这些错误可能是调用者的问题,也可能不是)。 - A. L. Flanagan
可能与此无关的错误:在 if(typeof genres=="Object"){ 中,应该使用小写的 "object" - A. L. Flanagan
1
@A.L.Flanagan,像https://dev59.com/fnVD5IYBdhLWcg3wHn6d#77797这样的东西比我在评论中能够更好地解释它。 - Mike 'Pomax' Kamermans
1
谢谢@Mike'Pomax'Kamermans,这确实很有道理。 - A. L. Flanagan
2个回答

6
看起来你正在构造函数中将值赋给了getter,而不是下划线前缀的后备字段。
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;
[..]

如果你还没有这样做,你应该在使用之前先声明你的后备字段。


2
我赞同了你的回答,但在Javascript中,在给属性赋值之前没有必要声明它。 - jfriend00

-1

你的代码不符合JS的惯用法。这种语言是弱类型的,其哲学基于鸭子类型。你在构造函数中所做的事情很糟糕,应该避免。JavaScript不是Java。如果你想要强类型和静态类型,请使用FlowTypeScript

在ES6类中使用getter和setter非常容易,就像在对象字面量中使用getter和setter一样。

如果你想要只读属性,可以使用_编码约定并简单地避免使用setter。如果我们以文档中的简单示例为例,我们将得到以下结果:

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()来防止不必要的更改。

使用getter,它可以正常工作:

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

没有 getters,它不工作:

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


1
第三个例子无法正常工作,因为类的所有实例都引用IIFE中的相同变量,所以let p1 = new Person('a', 'b'); let p2 = new Person('c', 'd'); console.log(p1.firstname, p1.lastname);输出的是c d而不是a b - MT0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接