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

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

Решение

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

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

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

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

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

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

Пример:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Хотя, как я уже сказал, я не могу вызвать setAmountDue(), getAmountDue() для Person.

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

Я играю роль учителя 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() вызывает функцию с заданным значением this и аргументами, представленными в виде массива (или объекта, похожего на массив).

а также

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

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

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

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

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


Резюме:

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

new FunctionName()

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

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

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

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

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

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

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 функция.

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

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

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

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


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.constructorJavaScript делает . искать как:

  • 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://stackru.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

Прочитав эту ветку, я запутался в цепочке прототипов JavaScript, а затем нашел эти графики

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html protytype]] * и свойство <code> prototype </ code> функциональных объектов prototype функциональных объектов">

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

а также

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

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

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

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

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

Каждый объект имеет внутреннее свойство [[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,

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

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

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

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

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

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

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

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

Не объекты

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

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

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

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

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

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

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

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

Объекты

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

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

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

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

Итак, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === 'function'

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

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

Как уже упоминалось, объекты типа Function могут дополнительно создавать новые объекты с помощью оператора 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() 

Все объекты, созданные таким образом, являются объектами типа Non Function и возвращают их 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.

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

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

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

function UserDefinedFunction()
{
}

следующее автоматически происходит

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

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

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

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

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

Для всех объектов типа Function функция конструктора всегда является функцией 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
  • Объект, созданный с помощью 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

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

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

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

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

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

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

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

  • typeof (new Array ()) === "объект" создан по типу языка и не является ошибкой, на что указывает Дуглас Крокфорд

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

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

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

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

 function Person() {}

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

Цепочка в направлении вверх выглядит так:

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

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

Возьмем для примера этот объект:

var p = new Person();

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

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

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

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

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

  2. .constructor: перейти от цепочки созданного объекта к цепочке конструктора.

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

Чтобы подвести итог:

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

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

Вы можете прыгать туда-сюда между двумя цепями прототипов:

Person.prototype.constructor === Person

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

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

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

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

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

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

Вот некоторые равенства, которые могут помочь понять проблему - все эти печати 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 опущено):

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

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

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

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

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

Если вы посмотрите на выше 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,

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

Давайте добавим метод к нашему Treeprototype,

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

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

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

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

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

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 от которого ребенок наследует.

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

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

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

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

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

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

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

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

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

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

https://jsfiddle.net/rknffckc/

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

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

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

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

https://jsfiddle.net/kbp7jr7n/

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

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

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

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

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

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

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

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

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

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

граф объектов JS

http://jsobjects.org/

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

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

  • .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 s это из Object функция Этот прототип содержит прототип объекта, который все [[Prototype]] цепочки содержат. На нем определены все доступные методы для новых объектов:

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

Теперь, так как .prototype это объект, он имеет [[Prototype]] имущество. Когда вы не делаете никаких назначений Function.prototype, .prototype "s [[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 ,

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

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

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

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

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

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

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

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

Еще одна попытка объяснить наследование на основе прототипов JavaScript с помощью более качественных изображений

Простые объекты наследуются

Резюме:

  • Функции являются объектами в 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);

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

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

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

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

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

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

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

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

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

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

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

Если вы хотите понять концепцию прототипа и наследования на основе прототипа из основ, проверьте официальную документацию MDN, они довольно хорошо объясняют это.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

Что касается наследования, в JavaScript есть только одна конструкция: объекты. Каждый объект имеет частное свойство, которое содержит ссылку на другой объект, называемый его прототипом. У этого объекта-прототипа есть собственный прототип, и так до тех пор, пока не будет достигнут объект с нулевым значением в качестве его прототипа. По определению null не имеет прототипа и действует как последнее звено в этой цепочке прототипов.

Кроме того, вот еще один хороший ресурс, который объясняет на простых примерах - https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes.

Другие вопросы по тегам