javascript - наследование - конструктор js




Как работает JavaScript.prototype? (16)

Какова цель этого свойства «.prototype»?

Интерфейс со стандартными классами становится расширяемым. Например, вы используете Arrayкласс, и вам также нужно добавить собственный сериализатор для всех объектов массива. Вы потратили бы время на кодирование подкласса или использование композиции или ... Свойство прототипа решает это, позволяя пользователям контролировать точный набор членов / методов, доступных для класса.

Подумайте о прототипах в качестве дополнительного vtable-указателя. Когда некоторые члены отсутствуют в исходном классе, прототип просматривается во время выполнения.

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

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Я помню много дискуссий, которые у меня были с людьми некоторое время назад (я не совсем уверен, что я делаю), но, как я понимаю, нет понятия класса. Это всего лишь объект, и экземпляры этих объектов являются клонами оригинала, верно?

Но какова цель этого свойства «.prototype» в JavaScript? Как это соотносится с экземплярами объектов?

Обновление: правильный путь

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Также эти slides действительно помогли.


Резюме:

  • Функции являются объектами в javascript и, следовательно, могут иметь свойства
  • (Constructor) всегда имеют свойство прототипа
  • Когда функция используется как конструктор с newключевым словом, объект получает __proto__свойство
  • Это __proto__свойство относится к prototypeсвойству функции-конструктора.

Пример:

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

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Почему это полезно?

Javascript имеет механизм при поиске свойств объектов, который называется «прототипным наследованием» , вот что в основном делает:

  • Сначала проверяется, находится ли свойство в самом объекте. Если это свойство возвращается.
  • Если свойство не расположено на самом объекте, оно «взойдет на проточаин». Он в основном смотрит на объект, на который ссылается свойство proto . Там он проверяет, доступно ли свойство на объекте, на который ссылается прото
  • Если свойство не расположено на прото- объекте, оно поднимется до прото- цепочки вплоть до объекта Object.
  • Если он не может найти свойство нигде в объекте и его цепочке прототипов, он вернется не определен.

Например:

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

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);


Javascript не имеет наследования в обычном смысле, но имеет цепочку прототипов.

цепочка прототипов

Если элемент объекта не может быть найден в объекте, он ищет его в цепочке прототипов. Цепочка состоит из других объектов. Прототип данного экземпляра можно получить с __proto__ переменной __proto__ . Каждый объект имеет один, поскольку нет разницы между классами и экземплярами в javascript.

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

Это также полезно для наследования, поскольку цепочка прототипов может состоять из многих других объектов.


Каждый объект JavaScript имеет внутреннее свойство, называемое [[Prototype]] . Если вы просматриваете свойство через obj.propName или obj['propName'] и у объекта нет такого свойства, которое может быть проверено с помощью obj.hasOwnProperty('propName') - среда выполнения просматривает свойство объекта на который ссылается [[Prototype]]. Если у прототипа-объекта также нет такого свойства, его прототип проверяется поочередно, таким образом ходя прототип-цепочка исходного объекта до тех пор, пока не будет найдено совпадение или его конец не будет достигнут.

Некоторые реализации JavaScript допускают прямой доступ к свойству [[Prototype]], например, через нестандартное свойство с именем __proto__ . В общем, только возможно установить прототип объекта при создании объекта: если вы создаете новый объект через new Func() , свойство [[Prototype]] объекта будет установлено в объект, на который ссылается Func.prototype .

Это позволяет моделировать классы в JavaScript, хотя система наследования JavaScript - как мы видели - прототипическая, а не на основе классов:

Просто подумайте о функциях конструктора как о классах и свойствах прототипа (т. Е. Объекта, на который ссылается свойство prototype функции конструктора) как общих членов, то есть членов, которые одинаковы для каждого экземпляра. В системах на основе классов методы реализуются одинаково для каждого экземпляра, поэтому методы обычно добавляются к прототипу, тогда как поля объекта специфичны для экземпляра и поэтому добавляются к самому объекту во время построения.


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

В JavaScript вы сначала создаете объект (нет понятия класса), затем вы можете увеличить свой собственный объект или создать из него новые объекты. Это не сложно, но немного чужой и трудно усваивается для кого-то, привыкшего к классическому.

Пример:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

До сих пор я расширял базовый объект, теперь создаю еще один объект, а затем наследую от Person.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

var Person = function (name) {
    this.name = name;
};
Person.prototype.getName = function () {
    return this.name;
};
var john = new Person("John");
alert(john.getName());
Person.prototype.sayMyName = function () {
    alert('Hello, my name is ' + this.getName());
};
john.sayMyName();
var Customer = function (name) {
    this.name = name;
};
Customer.prototype = new Person();

