Вопрос

Я занимался некоторым наследованием в 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.prototype не определен?

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

Вы не можете изменить прототип объекта после того, как он был создан с помощью 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.

Отвечайте цифрами на ваши вопросы:

  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" к прототипу дочернего элемента, которое вы не собирались делать.

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

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top