使用ES6的getter和setter相比于getProperty/setProperty惯例的优势是什么?

16
class Foo {
    getName = () => this.name;

    setName = (name) => this.name = name;
}

class Foo {
    get name () {
        return this.name;
    }

    set name (name) {
        this.name = name;
    }
}

我可以举出几个ES6 getters劣势的例子,例如:

您无法编写一个getter函数,根据参数值返回一个值:

/**
 * Returns an instance of Card associated with an element.
 *
 * @param {HTMLElement} element
 * @return {Card|undefined}
 */
getCard = (element) => {
    return _.find(this.index, {
        element: element
    });
};

没问题,但是如果你使用这个和ES6,你会引入代码风格不一致的问题。

你无法区分直接属性访问和方法访问。

class Foo {
    get name () {
        return this.name;
    }

    get expensive () {
        // An operation that takes a lot of computational power.
    }

    get randomValue () {
        // Returns random value.
    }
}

let foo = Foo();

foo.name;
foo.expensive;
foo.randomValue;

缺点是,您可能无法直观地知道您正在访问的属性可能需要大量计算功率(因此应该进行记忆化),或者每次访问它时都会更改。

最后,getter/setter 与箭头函数不兼容。无效示例:

class Foo {
    get name = () => {
        return this.name;
    }

    set name = (name) => {
        this.name = name;
    }
}

这使它们暴露于上下文问题。

相对于传统的 get {PropertyName} set {PropertyName} 抽象,使用ES6 getter和setter的优势是什么?


这两个都不是有效的ES6代码?请发布能够展示你实际操作的可运行代码。 - Bergi
@GajusKuizinas: getter 总是在实例上执行...而且您的代码存在语法错误! - Bergi
1
@GajusKuizinas:类属性(你的第一个例子)是一个阶段0 ES7提案 - Felix Kling
1
那你为什么说“ES6 getters和setters比getProperty/setProperty更好”呢?这应该是class Foo { getFoo() { } }class Foo { get foo() {} }的区别。 - Felix Kling
1
@GajusKuizinas:它确实会分散注意力。如果你的问题只是关于getter(语法)与getter(约定)的区别,那么这与ES6+无关。Getter已经在ES5中引入了。 - Felix Kling
显示剩余7条评论
2个回答

13

你无法区分直接属性访问和方法访问。

这是他们支持的主要论点。

编写经典Java风格的面向对象代码最奇怪的痛点之一是,任何具有公开属性的对象都必须编写getter和setter,这会产生大量的样板代码,特别是对于大型数据结构类型对象(例如DTOs)。

所有这些的原因是,您不能只将属性设为public,否则您将无法添加逻辑而不破坏API(例如仅允许设置某些值的逻辑,或者重构并以稍微不同的方式存储属性,同时仍然暴露相同的外部API等)。请参见https://softwareengineering.stackexchange.com/questions/176876/why-shouldnt-i-be-using-public-variables-in-my-java-class,了解一些关于此类问题的典型论点。

我认为近年来这一点已经被推到了逻辑的极限,但这并不意味着它是错误的;通过公开字段以进行直接访问,您确实暴露了存储数据的内部实现方式,这意味着您不能像以前那样轻松或安全地更改它。
ES6 getters / setters 解决了这个问题。事实上,一个对象的某个属性作为直接属性可读性已经不能再告诉你有关该属性的实现方式。它原本可能是一个字段,但最近却变成了 ES6 属性访问器,而 API 并没有改变。属性的实现对代码库的其余部分隐藏起来,因此更容易更改。
缺点是可能不太直观,即您正在访问的某个属性可能需要大量计算能力(因此应该进行备忘),或者每次访问时都会更改。
您说得对,这是一个风险。不过,任何 getX() 都存在这个问题;强烈建议使用像“getName()”这样的简单方法不要在幕后执行昂贵的操作,即使它们是方法,如果违反这一约定,您几乎肯定会让人们受到困扰(包括您自己,从现在开始的 6 个月)。

移动到属性并不会改变这一点,但你说得对,ES6意味着简单属性访问不再保证安全。答案就是你必须确保你(以及其他人)遵循惯例和最小惊奇原则:与现有的简单getter和setter一样,ES6属性访问器应该做简单而廉价的事情,不应在其他地方产生奇怪的副作用。


请注意,自ES5以来JavaScript就拥有了getter和setter,只是“class”语法是新的。 - Bergi
1
周到。我想我从未遇到描述的情况的原因是我从未公开除方法之外的属性。我依赖闭包封装。 - Gajus

4

getMethod()setMethod() 没有任何特定限制,但它们允许使用不同的代码风格。例如,你可以使用 get fooset foo 编写以下代码:

obj.foo++;

这将调用getter然后调用setter。当然,您可以让set函数验证值是否在特定范围内。传统的代码如下:

obj.setFoo(obj.getFoo() + 1);

你无法区分直接属性访问和方法访问。

这就是关键所在。我认为如果某个东西真的很昂贵,你就不应该使用getter方法。


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