Прототипное Наследование 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.log(Dog.prototype) // its an 'Object' 
console.log(fido.prototype) // UNDEFINED
console.log(fido.constructor.prototype == Dog.prototype) //this is true

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

fido.prototype = new KillerDog();

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

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

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

2) является ли цепочка наследования [obj] -> [конструктор] -> [прототип] вместо [obj] -> [прототип] ?

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

5 ответов


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

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

2) является цепочкой наследования [obj] - > [конструктор] -> [прототип] вместо [obj] - > [прототип]?

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

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) свойство "прототипа" нашего объект (Фидо) когда-нибудь проверялся? если так... почему deathBite' не определено (в последняя часть)?

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


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

в вашем примере выше, строки

fido.prototype = new KillerDog();

просто создает новый атрибут с именем prototype на объекте fido, и устанавливает этот атрибут в новый


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

  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]] и prototype в JavaScript.


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

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

Child.prototype = new Base();

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

вот правильный способ (я не изобретал это, 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, а затем добавьте методы в прототип

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