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

2007

Я не так увлекаюсь динамическими языками программирования, но я написал свою долю кода 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

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

  • 74
    У Джона Резига есть несколько слайдов о прототипах функций, которые были мне полезны при изучении темы (вы также можете внести изменения в код и посмотреть, что произойдет ...) http://ejohn.org/apps/learn/#64
  • 4
    Отличный справочный материал, для сохранения информативности этого вопроса, возможно, разместите некоторые комментарии с сайта Джона на ваш ответ на случай, если его сайт изменится так, что ваша ссылка больше не будет доступна. В любом случае +1, помог мне.
Показать ещё 9 комментариев
Теги:
oop
prototype-programming
prototypal-inheritance
dynamic-languages

25 ответов

906
Лучший ответ

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

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

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

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

  • 1
    Итак, я делаю что-то не так, определяя новые свойства для свойства prototype в моем коротком фрагменте?
  • 3
    Я думаю, что это означает, что функциональные объекты должны быть первоклассными гражданами.
Показать ещё 9 комментариев
1771

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

В 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 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 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 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() для Person.

//The following statement generates an error.
john.setAmountDue(1000);
  • 1
    Хотя я оцениваю подробный ответ, у этого вопроса уже есть несколько хороших ответов и один принятый ответ, поэтому я несколько удивлен, увидев, что такого рода вещи появляются сейчас. У меня есть вопрос, хотя для вас. Customer.prototype = new Person(); new оператор действительно требуется здесь? или вы бы намеренно иногда оставляли это? Я думаю, что он все еще будет работать без new оператора, но изменения в прототипе клиента теперь будут влиять на функциональный объект Person.
  • 347
    Я думаю, что ответы на stackoverflow интересны не только оригинальному постеру, но также и большому сообществу других людей, скрывающихся или прибывающих из поисков. И я был одним из них, и я получил выгоду от старых постов. Я думаю, что мог бы внести свой вклад в другие ответы, добавив несколько примеров кода. По поводу вашего вопроса: если вы пропустите новое, это не сработает. когда я вызываю myCustomer.sayMyName (), возвращается «myCustomer.sayMyName не является функцией». Самый простой способ - поэкспериментировать с firebug и посмотреть, что получится.
Показать ещё 13 комментариев
160

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


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

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Есть несколько важных моментов, которые мы должны рассмотреть перед тем, как пройти концепцию прототипа.

1- Как работают функции JavaScript:

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

Предположим, мы хотим создать объектную модель Person. но на этом этапе я попытаюсь сделать сделать то же самое, не используя prototype и new ключевое слово.

Итак, на этом шаге functions, objects и this, все, что у нас есть.

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

Итак, чтобы ответить, пусть говорят, что у нас пустой объект, и две функции:

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

function getName(){
    console.log(this.name);
}

и теперь без использования new ключевого слова, как мы могли бы использовать эти функции. Итак, у JavaScript есть 3 разных способа сделать это:

а. Первый способ - просто вызвать функцию как регулярную функцию:

Person("George");
getName();//would print the "George" in the console

в этом случае это будет текущий объект контекста, который обычно является глобальным объектом window в браузере или GLOBAL в Node.js. Это означает, что у нас было бы window.name в браузере или GLOBAL.name в Node.js, с его значением "George".

б. Мы можем прикрепить к объекту, так как его свойства

- Самый простой способ сделать это - изменить пустой объект Person, например:

person.Person = Person;
person.getName = getName;

таким образом мы можем назвать их такими:

person.Person("George");
person.getName();// -->"George"

и теперь объект Person выглядит следующим образом:

Object {Person: function, getName: function, name: "George"}

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

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Но таким образом, что мы фактически делаем, это изменение Object.prototype, потому что всякий раз, когда мы создаем объект JavaScript с использованием литералов ({ ... }), он создается на основе Object.prototype, который означает, что он привязан к вновь созданному объекту как атрибут с именем __proto__, поэтому, если мы его изменим, как это было сделано в нашем предыдущем фрагменте кода, все объекты JavaScript будут изменены, а не хорошая практика. Итак, что может быть лучшей практикой сейчас:

