Почему геттеры или сеттеры не наследуются независимо в JavaScript? - PullRequest
2 голосов
/ 28 марта 2020

Рассмотрим следующее:

class Base {
   _value;
   constructor() {
      this._value = 1;
   }
   get value()  { return this._value; }
   set value(v) { this._value = v;    }
}
class Derived extends Base {
   set value(v) {
      // ...
      super.value = v;
   }
}
const d = new Derived();
d.value = 2;
console.log(d.value); // <-- undefined

Я ожидал, что метод "getter" класса Base будет "унаследованным" в классе Derived, и, следовательно, для отображения значения 2 вместо undefined. Похоже, что оба метода «getter» или «setter» не наследуются независимо, а скорее рассматриваются как единое целое вместе. В том смысле, что если переопределенный метод сеттера отсутствовал или был связан с соответствующим геттером (специально объявленным в производном классе, а не наследуемым) следующим образом:

get value() { return super.value; }

, тогда не будет такой проблемы.

Итак, почему геттеры или сеттеры не наследуются независимо, так как они должны разделить понятия чтения и установки поля

Ответы [ 4 ]

2 голосов
/ 30 марта 2020

Наследование классов JavaScript использует цепочку прототипов, чтобы связать дочерний элемент Constructor.prototype с родительским Constructor.prototype для делегирования. Обычно также вызывается конструктор super(). Эти шаги формируют иерархию родительских / дочерних элементов одного предка и создают самую тесную связь, доступную в ОО-дизайне.

Предлагаю вам прочитать очень хорошую статью о Овладеть интервью JavaScript: в чем разница между классом & Наследование прототипа? написано Эри c Эллиоттом.

ОБНОВЛЕНИЕ

Для уточнения; это ожидаемое поведение, так как вы добавляете новый дескриптор к Derived.prototype. Когда вы добавляете дескриптор, используя get или set, на самом деле создается функция с таким именем, поэтому она будет иметь значение undefined, если не установлено. Это становится как собственное свойство.

Стандарт ECMA-262

14.3.9 Семантика времени выполнения: PropertyDefinitionEvaluation

  1. MethodDefinition : set PropertyName ( PropertySetParameterList ) { FunctionBody }
  2. Пусть propKey будет результат оценки PropertyName . ReturnIfAbrupt ( propKey ).
  3. Если код функции для этого MethodDefinition равен код строгого режима , пусть строгий be true . В противном случае пусть строгий будет ложным .
  4. Пусть scope будет контекстом работающего выполнения '* LexicalEnvironment .
  5. Пусть formalParameterList будет производством FormalParameters : [пусто]
  6. Пусть закрытие будет FunctionCreate (Метод, PropertySetParameterList , FunctionBody , scope , строго ).
  7. Выполнить MakeMethod ( замыкание , объект ).
  8. Выполнение SetFunctionName ( замыкание , propKey , "set").
  9. Пусть des c будет дескриптором Property {{[Set]]: закрытие , [[Enumerable]]: enumerable , [[Configurable]]: true }
  10. Return DefinePropertyOrThrow ( object , propKey, des c).

6.2.4.6 CompletePropertyDescriptor (Des c )

Когда абстрактная операция CompletePropertyDescriptor вызывается с Дескриптор свойства Des c, предпринимаются следующие шаги:

  1. ReturnIfAbrupt ( Des c).
  2. Assert : Des c является дескриптором свойства
  3. Пусть как будет записью {[[Value]]: undefined , [[Writable]]: false , [[Get ]]: undefined , [[Set]]: undefined , [[Enumerable]]: false , [[Configurable]]: false }.
  4. Если IsGenericDescriptor ( Des c) или IsDataDescriptor ( Des c ), то
    • a. Если Des c не имеет поля [[Value]], установите Des c* От 1184 *. [[Value]] до , как . [[Value]].
    • b. Если Des c нет иметь поле [[Writable]], установить Des c. [[Writabl e]] до как . [[Writable]].
  5. Остальное,
    • a. Если Des c не имеет поля [[Get]], установите Des c. [[Get]] на , как . [[Get]].
    • b. Если Des c не имеет поля [[Set]], установите Des c. [[ Установите]] на как . [[Установить]].
  6. Если Des c не имеет [[Enumerable]] установите для поля Des c. [[Enumerable]] значение , как . [[Enumerable]].
  7. Если Des c не имеет поля [[Configurable]], установите Des c. [[Configurable]] на подобно . [[Configurable]].
  8. Возврат Des c.

