Прототипное наследование Javascript?

Я занимался наследованием в js, чтобы лучше его понять, и нашел кое-что, что меня смущает.

Я знаю, что когда вы вызываете «функцию-конструктор» с ключевым словом new, вы получаете новый объект со ссылкой на прототип этой функции.

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

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

function Animal(){}
function Dog(){}

Animal.prototype.run = function(){alert("running...")};

Dog.prototype = new Animal(); 
Dog.prototype.bark = function(){alert("arf!")};

var fido = new Dog();
fido.bark() //ok
fido.run() //ok

console.info(Dog.prototype) // its an 'Object' 
console.info(fido.prototype) // UNDEFINED
console.info(fido.constructor.prototype == Dog.prototype) //this is true

function KillerDog(){};
KillerDog.prototype.deathBite = function(){alert("AAARFFF! *bite*")}

fido.prototype = new KillerDog();

console.info(fido.prototype) // no longer UNDEFINED
fido.deathBite(); // but this doesn't work!

(Это было сделано в консоли Firebug)

1) Почему, если все новые объекты содержат ссылку на прототип функции-создателя, fido.prototype не определен?

2) Цепочка наследования [obj] -> [constructor] -> [prototype] вместо [obj] -> [prototype]?

3) проверялось ли когда-либо свойство прототипа нашего объекта (fido)? если да ... почему 'deathBite' не определен (в последней части)?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
16
0
3 395
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вы не можете изменить прототип объекта после того, как он был создан с помощью new.

В приведенном выше примере строки вроде

fido.prototype = new KillerDog();

просто создает новый атрибут с именем prototype для объекта fido и устанавливает этот атрибут для нового объекта KillerDog. Это не отличается от

fido.foo = new KillerDog();

Поскольку ваш код стоит ...

// Doesn't work because objects can't be changed via their constructors
fido.deathBite();

// Does work, because objects can be changed dynamically, 
// and Javascript won't complain when you use prototype 
//as an object attribute name
fido.prototype.deathBite();

Особое поведение prototype применяется только к конструкторам в javascript, где конструкторами являются function, которые будут вызываться с помощью new.

Вы можете в Mozilla (Firefox и т. д.). Но да, это обычно непрактично.

thomasrutter 17.04.2010 20:44
Ответ принят как подходящий

1) Why if all new objects contain a reference to the creator function's prototype, fido.prototype is undefined?

Все новые объекты содержат ссылку на прототип, который присутствовал в их конструкторе во время создания. Однако имя свойства, используемое для хранения этой ссылки, не prototype, как в самой функции-конструкторе. Некоторые реализации Javascript действительно разрешают доступ к этому «скрытому» свойству через какое-то имя свойства, например __proto__, а другие - нет (например, Microsoft).

2) Is the inheritance chain [obj] -> [constructor] -> [prototype] instead of [obj] -> [prototype] ?

Нет. Взгляните на это: -

function Base() {}
Base.prototype.doThis = function() { alert("First"); }

function Base2() {}
Base2.prototype.doThis = function() { alert("Second"); }

function Derived() {}
Derived.prototype = new Base()

var x = new Derived()

Derived.prototype = new Base2()

x.doThis();

Это предупреждает "Первого", а не Второго. Если бы цепочка наследования прошла через конструктор, мы увидели бы «Второй». Когда объект создается, текущая ссылка, содержащаяся в свойстве прототипа функций, переносится на объект, скрытая ссылка на его прототип.

3) is the 'prototype' property of our object (fido) ever checked? if so... why is 'deathBite' undefined (in the last part)?

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

Хороший! Обратите внимание: если вы объявили var x = new Derived () ПОСЛЕ Derived.prototype = new Base2 (), вы увидите «Second». x просто содержит указатель на исходный Derived.prototype, но это не означает, что мы фактически не перенаправляли его на Base2 (). Указатели просто поменяли. Ни в коем случае не противоречит тому, что говорит Энтони, а просто уточняет последний момент. Смотрите мои примеры: github.com/roblevintennis/Testing-and-Debugging-JavaScript/b‌ lob /…

Rob 29.10.2009 17:53

О, и я думаю, вы имели в виду: прото, а не опытный образец

Rob 29.10.2009 17:55

Уценка, эх! подчеркивание подчеркивание прото подчеркивание подчеркивание

Rob 29.10.2009 17:55

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

Juan Mendes 31.07.2010 02:20

Ответьте по номерам на ваши вопросы:

  1. Свойство прототипа объекта не называется prototype. Стандарт использует для обозначения [[prototype]]. Firefox делает это свойство общедоступным под именем __proto__.
  2. Цепочка наследования - [obj][prototype object]. Ваше исходное предположение ([obj][constructor][prototype]) неверно, и вы можете легко опровергнуть его, изменив constructor и / или constructor.prototype и проверив, какие методы могут быть вызваны на вашем [obj] - вы обнаружите, что эти модификации ничего не меняют.
  3. Свойство prototype на объектах не проверяется и не используется. Вы можете установить все, что захотите. JavaScript использует его для объектов функций только во время создания объекта.

Чтобы продемонстрировать # 3, вот код из Додзё:

dojo.delegate = dojo._delegate = (function(){
  // boodman/crockford delegation w/ cornford optimization
  function TMP(){}
  return function(obj, props){
    TMP.prototype = obj;
    var tmp = new TMP();
    if (props){
      dojo._mixin(tmp, props);
    }
    return tmp; // Object
  }
})();

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

Вы можете найти последовательность создания объекта в моем ответе на Связь между [[Prototype]] и прототипом в JavaScript.

Я знаю, что на него уже ответили, но есть способ лучше наследовать. Вызывать конструктор только с целью наследования нежелательно. Один из нежелательных эффектов - это.

function Base() {this.a = "A"}
function Child() {this.b = "B"};

Child.prototype = new Base();

Теперь вы добавили свойство «a» к прототипу Child, которое вы не намеревались делать.

Вот правильный путь (это не я изобретал, это используют Ext-JS и другие библиотеки)

// This is used to avoid calling a base class's constructor just to setup inheritance.
function SurrogateCtor() {}

/**
 * Sets a contructor to inherit from another constructor
 */
function extend(BaseCtor, DerivedCtor) {
  // Copy the prototype to the surrogate constructor
  SurrogateCtor.prototype = BaseCtor.prototype;
  // this sets up the inheritance chain
  DerivedCtor.prototype = new SurrogateCtor();
  // Fix the constructor property, otherwise it would point to the BaseCtor
  DerivedCtor.prototype.constructor = DerivedCtor;
  // Might as well add a property to the constructor to 
  // allow for simpler calling of base class's method
  DerivedCtor.superclass = BaseCtor;
}

function Base() {
  this.a = "A";
}

Base.prototype.getA = function() {return this.a}

function Derived() {
  Derived.superclass.call(this);  // No need to reference the base class by name
  this.b = "B";
}

extend(Base, Derived);
// Have to set methods on the prototype after the call to extend
// otherwise the prototype is overridden;
Derived.prototype.getB = function(){return this.b};
var obj = new Derived();

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

extend(BaseCtor, DerivedCtor, {
  getB: function() {return this.b}
});

Тогда есть много других вещей, которые вы можете сделать для синтаксического сахара.

Об этом в блоге: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Стоит отметить, что в ECMAScript 5 (т.е. последней версии языка JavaScript) вы можете получить доступ к внутреннему свойству [[Prototype]] экземпляра через Object.getPrototypeOf:

Object.getPrototypeOf(fido) === Dog.prototype

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