Что такое прототип? - PullRequest
       35

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

1 голос
/ 16 октября 2019

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

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

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

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

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

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

Person.prototype.age = 0;  

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

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

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

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

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

Ответы [ 4 ]

2 голосов
/ 16 октября 2019

Когда вы делаете 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;

enter image description here

0 голосов
/ 16 октября 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.

0 голосов
/ 16 октября 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__

note: вы также можете заменить Person на встроенный Array, Object или String.

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

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

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

bruce.__proto__.isSuperHero = true;

То же самое происходит при добавлении свойств в прототип

, но это будет отражаться повсюду;

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

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

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

0 голосов
/ 16 октября 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__ и используем его, если он существует.

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

...