var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();
Customer.prototype.setAmountDue = function (amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function () {
    return this.amountDue;
};
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Хотя, как сказано, я не могу назвать setAmountDue (), getAmountDue () для Лица.

//The following statement generates an error.
john.setAmountDue(1000);

После прочтения этой темы, я чувствую себя смущенной с прототипом JavaScript, тогда я нашел эти диаграммы

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance

это четкая диаграмма, показывающая наследование JavaScript по прототипной цепочке

а также

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

этот пример содержит пример с кодом и несколькими хорошими диаграммами.

цепочка прототипов в конечном счете возвращается к Object.prototype.

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

Надеюсь, вам также будет полезно понять прототип кода JavaScript.


Окончательное руководство по объектно-ориентированному JavaScript - очень краткое и понятное объяснение 30-минутного вопроса по заданному вопросу (тема Prototypal Inheritance начинается с 5:45 , хотя я предпочел бы прослушать все видео). Автор этого видео также сделал веб-сайт визуализатора JavaScript http://www.objectplayground.com/ .


prototype позволяет создавать классы. если вы не используете prototype он становится статичным.

Вот краткий пример.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

В приведенном выше случае у вас есть статический тест вызова функции. К этой функции можно обращаться только obj.test, где вы можете представить, что obj является классом.

где, как и в приведенном ниже кодексе

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Объект стал классом, который теперь может быть создан. Несколько экземпляров obj могут существовать, и все они имеют функцию test .

Выше было мое понимание. Я делаю это wiki сообщества, поэтому люди могут исправить меня, если я ошибаюсь.


Здесь есть два разных, но связанных с ними объекта, которые нуждаются в объяснении:

  • .prototypeСвойство функций.
  • Свойство [[Prototype]] [1] всех объектов [2] .

Это две разные вещи.

[[Prototype]]Свойство:

Это свойство, которое существует на всех [2] объектах.

Здесь хранится другой объект, который, как сам объект, имеет [[Prototype]]свой собственный, который указывает на другой объект. У этого другого объекта есть [[Prototype]]свои. Эта история продолжается до тех пор, пока вы не достигнете прототипа объекта, который предоставляет методы, доступные для всех объектов (например .toString).

[[Prototype]]Свойство является частью того , что образует [[Prototype]]цепь. Эта цепочка [[Prototype]]объектов - это то, что проверяется, когда, например, [[Get]]или [[Set]]операции выполняются над объектом:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

.prototypeСвойство:

Это свойство, которое доступно только для функций. Используя очень простую функцию:

function Bar(){};

.prototypeСвойство содержит объект , который будет назначен , b.[[Prototype]]когда вы делаете var b = new Bar. Вы можете легко изучить это:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

Одним из наиболее важных .prototypeс является то , что Object.prototype . Этот прототип содержит прототипный объект, который [[Prototype]]содержит все цепи. На нем определены все доступные методы для новых объектов:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Теперь, поскольку .prototypeэто объект, он обладает [[Prototype]]свойством. Когда вы не выполняете никаких присвоений Function.prototype, точки .prototypes [[Prototype]]указывают на прототип объекта ( Object.prototype). Это автоматически выполняется в любое время при создании новой функции.

Таким образом, каждый раз, когда вы создаете new Bar;цепочку прототипов, вы получаете все, что определено, Bar.prototypeи все, что определено на Object.prototype:

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

Когда вы выполняете задания для Function.prototypeвсего, что вы делаете, это расширение цепочки прототипов для включения другого объекта. Это похоже на вставку в отдельный список.

Это в основном изменяет [[Prototype]]цепочку, позволяющую свойствам, определенным для объекта, назначенного для Function.prototypeпросмотра любым объектом, созданным функцией.

[1: Это никого не смутит; доступны через в __proto__собственности во многих реализациях.
[2]: Все, кроме null.


Просто, что у вас уже есть объект с Object.new, но вы все еще не имеете объекта при использовании синтаксиса конструктора.



Когда конструктор создает объект, этот объект неявно ссылается на свойство «prototype» конструктора для разрешения ссылок на свойства. Свойство «prototype» конструктора может ссылаться на конструктор конструктора program.prototype, а свойства, добавленные в прототип объекта, совместно используются через наследование всеми объектами, совместно использующими прототип.


Мне всегда нравятся аналоги, когда дело доходит до понимания этого типа вещей. «Прототипическое наследование» довольно запутанно по сравнению с наследованием класса баса, на мой взгляд, хотя прототипы - гораздо более простая парадигма. На самом деле, с прототипами, на самом деле нет наследования, поэтому это само по себе вводит в заблуждение, это скорее тип делегирования.

Представьте себе это ....

Вы в старшей школе, и вы в классе, и у вас есть викторина, которая должна состояться сегодня, но у вас нет ручки, чтобы заполнить ваши ответы. Doh!

Вы сидите рядом со своим другом Финнисом, у которого может быть ручка. Вы спрашиваете, и он безуспешно оглядывается по столу, но вместо того, чтобы сказать: «У меня нет ручки», он хороший друг, которого он проверяет со своим другом другом Дерпом, если у него есть ручка. У Дерпа действительно есть запасное перо и передает его Финниусу, который передает его вам, чтобы завершить викторину. Дерп доверил ручку Финниусу, который делегировал вам перо для использования.

Здесь важно то, что Дерп не дает вам ручку, поскольку у вас нет прямых отношений с ним.

Это упрощенный пример того, как работают прототипы, где дерево данных ищет предмет, который вы ищете.


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

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

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Каждый объект содержит внутреннее свойство, называемое [[прототипом]], к которому может обращаться Object.getPrototypeOf()функция. Object.create(model)создает новый объект и устанавливает его свойство [[prototype]] для объектной модели . Следовательно, когда вы это сделаете Object.getPrototypeOf(product), вы получите объектную модель .

Свойства продукта обрабатываются следующим образом:

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

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


Я счел полезным объяснить «цепочку прототипов» как рекурсивную конвенцию, на obj_n.prop_Xкоторую ссылаются:

если obj_n.prop_Xне существует, проверьте, obj_n+1.prop_Xгдеobj_n+1 = obj_n.[[prototype]]

Если prop_Xнаконец-то найдено в k-м объекте прототипа, тогда

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Вы можете найти график зависимости объектов Javascript от их свойств здесь:

http://jsobjects.org


другая схема, показывающая отношения __proto__ , prototype и constructor :





prototype-oriented