JavaScript类中getter和setter的目的

3
我一直在学习如何在JavaScript中使用类,而一直让我困惑的是getter和setter的工作原理。我现在终于明白了它们是如何工作的,下面的解释正确吗?
它们与普通方法没有区别,只是提供了另一种语法。
getter仅仅是一个无法带有参数的方法的替代品,这意味着您不必使用()进行调用,例如:
get myGetter() { return { msg: "hello" } };
...
classInstance.myGetter.msg; // "hello"

等同于:

myGetter() { return { msg: "hello" } };
...
classInstance.myGetter().msg; // "hello"

Setter是一个方法的替代品,它接受一个参数,例如:

set mySetter(value) { this.value = value };
...
classInstance.mySetter = "hello";

等价于:

mySetter(value) { this.value = value };
...
classInstance.mySetter("hello");
3个回答

3

从功能上讲,这个解释大部分是正确的,但它们也有更语义化的含义。Getter/Setter非常适用于更新依赖于值或计算值的事物,但不应该用于触发操作。例如,下面是一个错误使用Getter的例子:

const alerter = new Alerter;
// [...]
alerter.alert = "Hi there!"; // Alerts "Hi there!"

这是一个好的例子:

const player = new Player;
// [...]
player.health--; // Also updates the health bar

值得注意的是,在大多数情况下,它们的行为类似于方法,但它们实际上并不是方法!它们是属性的一部分。

在JS中,属性可以有数据描述符和访问器描述符。数据描述符是“普通”属性。它们有一个值,你可以获取/设置它。

const obj = {
  prop: 1;
};

console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set

访问器描述符不保存值,允许设置当获取和设置属性时发生的操作。

const obj = {};
Object.defineProperty(obj, "prop", {
  get() {
    console.log("Getter was called");
    return 1;
  },
  set(v) {
    console.log("Setter was called with the value %o.", v)
  }
});

/* Alternative syntax:

class Example {
  get prop() {
    console.log("Getter was called");
    return 1;
  }
  set prop(v) {
    console.log("Setter was called with the value %o.", v)
  }
}
const obj = new Example;
*/

console.log(obj.prop); // Get; logs 1
obj.prop = 2; // Set

那段代码记录了:

Getter被调用
1
Setter被调用,值为2。

1
在获取器/设置器和普通属性之间存在巨大的区别,在它们最简单的形式下,您可以将它们视为替代语法。然而,获取器/设置器为某些用例提供了更方便的解决方案 - 尽管最终获取器/设置器和方法都是属性,但获取器/设置器具有访问器描述符,而方法具有数据描述符。
我会列出一些我能想到的用例
- 获取器/设置器使您能够在读取/设置属性时触发自定义功能,而无需创建两个不同的方法
let xThatShouldBeHidden = 1;
const object = { 
   get x() {
    return xThatShouldBeHidden
   },
   set x(newX) {
     if (newX === 0) {
       throw new Error('You can not set x to 0')
     }
     xThatShouldBeHidden = newX
   }      
}

触发自定义功能是一个很酷的特性,它使您能够在简单语法背后进行优化。 想象一下,您有一个具有值的项目数组,然后您想要获取项目的权重(值/所有项目值的总和)。
const items = [{val: 2}, {val:4}]
one way to do it would be which required you to loop twice even if eventually the weight was read from only one item
const totalSum = items.reduce((acc,cur), acc + cur.val,0));
const itemsWithWeights = items.map(item => ({...item, weight: item.val / totalSum});

now with getters we do it in one loop plus number of actual reads
const getItemsWithWeightsGetter = () => {
  let totalSum;

  return items.map(item => ({
    ...item,
    get weight() {
      if (totalSum === undefined) {
        totalSum = items.reduce((acc, cur) => acc + cur.val, 0);
      }
      return item.val / totalSum;
    },
  }));
};

const itemsWithWeightsGetter = getItemsWithWeightsGetter();

另一个用例是我刚刚分享的例子,当您提供只读取器时,使值为只读状态,如果尝试设置该值,则代码会抛出异常 - 仅在严格模式下。

0
不同的是,你可以拥有同名的getter/setter对。例如,在与DOM(文档对象模型)一起工作时,存在着innerHTML getter/setter对。
const element = document.querySelector("div")

console.log(element.innerHTML) // Outputs HTML as string

element.innerHTML = "Hello!" // Sets the HTML of element to "Hello!"

啊,是的,我没有考虑到这一点,否则你将不得不在一个方法中处理设置和获取的情况。谢谢! - undefined

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