person.__proto__ = {
    Person: Person,
    getName: getName
};

и теперь другие объекты находятся в тишине, но это, по-видимому, не является хорошей практикой. Таким образом, у нас есть еще одно решение, но для использования этого решения мы должны вернуться к той строке кода, где создан объект Person (var person = {};), а затем изменить его так:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

то, что он делает, это создать новый JavaScript Object и прикрепить propertiesObject к атрибуту __proto__. Поэтому, чтобы убедиться, что вы можете сделать:

console.log(person.__proto__===propertiesObject); //true

Но сложной точкой является то, что у вас есть доступ ко всем свойствам, определенным в __proto__ на первом уровне объекта Person (более подробную информацию см. в итоговой части).


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

с. JavaScript имеет другой способ предоставить функцию this, которая использует call или apply для вызова функции.

Метод apply() вызывает функцию с заданным значением и аргументы, предоставленные в виде массива (или подобный массиву объект).

и

Метод call() вызывает функцию с заданным значением и аргументы, предоставленные индивидуально.

таким образом, который является моим любимым, мы можем легко вызвать наши функции, например:

Person.call(person, "George");

или

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

эти 3 метода являются важными начальными шагами для определения функциональности .prototype.


2- Как работает ключевое слово new?

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

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

в этой части я собираюсь выполнить все шаги, которые выполняет JavaScript, без использования ключевого слова new и prototype, когда вы используете ключевое слово new. поэтому, когда мы выполняем new Person("George"), функция Person выступает в качестве конструктора. Это то, что делает JavaScript, один за другим:

а. в первую очередь он создает пустой объект, в основном пустой хэш вроде:

var newObject = {};

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

мы имеем my_person_prototype здесь, аналогично объекту прототипа.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

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


а. и b. Вместо этих двух шагов вы можете получить тот же результат:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

теперь мы можем вызвать функцию getName в нашем my_person_prototype:

newObject.getName();

с. то он дает этот объект конструктору,

мы можем сделать это с нашим примером, например:

Person.call(newObject, "George");

или

Person.apply(newObject, ["George"]);

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

теперь конечный результат перед имитацией других шагов:   Object {name: "George" }


Резюме:

В основном, когда вы используете ключевое слово новое для функции, вы вызываете это, и эта функция служит в качестве конструктора, поэтому, когда вы говорите:

new FunctionName()

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

Итак, когда JavaScript переходит на поиск свойства на объекте, первое, что он делает, это он смотрит на этот объект. И тогда существует секретное свойство [[prototype]], которое мы обычно имеем как __proto__, и это свойство выглядит следующим образом. И когда он просматривает __proto__, поскольку он снова является другим объектом JavaScript, он имеет свой собственный атрибут __proto__, он поднимается вверх и вверх доходит до точки, где следующий __proto__ имеет значение NULL. Точка является единственным объектом в JavaScript, который имеет атрибут __proto__: null - это Object.prototype object:

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

и что как наследование работает в JavaScript.

Изображение 2069

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

  • 6
    a) Пожалуйста, не объясняйте прототипы, копируя свойства. b) Установка внутреннего [[prototype]] происходит до применения функции-конструктора к экземпляру. Пожалуйста, измените этот порядок. c) jQuery в этом вопросе совершенно оффтопичен.
  • 1
    @ Берги: спасибо за указание, я буду признателен, если вы дадите мне знать, если это нормально сейчас.
Показать ещё 7 комментариев
75

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 сообщества, поэтому люди могут исправить меня, если я ошибаюсь.

  • 13
    -1: prototype - это свойство функций конструктора, а не экземпляров, т.е. ваш код неверен! Возможно, вы имели в виду нестандартное свойство __proto__ объектов, но это совсем другой зверь ...
  • 0
    @Christoph - Спасибо за указание на это. Я обновил пример кода.
Показать ещё 5 комментариев
63

Семь Коанов-прототипов

Когда Чиро Сан спустился с Горы Огненной Лисы после глубокой медитации, его разум был чист и спокоен.

Однако его рука была беспокойной, и он сам схватил кисть и записал следующие заметки.


