Что такое прототип?

Я пытаюсь разобраться в прототипах и хотел бы знать, что такое прототип. Большая часть путаницы связана с непониманием метаязыка, используемого для описания прототипов.

Вот что я знаю:

Когда мы создаем именованную функцию-конструктор со свойствами внутри нее, свойства внутри тела этой функции-конструктора наследуются экземплярами объекта, созданными этой функцией-конструктором. Здесь я создал экземпляр с именем person001 из функции-конструктора с именем Person.

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}
undefined
var person001 = new Person("John","Doe");

Когда я смотрю на экземпляр объекта в консоли и отслеживаю цепочку прототипов, я нахожу его в двух разных местах. Это объект-конструктор прото-объекта dunder...

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
__proto__: Object

и свойство объекта-прототипа внутри того же объекта-конструктора.

Person {firstName: "John", lastName: "Doe"}
firstName: "John"
lastName: "Doe"
__proto__:
constructor: ƒ Person(firstName,lastName)
arguments: null
caller: null
length: 2
name: "Person"
prototype: 
constructor: ƒ Person(firstName,lastName)
__proto__: Object
__proto__: ƒ ()
[[FunctionLocation]]: script.js:76
[[Scopes]]: Scopes[1]
__proto__: Object

Когда я добавляю свойство, используя свойство .prototype именованной функции-конструктора, я добавляю это свойство к объекту-прототипу, а НЕ к функции-конструктору. Добавленное свойство будет находиться рядом с функцией-конструктором в объекте свойства-прототипа. Здесь я добавляю свойство с именем age, используя свойство прототипа функции-конструктора Person.

Person.prototype.age = 0;  

Итак, теперь, когда я добавил дополнительное свойство, что же такое прототип?

Когда я запускаю метод Object.getPrototypeOf для экземпляра объекта person001, он возвращает то, что мне кажется прототипом объекта. У него есть 3 свойства — функция-конструктор, свойство, которое я добавил, и неявный прото-объект dunder.

Object.getPrototypeOf(person001);
{age: 0, constructor: ƒ}
age: 0
constructor: ƒ Person(firstName,lastName)
__proto__: Object 

Так что же такое прототип? Это объект-прототип {функция-конструктор, дополнительные свойства}? Или это просто функция-конструктор объекта-прототипа?

Заранее спасибо за вашу помощь.


person efw    schedule 16.10.2019    source источник
comment
stackoverflow.com/questions/572897/   -  person Teemu    schedule 16.10.2019


Ответы (4)


Когда вы выполняете obj = new Person, в игре участвуют три игрока:

  • только что созданный объект obj
  • функция-конструктор Person
  • прототип, специальный скрытый объект, хранящийся под Person.prototype

Отношения между ними следующие:

obj.__proto__ === Person.prototype

Person.prototype.constructor === Person

Иллюстрация:

function Person(firstName,lastName) {
    this.firstName = firstName;
    this.lastName = lastName
}

var person1 = new Person("John","Doe");
var person2 = new Person("Ann","Smith");

Person.prototype.age = 42;

введите здесь описание изображения

person georg    schedule 16.10.2019
comment
Это блестящая иллюстрация. Итак, ссылаясь на иллюстрацию, теперь я понимаю, что прототипом «The» в данном случае является объект Person и все свойства, которые ему принадлежат, включая объект функции-конструктора. Когда вы добавляете свойство к объекту Person с помощью Person.prototype.age, это свойство сохраняется как объект в объекте-прототипе функции-конструктора и задается как свойство в объекте Person. - person efw; 16.10.2019
comment
Георг, не могли бы вы сказать мне, какое приложение вы использовали для создания графики? - person efw; 16.10.2019
comment
Ставлю на верх закладок. Абсолютный гений. Спасибо, Георг! - person efw; 16.10.2019

Сказал, что вы создали конструктор для Person, а затем два его экземпляра:

const Person = function(name) {
  this.name = name;
  this.speak = () => console.log('My name is ' + this.name)
};
const john = new Person('John');
const mary = new Person('Mary');

john.speak();
mary.speak();

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

Но Мария не только может говорить. Она умеет петь. Но Джон не может.

        const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        mary.sing = () => console.log('♪♪♪ Lalalalalala ♪♪♪');
        
        mary.sing();
        john.sing(); // John is such a bad singer, that this throws an error !

Что, если потом ты поймешь, что твоим людям нужно не только говорить, но и ходить. Вам нужна ссылка на их общего родителя, чтобы сказать им идти все в одну линию. Это прототип. И Мэри, и Джон имеют общий прототип и глубоко внутри имеют ссылку на этот прототип (это __proto__ для друзей и семьи).

const Person = function(name) {
      this.name = name;
      this.speak = () => console.log('My name is ' + this.name)
    };
    const john = new Person('John');
    const mary = new Person('Mary');

    john.speak();
    mary.speak();
    
    Person.prototype.walk = () => console.log('I am walking alright');
    
    john.walk();
    mary.walk();
    
    // That is the same as:
    john.__proto__.walk()
    mary.__proto__.walk()

