прототип существует во всех объектах как встроенное свойство. Это свойство помогает разделять функции между экземплярами объекта…

Оглавление

Функциональная реализация

Функциональная реализация означает создание объектов с функциями. Эти функции называются функциями-конструкторами.

// Constructor function
function Todo(title, description, creationDate){
 let todo = {};
 
 todo.title = title;
 todo.description = description;
 todo.creationDate = creationDate;
 todo.archiveLocation = "";

 todo.moveToArchive = function (archiveLocation) {
  console.log(`${this.title} moved to archive.`);
  this.archiveLocation = archiveLocation;
 }

 return todo; // Return created object.
}

const myTodo = Todo("myTodo-title", "myTodo-description", new Date());

Но в приведенном выше примере есть проблема. Эта проблема связана с чрезмерным использованием памяти, поскольку мы создаем новую функцию moveToArchive каждый раз, когда создается объект, и мы не совместно используем эту функцию среди экземпляров объекта. Решение…

Функциональная реализация с общими методами

const todoMethods = {
 moveToArchive(archiveLocation) {
  console.log(`${this.title} moved to archive.`);
  this.archiveLocation = archiveLocation;
 }
};

function Todo(title, description, creationDate){
   let todo = {};
   
   todo.title = title;
   todo.description = description;
   todo.creationDate = creationDate;
   todo.archiveLocation = "";
  
   // Share the function between instances.
   todo.moveToArchive = todoMethods.moveToArchive;
  
   return todo;
}

const myTodo = Todo("myTodo-title", "myTodo-description", new Date());

Теперь мы разделяем функцию moveToArchive между экземплярами объекта, но когда мы планируем добавить функцию в todoMethods, мы также должны изменить функцию конструктора Todo. . С помощью функции Object.create мы можем неявно копировать свойства todoMethods.

//...
function Todo(title, description, creationDate){
   let todo = Object.create(todoMethods);

   todo.title = title;
   todo.description = description;
   todo.creationDate = creationDate;
   todo.archiveLocation = "";

   return todo;
}
// ...

Теперь, когда мы добавляем функцию в todoMethods, нам не нужно изменять функцию-конструктор Todo.

Прототип

В Javascript каждый объект имеет встроенное свойство prototype. Мы можем использовать это свойство вместо управления todoMethods в другом объекте.

Функции массива, такие как concat, filter и find, наследуются с использованием свойства прототипа. (например, Array.prototype.concat, Array.prototype.filter)

function Todo(title, description, creationDate){
   // Now we are inheriting functions from prototype
   let todo = Object.create(Todo.prototype);

   todo.title = title;
   todo.description = description;
   todo.creationDate = creationDate;
   todo.archiveLocation = "";
  
   return todo;
}

// Put the function into prototype.
Todo.prototype.moveToArchive = function(archiveLocation) {
 console.log(`${this.title} moved to archive.`);
 this.archiveLocation = archiveLocation;
};

const myTodo = Todo("myTodo-title", "myTodo-description", new Date());
myTodo.moveToArchive();

Как насчет статических методов?

Статические методы определяются непосредственно как свойство объекта, поскольку они не требуют экземпляра и не переопределяются.

Todo.prototype.moveToArchive = function()...
Todo.staticMethod = function()...

Мы можем использовать функцию hasOwnProperty. Эта функция возвращает true, если свойство существует в самом объекте (не в его прототипе), в противном случае возвращает false.

//...
const myTodo = Todo("myTodo-title", "myTodo-description", new Date());
console.log(myTodo.hasOwnProperty('description')); // true
console.log(myTodo.hasOwnProperty('staticMethod')); // false
console.log(myTodo.hasOwnProperty('moveToArchive')); // true ->
// Because we are creating the todo with Object.create(Todo.prototype) 

Что такое ключевое слово this и как происходит наследование прототипов?

Когда мы вызываем свойство объекта, JS проверяет, есть ли у объекта собственное свойство (как выше). Если у объекта есть соответствующее свойство, оценка завершается, в противном случае JS обращается к прототипу объекта. (т.е. родительский объект)