0) Две разные вещи можно назвать "прототипом":

  • свойство prototype, как в obj.prototype

  • внутреннее свойство прототипа, обозначаемое как [[Prototype]] в ES5.

    Его можно получить через ES5 Object.getPrototypeOf().

    Firefox делает его доступным через свойство __proto__ как расширение. ES6 теперь упоминает некоторые необязательные требования для __proto__.


1) Эти понятия существуют, чтобы ответить на вопрос:

Когда я делаю obj.property, где JS ищет .property?

Интуитивно понятно, что классическое наследование должно влиять на поиск свойств.


2)

  • __proto__ используется для точки . поиск свойства как в obj.property.
  • .prototype не используется для поиска напрямую, только косвенно, так как он определяет __proto__ при создании объекта с new.

Порядок поиска:

  • Свойства obj добавлены с помощью obj.p =... или Object.defineProperty(obj,...)
  • свойства объекта obj.__proto__
  • свойства объекта obj.__proto__.__proto__ и т.д.
  • если какое-то __proto__ равно null, вернуть undefined.

Это так называемая цепь прототипов.

Вы можете избежать . поиск с помощью obj.hasOwnProperty('key') и Object.getOwnPropertyNames(f)


3) Есть два основных способа установить obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()
    

    тогда new установил:

    f.__proto__ === F.prototype
    

    Это где .prototype используется.

  • Object.create:

     f = Object.create(proto)
    

    наборы:

    f.__proto__ === proto
    

4) код:

var F = function() {}
var f = new F()

Соответствует следующей диаграмме:

(Function)       (  F  )                                      (f)
 |  ^             | | ^                                        |
 |  |             | | |                                        |
 |  |             | | +-------------------------+              |
 |  |constructor  | |                           |              |
 |  |             | +--------------+            |              |
 |  |             |                |            |              |
 |  |             |                |            |              |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |
 |  |             |                |            |              |
 |  |             |                | +----------+              |
 |  |             |                | |                         |
 |  |             |                | | +-----------------------+
 |  |             |                | | |
 v  |             v                v | v
(Function.prototype)              (F.prototype)
 |                                 |
 |                                 |
 |[[Prototype]]                    |[[Prototype]]
 |                                 |
 |                                 |
 | +-------------------------------+
 | |
 v v
(Object.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

На этой диаграмме показано множество предопределенных языковых узлов объектов: null, Object, Object.prototype, Function и Function.prototype. Наши 2 строки кода создали только f, F и F.prototype.


5) .constructor обычно приходит из F.prototype через . уважать:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Когда мы пишем f.constructor, JavaScript делает . искать как:

  • f не имеет .constructor
  • f.__proto__ === F.prototype имеет .constructor === F, поэтому возьмите его

Результат f.constructor == F интуитивно корректен, поскольку F используется для конструирования f, например, для заданных полей, во многом как в классических языках ООП.


6) Классический синтаксис наследования может быть достигнут путем манипулирования цепочками прототипов.

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

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
(new C(1)).inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what 'd.inc' actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because 'D.__proto__ === C'.
D.c === 1
// Nothing makes this work.
d.c === undefined

Упрощенная схема без всех предопределенных объектов:

      __proto__
(C)<---------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |__proto__
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|__proto__        (D.prototype)
| |                |
| |                |
| |                |__proto__
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)--->(inc)
|
v
Function.prototype
  • 1
    @tomasb спасибо! «Я не знаю, где вы это взяли»: после того, как я увидел несколько из этих динамических языков, я заметил, что самое главное в их системе классов - как . поиск работает (и сколько копий данных сделано). Поэтому я решил понять этот момент. Остальное - Google + посты в блоге + переводчик Js под рукой. :)
  • 1
    Я до сих пор не понимаю, почему g.constructor === Object, потому что вы сказали, что "4) Когда вы делаете f = new F, new также устанавливает f.constructor = F". Не могли бы вы объяснить мне больше? В любом случае, это лучший ответ, который я ищу. Спасибо вам большое!
Показать ещё 7 комментариев
63

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

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance Изображение 2070

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

и

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

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

Цепочка прототипа

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

Цепочка прототипа

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

