javascript 2.3.5 Прототипное наследование. Запись и this

Операция записи не использует прототип

Прототип используется только для чтения свойств.

Операции записи/удаления работают напрямую с объектом.

В приведённом ниже примере мы присваиваем rabbit собственный метод walk:

let animal = {
  eats: true,
  walk() {
    /* этот метод не будет использоваться в rabbit */
  }
};

let rabbit = {
  __proto__: animal
};

rabbit.walk = function() {
  alert("Rabbit! Bounce-bounce!");
};

rabbit.walk(); // Rabbit! Bounce-bounce!

Теперь вызов rabbit.walk() находит метод непосредственно в объекте и выполняет его, не используя прототип:

Свойства-аксессоры – исключение, так как запись в него обрабатывается функцией-сеттером. То есть, это, фактически, вызов функции.

По этой причине admin.fullName работает корректно в приведённом ниже коде:

let user = {
  name: "John",
  surname: "Smith",

  set fullName(value) {
    [this.name, this.surname] = value.split(" ");
  },

  get fullName() {
    return `${this.name} ${this.surname}`;
  }
};

let admin = {
  __proto__: user,
  isAdmin: true
};

alert(admin.fullName); // John Smith (*)

// срабатывает сеттер!
admin.fullName = "Alice Cooper"; // (**)
alert(admin.name); // Alice
alert(admin.surname); // Cooper

Здесь в строке (*) свойство admin.fullName имеет геттер в прототипе user, поэтому вызывается он. В строке (**) свойство также имеет сеттер в прототипе, который и будет вызван.

Значение «this»

В приведённом выше примере может возникнуть интересный вопрос: каково значение this внутри set fullName(value)? Куда записаны свойства this.name и this.surname: в user или в admin?

Ответ прост: прототипы никак не влияют на this.

Неважно, где находится метод: в объекте или его прототипе. При вызове метода this — всегда объект перед точкой.

Таким образом, вызов сеттера admin.fullName= в качестве this использует admin, а не user.

Это на самом деле очень важная деталь, потому что у нас может быть большой объект со множеством методов, от которого можно наследовать. Затем наследующие объекты могут вызывать его методы, но они будут изменять своё состояние, а не состояние объекта-родителя.

Например, здесь animal представляет собой «хранилище методов», и rabbit использует его.

Вызов rabbit.sleep() устанавливает this.isSleeping для объекта rabbit:

// методы animal
let animal = {
  walk() {
    if (!this.isSleeping) {
      alert(`I walk`);
    }
  },
  sleep() {
    this.isSleeping = true;
  }
};

let rabbit = {
  name: "White Rabbit",
  __proto__: animal
};

// модифицирует rabbit.isSleeping
rabbit.sleep();

alert(rabbit.isSleeping); // true
alert(animal.isSleeping); // undefined (нет такого свойства в прототипе)

Картинка с результатом:

Если бы у нас были другие объекты, такие как birdsnake и т.д., унаследованные от animal, они также получили бы доступ к методам animal. Но this при вызове каждого метода будет соответствовать объекту (перед точкой), на котором происходит вызов, а не animal. Поэтому, когда мы записываем данные в this, они сохраняются в этих объектах.

В результате методы являются общими, а состояние объекта — нет.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *