Вопрос: Как работает JavaScript .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

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

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


редактировать

Эти слайды действительно помогли понять эту тему.


1842


источник


Ответы:


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

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

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

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


920



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

1749



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

Метод 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"]);

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

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


Резюме:

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

new FunctionName()

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

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

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

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

The prototype chain

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


161



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


66



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

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance *[[protytype]]* and <code>prototype</code> property of function objects

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

а также

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

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

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

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

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


59



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

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

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


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

  • свойство прототипа, как в 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

55



Every object has an internal property, [[Prototype]], linking it to another object:

object [[Prototype]] -> anotherObject

In traditional javascript, the linked object is the prototype property of a function:

object [[Prototype]] -> aFunction.prototype

Some environments expose [[Prototype]] as __proto__:

anObject.__proto__ === anotherObject

You create the [[Prototype]] link when creating an object.

// (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

So these statements are equivalent:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

A new statement doesn't show the link target (Object.prototype) itself; instead the target is implied by the constructor (Object).

Remember:

  • Every object has a link, [[Prototype]], sometimes exposed as __proto__.
  • Every function has a prototype property.
  • Objects created with new are linked to the prototype property of their constructor.
  • If a function is never used as a constructor, its prototype property will go unused.
  • If you don't need a constructor, use Object.create instead of new.

32



Javascript doesn't have inheritance in the usual sense, but it has the prototype chain.

prototype chain

If a member of an object can't be found in the object it looks for it in the prototype chain. The chain consists of other objects. The prototype of a given instance can be accessed with the __proto__ variable. Every object has one, as there is no difference between classes and instances in javascript.

The advantage of adding a function / variable to the prototype is that it has to be in the memory only once, not for every instance.

It's also useful for inheritance, because the prototype chain can consist of many other objects.


23



This article is long. But I am sure it will clear most of your queries regarding the "prototypical" nature of JavaScript Inheritance. And even more. Please read the complete article.

JavaScript basically has two kinds of data types

  • Non objects
  • Objects

Non objects

Following are the Non object data types

  • string
  • number (including NaN and Infinity)
  • boolean values(true,false)
  • undefined

These data types return following when you use the typeof operator

typeof "string literal" (or a variable containing string literal) === 'string'

typeof 5 (or any numeric literal or a variable containing numeric literal or NaN or Infynity) === 'number'

typeof true (or false or a variable containing true or false) === 'boolean'

typeof undefined (or an undefined variable or a variable containing undefined) === 'undefined'

The string,number and boolean data types can be represented both as Objects and Non objects.When they are represented as objects their typeof is always === 'object'. We shall come back to this once we understand the object data types.

Objects

The object datatypes can be further divided into two types

  1. Function type objects
  2. Non Function type objects

The Function type objects are the ones that return the string 'function' with typeof operator. All the user defined functions and all the JavaScript built in objects that can create new objects by using new operator fall into this category. For eg.

  • Object
  • String
  • Number
  • Boolean
  • Array
  • Typed Arrays
  • RegExp
  • Function
  • All the other built in objects that can create new objects by using new operator
  • function UserDefinedFunction(){ /*user defined code */ }

So, typeof(Object) === typeof(String) === typeof(Number) === typeof(Boolean) === typeof(Array) === typeof(RegExp) === typeof(Function) === typeof(UserDefinedFunction) === 'function'

All the Function type objects are actually instances of the built in JavaScript object Function (including the Function object i.e it is recursively defined). It is as if the these objects have been defined in the following way

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")

As mentioned, the Function type objects can further create new objects using the new operator. For e.g an object of type Object, String, Number, Boolean, Array, RegExp Or UserDefinedFunction can be created by using

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() 

The objects thus created are all Non Function type objects and return their typeof==='object'. In all these cases the object "a" cannot further create objects using operator new. So the following is wrong

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

The built in object Math is typeof==='object'. Hence a new object of type Math cannot be created by new operator.

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

Also notice that Object,Array and RegExp functions can create a new object without even using operator new. However the follwing ones don't.

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'

The user defined functions are special case.

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

Since the Function type objects can create new objects they are also called Constructors.

Every Constructor/Function (whether built in or user defined) when defined automatically has a property called "prototype" whose value by default is set as an object. This object itself has a property called "constructor" which by default references back the Constructor/Function .

For example when we define a function

function UserDefinedFunction()
{
}

following automatically happens

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

This "prototype" property is only present in the Function type objects (and never in Non Function type objects).

This is because when a new object is created (using new operator)it inherits all properties and methods from Constructor function's current prototype object i.e. an internal reference is created in the newly created object that references the object referenced by Constructor function's current prototype object.

This "internal reference" that is created in the object for referencing inherited properties is known as the object's prototype (which references the object referenced by Constructor's "prototype" property but is different from it). For any object (Function or Non Function) this can be retrieved using Object.getPrototypeOf() method. Using this method one can trace the prototype chain of an object.

Also, every object that is created (Function type or Non Function type) has a "constructor" property which is inherited from the object referenced by prototype property of the Constructor function. By default this "constructor" property references the Constructor function that created it (if the Constructor Function's default "prototype" is not changed).

For all Function type objects the constructor function is always function Function(){}

For Non Function type objects (e.g Javascript Built in Math object) the constructor function is the function that created it. For Math object it is function Object(){}.

All the concept explained above can be a little daunting to understand without any supporting code. Please go through the following code line by line to understand the concept. Try to execute it to have a better understanding.

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"

The prototype chain of every object ultimately traces back to Object.prototype (which itself does not have any prototype object) . Following code can be used for tracing the prototype chain of an object

var o=Starting object;

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

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

The prototype chain for various objects work out as follows.

  • Every Function object (including built in Function object)-> Function.prototype -> Object.prototype -> null
  • Simple Objects (created By new Object() or {} including built in Math object)-> Object.prototype -> null
  • Object created with new or Object.create -> One or More prototype chains -> Object.prototype -> null

For creating an object without any prototype use the following:

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

One might think that setting the prototype property of the Constructor to null shall create an object with a null prototype. However in such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object. This is demonstrated by the following code

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

Following in the summary of this article

  • There are two types of objects Function types and Non Function types
  • Only Function type objects can create a new object using the operator new. The objects thus created are Non Function type objects. The Non Function type objects cannot further create an object using operator new.

  • All Function type objects by default have a "prototype" property. This "prototype" property references an object that has a "constructor" property that by default references the Function type object itself.

  • All objects (Function type and Non Function type) have a "constructor" property that by default references the Function type object/Constructor that created it.

  • Every object that gets created internally references the object referenced by "prototype" property of the Constructor that created it. This object is known as the created object's prototype (which is different from Function type objects "prototype" property which it references) . This way the created object can directly access the methods and properties defined in object referenced by the Constructor's "prototype" property (at the time of object creation).

  • An object's prototype (and hence its inherited property names) can be retrieved using the Object.getPrototypeOf() method. In fact this method can be used for navigating the entire prototype chain of the object.

  • The prototype chain of every object ultimately traces back to Object.prototype (Unless the object is created using Object.create(null) in which case the object has no prototype).

  • typeof(new Array())==='object' is by design of language and not a mistake as pointed by Douglas Crockford

  • Setting the prototype property of the Constructor to null(or undefined,number,true,false,string) shall not create an object with a null prototype. In such cases the newly created object's prototype is set to Object.prototype and its constructor is set to function Object.

Hope this helps.


23



The concept of prototypal inheritance is one of the most complicated for many developers. Let's try to understand the root of problem to understand prototypal inheritance better. Let's start with a plain function.

enter image description here

If we use a new operator on the Tree function, we call it as a constructor function.

enter image description here

Every JavaScript function has a prototype. When you log the Tree.prototype, you get...

enter image description here

If you look at the above console.log() output, you could a see a constructor property on Tree.prototype and a __proto__ property too. The __proto__ represents the prototype that this function is based off, and since this is just a plain JavaScript function with no inheritance set up yet, it refers to the Object prototype which is something just built in to JavaScript...

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

This has things like .toString, .toValue, .hasOwnProperty etc...

__proto__ which was brought my mozilla is deprecated and is replaced by Object.getPrototypeOf method to get the object's prototype.

enter image description here

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

Let's add a method to our Tree prototype.

enter image description here

We have modified the Root and added a function branch to it.

enter image description here

That means when you create an instance of Tree, you can call it's branch method.

enter image description here

We can also add primitives or objects to our Prototype.

enter image description here

Let's add a child-tree to our Tree.

enter image description here

Here the Child inherits its prototype from Tree, what we are doing here is using Object.create() method to create a new object based off what you pass, here it is Tree.prototype. In this case what we're doing is setting the prototype of Child to a new object that looks identical to the Tree prototype. Next we are setting the Child's constructor to Child, if we don't it would point to Tree().

enter image description here

Child now has its own prototype, its __proto__ points to Tree and Tree's prototype points to base Object.

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

Now you create an instance of Child and call branch which is originally available in Tree. We haven't actually defined our branch on the Child prototype. BUT, in the Root prototype which Child inherits from.

enter image description here

In JS everything is not an object, everything can act like an object.

Javascript has primitives like strings, number, booleans, undefined, null. They are not object(i.e reference types), but certainly can act like an object. Let's look at an example here.

enter image description here

In the first line of this listing, a primitive string value is assigned to name. The second line treats name like an object and calls charAt(0) using dot notation.

This is what happens behind the scenes: // what the JavaScript engine does

enter image description here

The String object exists only for one statement before it’s destroyed (a process called autoboxing). Let's again get back to our prototypal inheritance.

  • Javascript supports inheritance via delegation based on prototypes.
  • Each Function has a prototype property, which refers to another object.
  • properties/functions are looked from the object itself or via prototype chain if it does not exist

A prototype in JS is an object which yields you to the parent of another object. [ie.. delegation] Delegation means that if you are unable to do something, you’ll tell someone else to do it for you.

enter image description here

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

If you look up the above fiddle, dog has access to toString method, but its not available in it, but available via the prototype chain which delegates to Object.prototype

enter image description here

If you look at the below one, we are trying to access the call method which is available in every function.

enter image description here

https://jsfiddle.net/rknffckc/

If you look up the above fiddle, Profile Function has access to call method, but its not available in it, but available via the prototype chain which delegates to Function.prototype

enter image description here

Note: prototype is a property of the function constructor, whereas __proto__ is a property of the objects constructed from the function constructor. Every function comes with a prototype property whose value is an empty object. When we create an instance of the function, we get an internal property [[Prototype]] or __proto__ whose reference is the prototype of the Function constructor.

enter image description here

The above diagram looks bit complicated, but brings out the whole picture on how prototype chaining works. Let's walk through this slowly:

There are two instance b1 and b2, whose constructor is Bar and parent is Foo and has two methods from prototype chain identify and speak via Bar and Foo

enter image description here

https://jsfiddle.net/kbp7jr7n/

If you look up the code above, we have Foo constructor who has the method identify() and Bar constructor which has speak method. We create two Bar instance b1 and b2 whose parent type is Foo. Now while calling speak method of Bar, we are able to identify the who is calling the speak via prototype chain.

enter image description here

Bar now has all the methods of Foo which are defined in its prototype. Let's dig further in understanding the Object.prototype and Function.prototype and how they are related. If you look up the constructor of Foo, Bar and Object are Function constructor.

enter image description here

The prototype of Bar is Foo, prototype of Foo is Object and if you look closely the prototype of Foo is related to Object.prototype.

enter image description here

Before we close this down, let's just wrap with a small piece of code here to summarize everything above. We are using instanceof operator here to check whether an object has in its prototype chain the prototype property of a constructor which below summarizes the entire big diagram.

enter image description here

I hope this add's some information, I know this kinda could be big to grasp... in simple words its it's just objects linked to objects!!!!


19



what is the exact purpose of this ".prototype" property?

The interface to standard classes become extensible. For example, you are using the Array class and you also need to add a custom serializer for all your array objects. Would you spend time coding up a subclass, or use composition or ... The prototype property solves this by letting the users control the exact set of members/methods available to a class.

Think of prototypes as an extra vtable-pointer. When some members are missing from the original class, the prototype is looked up at runtime.


17



It may help to categorise prototype chains into two categories.

Consider the constructor:

 function Person() {}

The value of Object.getPrototypeOf(Person) is a function. In fact, it is Function.prototype. Since Person was created as a function, it shares the same prototype function object that all functions have. It is the same as Person.__proto__, but that property should not be used. Anyway, with Object.getPrototypeOf(Person) you effectively walk up the ladder of what is called the prototype chain.

The chain in upward direction looks like this:

    PersonFunction.prototypeObject.prototype (end point)

Important is that this prototype chain has little to do with the objects that Person can construct. Those constructed objects have their own prototype chain, and this chain can potentially have no close ancestor in common with the one mentioned above.

Take for example this object:

var p = new Person();

p has no direct prototype-chain relationship with Person. Their relationship is a different one. The object p has its own prototype chain. Using Object.getPrototypeOf, you'll find the chain is as follows:

    pPerson.prototypeObject.prototype (end point)

There is no function object in this chain (although that could be).

So Person seems related to two kinds of chains, which live their own lives. To "jump" from one chain to the other, you use:

  1. .prototype: jump from the constructor's chain to the created-object's chain. This property is thus only defined for function objects (as new can only be used on functions).

  2. .constructor: jump from the created-object's chain to the constructor's chain.

Here is a visual presentation of the two prototype chains involved, represented as columns:

enter image description here

To summarise:

The prototype property gives no information of the subject's prototype chain, but of objects created by the subject.

It is no surprise that the name of the property prototype can lead to confusion. It would maybe have been clearer if this property had been named prototypeOfConstructedInstances or something along that line.

You can jump back and forth between the two prototype chains:

Person.prototype.constructor === Person

This symmetry can be broken by explicitly assigning a different object to the prototype property (more about that later).

Create one Function, Get Two Objects

Person.prototype is an object that was created at the same time the function Person was created. It has Person as constructor, even though that constructor did not actually execute yet. So two objects are created at the same time:

  1. The function Person itself
  2. The object that will act as prototype when the function is called as a constructor

Both are objects, but they have different roles: the function object constructs, while the other object represents the prototype of any object that function will construct. The prototype object will become the parent of the constructed object in its prototype chain.

Since a function is also an object, it also has its own parent in its own prototype chain, but recall that these two chains are about different things.

Here are some equalities that could help grasp the issue -- all of these 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);

Adding levels to the prototype chain

Although a prototype object is created when you create a constructor function, you can ignore that object, and assign another object that should be used as prototype for any subsequent instances created by that constructor.

For instance:

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

Now the prototype chain of t is one step longer than that of p:

    tpPerson.prototypeObject.prototype (end point)

The other prototype chain is not longer: Thief and Person are siblings sharing the same parent in their prototype chain:

    Person}
    Thief  } → Function.prototypeObject.prototype (end point)

The earlier presented graphic can then be extended to this (the original Thief.prototype is left out):

enter image description here

The blue lines represent prototype chains, the other coloured lines represent other relationships:

  • between an object and its constructor
  • between a constructor and the prototype object that will be used for constructing objects

15