Надеемся, что это также поможет вам понять цепочку прототипов JavaScript.

  • 285
    это четкий график? :)
  • 2
    Диаграмма говорит тысячу слов. Это так полезно :)
Показать ещё 5 комментариев
37

Каждый объект имеет внутреннее свойство, [[Прототип]], связывая его с другим объектом:

object [[Prototype]] -> anotherObject

В традиционном javascript связанный объект является свойством prototype функции:

object [[Prototype]] -> aFunction.prototype

Некоторые среды выставляют [[Prototype]] как __proto__:

anObject.__proto__ === anotherObject

Вы создаете ссылку [[Prototype]] при создании объекта.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Итак, эти утверждения эквивалентны:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;
Оператор

A new не показывает цель ссылки (Object.prototype); вместо этого цель подразумевается конструктором (Object).

Помните:

  • Каждый объект имеет ссылку, [[Прототип]], иногда отображается как __proto__.
  • Каждая функция имеет свойство prototype.
  • Объекты, созданные с помощью new, связаны с свойством prototype своего конструктора.
  • Если функция никогда не используется как конструктор, ее свойство prototype не будет использоваться.
  • Если вам не нужен конструктор, используйте Object.create вместо new.
  • 1
    +1 за выделение Object.create ()
  • 1
    Редакция 5 удалила некоторую полезную информацию, включая информацию о Object.create (). Смотрите редакцию 4 .
Показать ещё 3 комментария
24

Эта статья длинна. Но я уверен, что это очистит большинство ваших запросов в отношении "прототипического" характера наследования JavaScript. И даже больше. Пожалуйста, прочитайте полную статью.

JavaScript в основном имеет два типа типов данных

  • Не объекты
  • Объекты

Не объекты

Ниже приведены типы Non object

  • строка
  • число (включая NaN и бесконечность)
  • логические значения (true, false)
  • undefined

Эти типы данных возвращаются после использования оператора typeof

typeof "строковый литерал" (или переменная, содержащая строковый литерал) === 'строка'

typeof 5 (или любой числовой литерал или переменная, содержащая числовой литерал или NaN или Infynity) === 'number'

typeof true (или false или переменная, содержащая true или false) === 'boolean'

typeof undefined (или переменная undefined или переменная, содержащая undefined) === 'undefined'

Типы данных , число и boolean могут быть представлены как Объекты и Non объекты. Когда они представлены как объекты, их typeof всегда === 'object'. Мы вернемся к этому, когда поняли типы данных объекта.

Объекты

Типы данных объекта можно разделить на два типа

  • Объекты типа функций
  • Объекты не функционального типа

Объекты типа - это те, которые возвращают строку 'function' с оператором typeof. Все пользовательские функции и все встроенные JavaScript объекты, которые могут создавать новые объекты с помощью нового оператора, попадают в эту категорию. Например,

  • Объект
  • Строка
  • Номер
  • Boolean
  • Массив
  • Типизированные массивы
  • RegExp
  • Функция
  • Все другие встроенные объекты, которые могут создавать новые объекты с помощью нового оператора
  • функция UserDefinedFunction() {/* пользовательский код */}

Итак, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (функция) === typeof (UserDefinedFunction) === 'function'

Все Объекты типа - это фактически экземпляры встроенного объекта JavaScript Функция (включая объект Функция, то есть он рекурсивно определен), Это как если бы эти объекты были определены следующим образом.

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

Как уже упоминалось, объекты Тип функции могут создавать новые объекты с помощью оператора new. Например, для объекта типа Объект, Строка, Число, Логическое, Массив, RegExp или UserDefinedFunction можно создать с помощью

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

Созданные объекты - это все Объекты типа Non Function и возвращают тип === 'объект. Во всех этих случаях объект "a" не может далее создавать объекты с использованием оператора new. Итак, неправильно:

var b=new a() //error. a is not typeof==='function'

Встроенный объект Math - typeof === 'object'. Следовательно, новый объект типа Math не может быть создан новым оператором.

var b=new Math() //error. Math is not typeof==='function'

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

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

Пользовательские функции являются особым случаем.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Так как объекты Объекты функций могут создавать новые объекты, их также называют Конструкторами.