Теперь у Джона неудачное падение, и ему трудно ходить.

    const Person = function(name) {
          this.name = name;
          this.speak = () => console.log('My name is ' + this.name)
        };
        const john = new Person('John');
        const mary = new Person('Mary');

        john.speak();
        mary.speak();
        
        Person.prototype.walk = () => console.log('I am walking alright');
        
        // John's infamous accident
        john.walk = () => console.log('My leg hurts so bad...');
        
        john.walk();
        mary.walk();
        
        

Экземпляр имеет собственное свойство, мы его используем.
Его нет, мы смотрим в его __proto__ и используем, если оно существует.

Надеюсь это поможет!

person giuseppedeponte    schedule 16.10.2019
comment
Спасибо, Джузеппе, это помогает. - person efw; 16.10.2019

РЕДАКТИРОВАТЬ: Мой друг @teemu добавил к моему ответу: у каждого встроенного объекта есть свой прототип.

На очень простом языке каждая функция представляет собой объект особого типа в javascript, и каждая функция имеет свой собственный контейнер объекта «прототип». Таким образом, каждая функция-конструктор будет иметь свой собственный объект-прототип, который будет использоваться совместно со всеми объектами в __proto__, созданными с использованием этого. Посмотрим на примере:

// constructor function:
    var Person = function(name, age){
      this.name = name;
      this.age = age
    }

var tony = new Person('tony', 21);
var bruce = new Person('bruce', 22);

так как мы обсуждали,

Person.prototype должно совпадать с tony.__proto__ и bruce.__proto__

примечание: вы также можете заменить Person на встроенный массив, объект или строку.

чтобы убедиться в этом, мы можем сделать это:

Person.prototype == tony.__proto__;  //true
Person.prototype == bruce.__proto__; //true
bruce.__proto__ == tony.__proto__;  //true

Следующее, что мы собираемся сделать, добавим одно свойство в bruce.__proto__:

bruce.__proto__.isSuperHero = true;

то же самое происходит, когда вы добавляете свойства в prototype.

но это отразится повсюду;

console.log(tony.__proto__.isSuperHero )  // true
console.log(Person.prototype.isSuperHero)   //true

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

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

person Sheelpriy    schedule 16.10.2019
comment
Не только функции, каждый объект в JS имеет прототип (кроме специально созданных без него). - person Teemu; 16.10.2019

Во-первых, прототип — это просто объект. Почти каждый объект в JS (за исключением, например, случая, когда вы используете Object.create(null)) имеет некоторый прототип, и прототипы могут быть объединены в цепочку.

Пример: когда вы создаете массив с помощью литерала [], ваш экземпляр массива подключается к объекту (также экземпляр массива между прочим), определяя свойства массива, такие как map и т. д., который сам подключается к другому прототипу (экземпляру объекта), определяя свойства таких объектов, как toString. Эти свойства обычно являются функциями. Теперь, когда вы получаете доступ к одному из свойств вашего объекта, например [].hasOwnProperty, движок просматривает цепочку прототипов, чтобы найти его. Он начинается с вашего экземпляра [], не находит его, переходит к прототипу (массиву), также безуспешно, поэтому переходит к последнему прототипу (экземпляру объекта), где он, наконец, найден.

Как видите, цепочка прототипов всегда где-то заканчивается, поэтому, если вы попытаетесь получить прототип на последнем прототипе в цепочке, вы получите null.

Теперь вернемся к функциям-конструкторам. Первое, что нужно отметить, это то, что это обычные функции — фактическим «создателем» экземпляра является ключевое слово new. Теперь у каждой функции есть свойство prototype, которое говорит вам следующее: каждый объект, созданный с помощью этой функции, будет связан прототипом со всем, что находится в свойстве prototype функции. По умолчанию каждая функция содержит экземпляр Object в этом свойстве, а это означает, что каждый объект, который вы создаете из этой функции, будет связан с прототипом этого экземпляра и, следовательно, «наследует» такие свойства, как toString.

В этом примере вы можете увидеть связь между свойством prototype функции и прототипом экземпляра.

function A() {}
var a = new A();
a.__proto__ == A.prototype; // is true

Наконец, свойство constructor в свойстве prototype функции сообщает вам, какая функция будет использоваться для создания новых экземпляров при использовании функции с ключевым словом new. Этот глоток сводится к:

function A() {}
A == A.prototype.constructor; // is true

Это ссылка на себя. Это свойство было необязательным для установки при создании собственной цепочки наследования до ES6, но, тем не менее, было сделано для корректности.

Так что же такое прототип? Это просто объект, связанный с другими экземплярами объекта через специальное соединение-прототип, что дает ему доступ к предопределенным свойствам. Это способ наследования в JS.

person Dan Macák    schedule 16.10.2019
comment
Нет, это не так, учтите, что Object.hasOwnProperty([], hasOwnProperty) возвращает false, но [].hasOwnProperty возвращает вам функцию. - person Dan Macák; 16.10.2019
comment
Ах, да, это так, но ваш пример не вызывал .hasOwnProperty. Удаление комментария. - person Teemu; 16.10.2019
comment
Мы говорили о яблоках и апельсинах. Теперь я вижу, что вы комментировали вызов fn — да, он явно проверяет только экземпляр. Но что касается доступа к свойствам, он идет вверх по цепочке прототипов. - person Dan Macák; 16.10.2019
comment
Спасибо, Дэн. Это очень полезно. - person efw; 16.10.2019