Я учился использовать классы в JavaScript, и меня всегда смущало то, как работают геттеры и сеттеры. Думаю, теперь я наконец их понял. Правильно ли приведенное ниже объяснение?
Они не отличаются от обычных методов и просто предоставляют альтернативный синтаксис.
Получатель - это просто альтернатива методу, который не может иметь параметра, и означает, что вам не нужно использовать () для вызова, например:

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

Эквивалентно:

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

Сеттер - это просто альтернатива методу, который принимает параметр, например:

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

Эквивалентно:

mySetter(value) { this.value = value };
...
classInstance.mySetter("hello");
1
Max888 14 Сен 2020 в 19:34

3 ответа

Лучший ответ

Функционально это объяснение в основном правильное, но оно также имеет более семантическое значение. Геттеры / сеттеры очень полезны для обновления вещей, зависящих от значения или вычисления значения, но их не следует использовать для запуска действий. Например, это неправильное использование геттера:

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 was called
1
Setter was called with the value 2.
2
D. Pardal 14 Сен 2020 в 16:53

Существует огромная разница между геттерами / сеттерами и обычными свойствами, в их наиболее простой форме вы можете рассматривать их как альтернативный синтаксис. однако геттеры / сеттеры предоставляют более удобные решения для определенных случаев использования - хотя в конечном итоге получатели / сеттеры и методы являются свойствами, у геттеров / сеттеров есть дескрипторы доступа, а у методов есть дескрипторы данных.

Я собираюсь перечислить несколько вариантов использования над головой

  • геттеры / сеттеры позволяют запускать настраиваемые функции при чтении / установке свойства без необходимости создавать два разных метода
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();
  • Другой вариант использования - это пример, который я только что поделился выше, когда вы предоставляете только геттер, который делает значение только для чтения, делая выбросы кода при попытке установить значение - только в строгом режиме
1
ehab 14 Сен 2020 в 17:37

Разница в том, что у вас может быть пара геттер / сеттер с тем же именем. Например, при работе с DOM существует пара геттер / сеттер innerHTML.

const element = document.querySelector("div")

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

element.innerHTML = "Hello!" // Sets the HTML of element to "Hello!"
0
Luke-zhang-04 14 Сен 2020 в 16:38