Каждый Конструктор/Функция(независимо от того, была ли построена или определена пользователем), когда определено автоматически, имеет свойство "prototype", значение которого по умолчанию задано как объект. Этот объект имеет свойство "конструктор, который по умолчанию ссылается на Конструктор/Функция.

Например, когда мы определяем функцию

function UserDefinedFunction()
{
}

автоматически происходит

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Это свойство "prototype" присутствует только в объектах типа функций (и никогда не в объектах типа Non Function).

Это связано с тем, что при создании нового объекта (с использованием нового оператора) он наследует все свойства и методы из объекта прототипа функции конструктора, т.е. внутренняя ссылка создается во вновь созданном объекте, который ссылается на объект, на который ссылается объект прототипа функции Constructor.

Эта "внутренняя ссылка, созданная в объекте для ссылки на унаследованные свойства, известна как прототип объекта (который ссылается на объект, на который ссылается конструктор "prototype", но отличается от него). Для любого объекта (Function или Non Function) это можно получить с помощью метода Object.getPrototypeOf(). Используя этот метод, можно проследить цепочку прототипов объекта.

Кроме того, каждый созданный объект (Тип функции или Тип нефункции) имеет конструктор ", которое наследуется от объекта, на который ссылается свойство prototype функции Constructor. По умолчанию свойство " конструктор ссылается на конструктор, который его создал (если <конструктоp > по умолчанию "prototype" не изменен).

Для всех объектов типа функций функция конструктора всегда Функция Function() {}

Для объектов типа Non Function (например, Javascript Built in Math object) функция-конструктор - это функция, которая ее создала. Для объекта Math это функция Object() {}.

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

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

Цепочка прототипов каждого объекта в конечном счете возвращается к объекту Object.prototype(который сам по себе не имеет прототипа). Следующий код может использоваться для отслеживания цепочки прототипов объекта

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

Цепочка прототипов для различных объектов работает следующим образом.

  • Каждый объект Function (включая встроенный объект Function) → Function.prototype → Object.prototype → null
  • Простые объекты (созданные новым объектом() или {}, включая встроенный объект Math) → Object.prototype → null
  • Объект, созданный с помощью новой или Object.create → Цепочки прототипов One или More → Object.prototype → null

Для создания объекта без какого-либо прототипа используйте следующее:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

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

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Следуя в резюме этой статьи

  • Существует два типа объектов Типы функций и Нефункциональные типы
  • Только Объекты типа функций могут создавать новый объект с помощью оператора new. Созданные объекты представляют собой объекты Non Function type. Объекты Non Function type не могут создавать объект с помощью оператора new.

  • Все Объекты типа функций по умолчанию имеют свойство "prototype". Это свойство "prototype" ссылается на объект с конструктором "конструктор, который по умолчанию ссылается на объект Тип объекта.

  • Все объекты (Тип функции и Тип нефункции) имеют свойство "конструктор", которое по умолчанию ссылается на созданный им объект Объект функции/ Конструктор.

  • Каждый объект, который создается внутри, ссылается на объект, на который ссылается "prototype" свойства создателя, создавшего его. Этот объект известен как созданный прототип объекта (который отличается от свойства объектов типа "prototype", который он ссылается). Таким образом, созданный объект может напрямую обращаться к методам и свойствам, определенным в объекте, на который ссылается свойство Constructor "prototype" (во время создания объекта).

  • Прототип объекта (и, следовательно, его наследуемые имена свойств) можно получить с помощью метода Object.getPrototypeOf(). На самом деле этот метод может использоваться для навигации по всей цепочке прототипов объекта.

  • Цепочка прототипов каждого объекта в конечном счете возвращается к Object.prototype(если объект не создан с использованием Object.create(null), и в этом случае у объекта нет прототипа).

  • typeof (new Array()) === 'object' - это дизайн языка, а не ошибка, указанная Дуглас Крокфорд

  • Установка свойства prototype конструктора в null (или undefined, number, true, false, string) не должна создавать объект с нулевым прототипом. В таких случаях созданный прототип объекта устанавливается в Object.prototype, а его конструктор настроен на функцию Object.

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

24

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

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

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

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

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

  • 1
    FF и Chrome поддерживают Proto , но не IE и не Opera.
  • 0
    Георг, пожалуйста, уточни для нуба: «В javascript нет различий между классами и экземплярами». - не могли бы вы уточнить? Как это работает?
Показать ещё 1 комментарий
21

Концепция наследования prototypal является одним из самых сложных для многих разработчиков. Попробуем понять корень проблемы, чтобы лучше понять prototypal inheritance. Начнем с функции plain.

Изображение 2071

Если мы используем оператор new на Tree function, мы называем его как функцию constructor.

Изображение 2072

Каждая функция JavaScript имеет a prototype. Когда вы регистрируете Tree.prototype, вы получаете...

Изображение 2073

Если вы посмотрите на вышеприведенный вывод console.log(), вы можете увидеть свойство конструктора в свойствах Tree.prototype и __proto__. __proto__ представляет prototype, что этот function основан на выключении, и поскольку это всего лишь простой JavaScript function без inheritance, установленный еще, он ссылается на Object prototype, который только что встроен в к JavaScript...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

Это имеет такие вещи, как .toString, .toValue, .hasOwnProperty и т.д.

__proto__, который был принесен моей мозилле, устарел и заменен на метод Object.getPrototypeOf, чтобы получить Object prototype.

Изображение 2074

Object.getPrototypeOf(Tree.prototype); // Object {} 

Добавьте метод к нашему Tree prototype.

Изображение 2071

Мы модифицировали Root и добавили к нему ветвь function.

Изображение 2072

Это означает, что при создании instance Tree вы можете вызвать метод branch.

Изображение 2073

Мы также можем добавить primitives или objects в наш prototype.

Изображение 2074

Допустим a child-tree к нашему Tree.

Изображение 2075

Здесь Child наследует свой prototype от дерева, то, что мы здесь делаем, использует метод Object.create() для создания нового объекта, основанного на том, что вы проходите, здесь это Tree.prototype. В этом случае мы создаем прототип Child для нового объекта, который выглядит как прототип Tree. Затем мы устанавливаем Child constructor to Child, если мы не будем указывать на Tree().

Изображение 2076

Child теперь имеет свой собственный prototype, его __proto__ указывает на Tree и Tree prototype указывает на base Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Теперь вы создаете instance из Child и вызываете branch, который изначально доступен в Tree. Мы фактически не определили наш branch на Child prototype. НО, в Root prototype, из которого ребенок наследует.

Изображение 2077

В JS все не является объектом, все может действовать как объект.

JavaScript имеет такие примитивы, как strings, number, booleans, undefined, null. Они не object(i.e reference types), но, безусловно, могут действовать как Object. Рассмотрим здесь пример.

Изображение 2078

В первой строке этого списка для имени присваивается строковое значение primitive. Вторая строка относится к имени типа Object и вызывает charAt(0) с использованием точечной нотации.

Вот что происходит за кулисами: // что движок JavaScript делает

Изображение 2079

String object существует только для одного оператора перед его уничтожением (процесс называется autoboxing). Вернемся к нашему prototypal inheritance.

  • JavaScript поддерживает наследование через delegation на основе prototypes.
  • Каждый function имеет свойство prototype, которое ссылается на другое объект.
  • properties/functions выглядят из самого Object или через prototype цепочка, если она не существует

A prototype в JS - это объект, который yields вам принадлежит родительскому элементу другого Object. [то есть делегирование] delegation означает, что если вы не можете что-то сделать, вы скажете кому-то еще сделать это за вас.

Изображение 2080

https://jsfiddle.net/say0tzpL/1/

Если вы посмотрите вышеприведенную скрипту, у собаки есть доступ к методу toString, но она недоступна в ней, но доступна через цепочку прототипов, которая делегирует Object.prototype

Изображение 2081

Если вы посмотрите на приведенный ниже, мы пытаемся получить доступ к методу call, который доступен в каждом function.

Изображение 2082

https://jsfiddle.net/rknffckc/

Если вы посмотрите на приведенную выше скрипту, Profile Функция имеет доступ к методу call, но ее недоступна в ней, но доступна через цепочку прототипов, которая делегирует Function.prototype

Изображение 2083

Примечание. prototype является свойством конструктора функций, тогда как __proto__ является свойством объектов, построенных из конструктора функций. Каждая функция имеет свойство prototype, значение которой является пустым Object. Когда мы создаем экземпляр функции, мы получаем внутреннее свойство [[Prototype]] или __proto__, чья ссылка является прототипом функции constructor.

Изображение 2084

Вышеприведенная диаграмма выглядит немного сложной, но выводит всю картину о том, как работает prototype chaining. Продвигайтесь медленно:

Есть два экземпляра b1 и b2, конструктор которого Bar, а parent - Foo и имеет два метода из цепочки прототипов identify и speak через Bar и Foo

Изображение 2085

https://jsfiddle.net/kbp7jr7n/

Если вы посмотрите на код выше, у нас есть конструктор Foo, у которого есть метод identify() и Bar, у которого есть метод speak. Мы создаем два экземпляра Bar b1 и b2, родительский тип которых Foo. Теперь, вызывая метод speak Bar, мы можем определить, кто вызывает разговор через цепочку prototype.

Изображение 2086

Bar теперь имеет все методы Foo, которые определены в его prototype. Давайте еще больше углубимся в понимание Object.prototype и Function.prototype и то, как они связаны. Если вы посмотрите на конструктор Foo, Bar и Object - Function constructor.

Изображение 2087

prototype Bar составляет Foo, prototype of Foo is Object, и если вы внимательно посмотрите, prototype of Foo связан с Object.prototype.

Изображение 2088

Прежде чем закрыть это, давайте просто оберните небольшим фрагментом кода здесь, чтобы суммировать все выше. Мы используем оператор instanceof здесь, чтобы проверить, имеет ли Object в своей цепочке prototype свойство prototype a constructor, которое ниже суммирует всю большую диаграмму.

Изображение 2089

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

21

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

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

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

19

Это может помочь классифицировать цепи прототипов в две категории.

Рассмотрим конструктор:

 function Person() {}

Значение Object.getPrototypeOf(Person) является функцией. На самом деле это Function.prototype. Поскольку Person был создан как функция, он разделяет тот же объект-объект прототипа, что и все функции. Это то же самое, что и Person.__proto__, но это свойство не должно использоваться. Во всяком случае, при Object.getPrototypeOf(Person) вы эффективно поднимаетесь по лестнице так называемой цепи прототипов.

Цепочка в направлении вверх выглядит следующим образом:

    PersonFunction.prototypeObject.prototype (конечная точка)

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

Возьмем, к примеру, этот объект:

var p = new Person();

p не имеет прямых отношений между прототипом и линией. Их отношения разные. Объект p имеет свою собственную прототипную цепочку. Используя Object.getPrototypeOf, вы найдете цепочку следующим образом:

    pPerson.prototypeObject.prototype (конечная точка)

В этой цепочке нет функционального объекта (хотя это может быть).

Итак, Person кажется связанным с двумя типами цепей, которые живут своей жизнью. Чтобы "прыгать" из одной цепи в другую, вы используете:

  • .prototype: перейти от цепочки конструктора к цепочке созданных объектов. Это свойство, таким образом, определяется только для объектов функции (поскольку new может использоваться только для функций).

  • .constructor: перейти от цепочки созданных объектов к цепочке конструкторов.

Здесь представлено визуальное представление двух задействованных цепочек прототипов, представленных в виде столбцов:

Изображение 2090

Подводя итог:

Свойство prototype не дает информации о цепочке прототипов субъекта, а о объектах, созданных субъектом.

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

Вы можете прыгать назад и вперед между двумя цепочками прототипов:

Person.prototype.constructor === Person

Эта симметрия может быть разбита путем явного назначения другого объекта свойству prototype (подробнее об этом позже).

Создать одну функцию, получить два объекта

Person.prototype - это объект, который был создан одновременно с созданием функции Person. Он имеет конструкцию Person, хотя этот конструктор еще не выполнялся. Таким образом, одновременно создаются два объекта:

  • Сама функция Person
  • Объект, который будет действовать как прототип, когда функция вызывается как конструктор

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

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