Также посмотрите на 6.1.7.2 Внутренний объект Методы и внутренние слоты в Таблица 5 - Основные внутренние методы и особенно GetOwnProperty и DefineOwnProperty.

[[GetOwnProperty]] (propertyKey) → Не определено | Дескриптор свойства

Возвращает дескриптор свойства для собственного свойства этого объекта, ключ которого равен propertyKey или undefined , если его нет свойство существует.

[[DefineOwnProperty]] (propertyKey, PropertyDescriptor) → Boolean

Создать или изменить собственное свойство, ключ которого равен propertyKey , чтобы иметь состояние, описанное PropertyDescriptor . Вернуть true , если это свойство было успешно создано / обновлено, или false , если свойство не может быть создано или обновлено.

0 голосов
/ 06 апреля 2020

Я хотел бы подчеркнуть то, что уже говорили другие: это способ Javascript работает .

Javascript был подделан в войнах браузеров . Это набор хаотических c функций, выбранных с учетом компромиссов и обратной совместимости, а не тщательно разработанного языка.

Итак, спрашивается: «Почему геттеры и сеттеры работают так, как они работают?» почти как спрашивать, почему:

console.log(
  (![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]
)
// === fail

или, если мы говорим о OOP, «почему this работает в JS так, как работает?»

Это должно быть достаточно, чтобы ответить на вопрос «почему?».

Но также подумайте:

  1. Как перечислять сеттеры и геттеры, если вы хотите, чтобы они были похожи на обычные функции , которые (обычно) перечислимы в JS? Особенно учитывая, что сеттер и геттер имеют одно и то же имя?

    function A() {}
    A.prototype.f = function() {}
    const obj = new A()
    for(const key in obj) console.log(key, "is own property:", obj.hasOwnProperty(key))
  2. Некоторые люди даже говорят, что вся идея наследования является злом. А в случае с геттерами и сеттерами, я лично думаю, что этот код:

    class B extends A { get x() { ... } }
    

    читается как "класс B имеет свойство только для чтения x" и явный установщик (если он присутствует) сделает этот код чище.

  3. Сам OOP не высечен в камне. Каждый язык реализует его по-своему.

  4. MDN

    JavaScript классы, представленные в ECMAScript 2015 , в основном синтаксические сахар по сравнению с существующим наследованием JavaScript на основе прототипов. Синтаксис класса не не вводит новую модель объектно-ориентированного наследования для JavaScript.

    И геттеры / сеттеры существовали задолго до этого.

  5. Наследование прототипа в те времена считалось некоторыми парадигмой само по себе.

  6. https://esdiscuss.org/topic/getter-and-setter-inheritance

    Я считаю, что геттеры и сеттеры вводят свойства и что то, что мы делаем, должно соответствовать этой модели

    Я не очень понимаю, что под этой моделью они подразумевают, вероятно, они тоже. Но это их мнение. Поправь меня, если я ошибаюсь ... мнения людей повлияли на историю JS (???). И я, возможно, пропустил это, но я не увидел четко сформулированных причин, на которых они основаны.

0 голосов
/ 03 апреля 2020

Проблема, которую я вижу, заключается в использовании супер. если вместо этого вы используете super () в конструкторе для дочернего класса, вы получите весь метод от родительского, так что вам не нужен установщик для значения в дочернем, потому что наследуется

class Base {
   _value;
   constructor() {
      this._value = 1;
   }
   get value()  { return this._value; }
   set value(v) { console.log('value in parent',v), this._value = v;    }
}
class Derived extends Base {
   constructor() {
      super();
  }
}
const d = new Derived();
d.value = 7;
console.log(d.value); // <-- 7
0 голосов
/ 30 марта 2020

Дескрипторы Acessor имеют геттер и сеттер. Вы не можете иметь геттеры / сеттеры без дескрипторов ацессоров. Derived.prototype содержит дескриптор acessor для value, который вы указали в Derived. Вот как работает JavaScript.

Если вы хотите наследовать геттер, вы можете изменить прототип Derived вручную, например так:

Object.defineProperty(Derived.prototype, "value", {
    get: Object.getOwnPropertyDescriptor(Base.prototype, "value").get,
    set: Object.getOwnPropertyDescriptor(Derived.prototype, "value").set
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...