Давайте посмотрим на пример из MDN:

const parent = {
  value: 2,
  method() {
    return this.value + 1;
  },
};

// In this case `this` keyword refers to the 'parent' object.
console.log(parent.method()); // 3

const child = {
  __proto__: parent, // We are setting the object's prototype.
};

// In this case `this` keyword refers to the `child` object.
// `child` object neither has property `method` and `value`
// These are resolved using the child's prototype which is 
// the `parent` object.
console.log(child.method()); // 3

// Now the `child` has own property `value`
child.value = 10;

// this.value = 10
console.log(child.method()); // 11

// Also we can override the `method`
child.method = function () {
  return this.value + 10;
}

console.log(child.method()); // 20

console.log(child.__proto__.method()); // 3
// Because now the `this` keyword refers to `child.__proto__`, the
// `parent` object.

console.log(child.__proto__.method.apply(child)); // 11
// with the `apply` function we are setting
// the `this` property to `child`. But the `method` is coming
// from the `parent` object.

console.log(child.method.apply(parent)); // 12
// the child.method is coming from the `child` which is overridden
// at line 28, and we are setting the `this` property to 
// the `parent` object so the `value` property is equal to 2.
// so the result is 12.

Псевдоклассическая реализация — «новое» ключевое слово

С ключевым словом new мы можем опустить вызов функции Object.create, потому что ключевое слово new делает это автоматически.

function Todo(title, description, dateCreated) {
  // Below statement automatically included 
  // when we call new Todo(...)
  // const this = Object.create(Todo.prototype)

  this.name = name;
  this.energy = energy;

  // Below statement automatically included 
  // when we call new Todo(...)
  // return this ;
}

const todo = new Todo("myTodo-title", "myTodo-description", new Date());

Прототипы цепочек

У каждого объекта есть поле prototype, но подумайте об этом, поле prototype само по себе является объектом, поэтому также поле prototype имеет prototype свойство. Это продолжается как цепочка, пока один из прототипов не станет равным null. Значение null завершает цепочку прототипов.

const o = {
  a: 1,
  b: 2,
  __proto__: { 
    b: 3,
    c: 4,
  },
};

// o = {a: 1, b: 2}
// o.[[Prototype]] = {b:3, c:4}
// o.[[Prototype]].[[Prototype]] = Object.prototype
// o.[[Prototype]].[[Prototype]].[[Prototype]] = null | End of the chain

console.log(Object.prototype.__proto__); // null

Полный пример

function Animal(name){
 this.name = name;
}

Animal.prototype.eat = function(){
  console.log(`${this.name} is eating.`);
}

function Lion(name, age) {
  Animal.call(this, name); // We are calling the base constructor.
  this.age = age;
}
Lion.prototype.walk = function() {
  console.log(`${this.name} is walking`);
}
Lion.roar = function () {
  console.log("Static function is called.");
}

Object.setPrototypeOf(Lion.prototype, Animal.prototype); // Inheritance

const animal = new Animal("An Animal");

animal.eat(); // An Animal is eating.

const lion = new Lion("The Lion", 8); // The Lion is eating.

lion.eat();
lion.walk(); // The Lion is walking

// animal.walk(); Error animal.walk is not a function
// lion.roar(); Error lion.roar is not a function

Lion.roar(); // Static function is called.

Как написать этот пример, используя современный Javascript (ES6)?

class Animal {
  constructor(name){
    this.name = name;
  } 

  eat(){
   console.log(`${this.name} is eating.`);
  }
};

class Lion extends Animal {
  constructor(name, age){
    super(name);
    this.age = age;
  }
  
  walk(){
 console.log(`${this.name} is walking`);
  }

  static roar(){
   console.log("Static function is called.");
  }
};

const animal = new Animal("An Animal");

animal.eat(); // An Animal is eating.

const lion = new Lion("The Lion", 8); // The Lion is eating.

lion.eat();
lion.walk(); // The Lion is walking

// animal.walk(); Error animal.walk is not a function
// lion.roar(); Error lion.roar is not a function

Lion.roar(); // Static function is called.

Спасибо за прочтение…

Смотрите также