Вот некоторые равенства, которые могут помочь понять проблему - все эти print true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Добавление уровней в цепочку прототипов

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

Например:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Теперь цепочка прототипов t на один шаг длиннее, чем у p:

    tpPerson.prototypeObject.prototype (конечная точка)

Другая цепочка прототипов не больше: Thief и Person являются братьями и сестрами, разделяющими один и тот же родительский элемент в цепочке прототипов:

    Person}
    Thief } → Function.prototypeObject.prototype (конечная точка)

Предыдущая представленная графика затем может быть расширена до этого (исходный Thief.prototype не учитывается):

Изображение 2091

Синие линии представляют цепи прототипов, другие цветные линии представляют собой другие отношения:

  • между объектом и его конструктором
  • между конструктором и прототипом объекта, который будет использоваться для построения объектов
16

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

  • 1
    отличный видео справочник
14

Мне было полезно объяснить "цепочку прототипов" как рекурсивное соглашение, когда ссылается 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 от их свойств здесь:

Изображение 2094

http://jsobjects.org

13

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

10

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

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

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

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

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

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

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

  • 0
    Не всегда написано на товаре по заданию. Вы не очень ясно даете понять, что конкретные элементы экземпляра должны быть инициализированы, а общие участники могут использовать прототип. Особенно, когда у вас есть изменяемые члены, специфичные для экземпляра: stackoverflow.com/questions/16063394/…
  • 0
    HMR: В вашем примере в вашем ответе ben.food.push («Гамбургер»); line изменяет свойство объекта-прототипа из-за следующего: 1.) Сначала ищется ben.food, и любое действие поиска просто ищет цепочку области видимости. 2.) Функция push этого объекта ben.food выполняется. Под написанием режима в моем ответе я подразумеваю, когда вы явно устанавливаете для него значение, например: ben.food = ['Idly']; Это всегда создаст новое свойство (если его еще нет) в объекте продукта, а затем присвоит ему значение.
Показать ещё 4 комментария
9

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

  • Свойство .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]]. На нем определены все доступные методы для новых объектов:

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

Теперь, поскольку .prototype является объектом, он имеет свойство [[Prototype]]. Если вы не выполняете никаких присвоений Function.prototype, .prototype [[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.

9

Рассмотрим следующий объект keyValueStore:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Я могу создать новый экземпляр этого объекта, выполнив следующее:

kvs = keyValueStore.create();

Каждый экземпляр этого объекта имеет следующие общедоступные свойства:

  • data
  • get
  • set
  • delete
  • getLength

Теперь предположим, что мы создаем 100 экземпляров этого объекта keyValueStore. Даже если get, set, delete, getLength будут делать то же самое для каждого из этих 100 экземпляров, каждый экземпляр имеет свою собственную копию этой функции.

Теперь представьте, можете ли вы иметь только одну копию get, set, delete и getLength, и каждый экземпляр ссылается на эту же функцию. Это будет лучше для производительности и потребует меньше памяти.

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

Теперь рассмотрим объект keyValueStore еще раз. Я мог бы переписать его вот так:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Это ТОЧНО аналогично предыдущей версии объекта keyValueStore, за исключением того, что все его методы теперь помещены в прототип. Это означает, что все 100 экземпляров теперь используют эти четыре метода, а не каждый из них имеет свою собственную копию.

8

Еще одна попытка объяснить наследование на основе прототипа JavaScript с лучшими снимками

Изображение 2095

7

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

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

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

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

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

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

4

Резюме:

  • Функции являются объектами в JavaScript и, следовательно, могут иметь свойства
  • (Конструктор) функции всегда имеют свойство прототипа
  • Когда функция используется в качестве конструктора с 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);
3

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

2

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

0

Важно понимать, что существует различие между прототипом объекта (который доступен через Object.getPrototypeOf(obj) или через устаревшее свойство obj.__proto__) и свойством prototype в функциях конструктора. Первый является свойством каждого экземпляра, а второй - свойством конструктора. То есть Object.getPrototypeOf(new Foobar()) ссылается на тот же объект, что и Foobar.prototype.

Ссылка: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

0

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню