Как работает JavaScript .prototype? - PullRequest
1948 голосов
/ 21 февраля 2009

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

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

Ответы [ 25 ]

1780 голосов
/ 24 января 2011

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

//The following statement generates an error.
john.setAmountDue(1000);
965 голосов
/ 21 февраля 2009

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

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

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

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

170 голосов
/ 13 февраля 2014

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


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

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

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

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


Резюме:

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

new FunctionName()

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

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

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

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

The prototype chain

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

74 голосов
/ 21 февраля 2009

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.

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

67 голосов

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

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

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


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. Наши две строки кода создали только 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
65 голосов
/ 07 ноября 2012

Прочитав эту ветку, я запутался в JavaScript Prototype Chain, а затем нашел эти диаграммы

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance imageprototype property of function objects">

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

и

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

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

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

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

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

37 голосов
/ 18 июня 2013

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

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;

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

Помните:

  • Каждый объект имеет ссылку, [[Prototype]], иногда обозначаемую как __proto__.
  • Каждая функция имеет свойство prototype.
  • Объекты, созданные с помощью new, связаны со свойством prototype их конструктора.
  • Если функция никогда не используется в качестве конструктора, ее свойство prototype не будет использоваться.
  • Если вам не нужен конструктор, используйте Object.create вместо new.
26 голосов
/ 04 ноября 2015

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

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

  • Не объекты
  • Предметы

Не объекты

Ниже приведены Необъект типы данных

  • строка
  • число (включая NaN и Infinity)
  • логические значения (true, false)
  • неопределенный

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

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

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

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

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

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

Объекты

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

  1. Объекты типа функции
  2. Объекты не функционального типа

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

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

Итак, typeof (Объект) === typeof (Строка) === typeof (Число) === typeof (Булево) = == typeof (Массив) === typeof (RegExp) === typeof (Функция) === typeof (UserDefinedFunction) === 'функция'

Все объекты типа функции фактически являются экземплярами встроенного объекта 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 . Например, для объекта типа Object , String , Number , Boolean , Array , 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() 

Все созданные таким образом объекты являются объектами, не относящимися к типу функции * и возвращают их typeof === 'object' . Во всех этих случаях объект "а" не может в дальнейшем создавать объекты с использованием оператора 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'

Также обратите внимание, что функции Object , Array и 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" присутствует только в объектах типов функций (и никогда в Объекты не функционального типа ).

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

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

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

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

Для Объекты не-функционального типа (например, Javascript Built in Math object) функция конструктора - это функция, которая его создала. Для Math объекта это function 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
  • Объект создан с помощью new или Object.create -> Одна или несколько цепочек прототипов -> 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 . Созданные таким образом объекты являются объектами, не относящимися к типу функции . Объекты не-типа не могут в дальнейшем создавать объект с помощью оператора new .

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

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

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

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

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

  • typeof (new Array ()) === 'object' написан на языке и не является ошибкой, как указано Douglas Crockford

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

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

25 голосов
/ 21 февраля 2009

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

прототип цепи

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

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

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

22 голосов
/ 04 июля 2016

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

enter image description here

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

enter image description here

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

enter image description here

Если вы посмотрите на вышеприведенный вывод 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__, который был доставлен, моя mozilla устарела и заменена методом Object.getPrototypeOf, чтобы получить object's prototype.

enter image description here

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

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

enter image description here

Мы изменили Root и добавили к нему ветку function.

enter image description here

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

enter image description here

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

enter image description here

Давайте добавим child-tree к нашему Tree.

enter image description here

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

enter image description here

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

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

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

enter image description here

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

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

enter image description here

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

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

enter image description here

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

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

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

enter image description here

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

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

enter image description here

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

enter image description here

https://jsfiddle.net/rknffckc/

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

enter image description here

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

enter image description here

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

Существуют два экземпляра b1 и b2, конструктор которых Bar, а родительский элемент - Foo и имеет два метода из цепочки прототипов identify и speak через Bar и Foo

enter image description here

https://jsfiddle.net/kbp7jr7n/

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

enter image description here

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

enter image description here

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

enter image description here

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

enter image description here

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...