2008-12-24 13 views
16

He estado haciendo algo de herencia en js para entenderlo mejor, y encontré algo que me confunde.Javascript Prototypal Herencia Duda II

Sé que cuando llamas una 'función de constructor' con la nueva palabra clave, obtienes un objeto nuevo con una referencia al prototipo de esa función.

También sé que para hacer una herencia prototípica debe reemplazar el prototipo de la función constructora con una instancia del objeto que quiere que sea la 'superclase'.

así lo hice este ejemplo tonto para tratar estos conceptos:

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! 

(esto se hizo en la consola de Firebug)

1) ¿Por qué si todos los nuevos objetos contienen una referencia al prototipo de la función de creador, fido.prototype no está definido?

2) ¿Es la cadena de herencia [obj] -> [constructor] -> [prototipo] en lugar de [obj] -> [prototipo]?

3) ¿La propiedad "prototipo" de nuestro objeto (fido) alguna vez se ha verificado? si es así ... ¿por qué 'deathBite' no está definido (en la última parte)?

Gracias!

Respuesta

12

1) ¿Por qué si todos los nuevos objetos contienen una referencia a prototipo de la función de creador, fido.prototype es indefinido?

Todos los objetos nuevos tienen una referencia al prototipo que estaba presente en su constructor en el momento de la construcción. Sin embargo, el nombre de propiedad utilizado para almacenar esta referencia no es prototype, ya que está en la propia función del constructor. Algunas implementaciones de Javascript permiten el acceso a esta propiedad 'oculta' a través de un nombre de propiedad como __proto__ donde otros no lo hacen (por ejemplo, Microsofts).

2) es la cadena de herencia [obj] -> [constructor] -> [prototipo] en lugar de [obj] -> [Prototype]?

Nº Tome un vistazo a esto: -

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

Esto alerta "primero" no segundo. Si la cadena de herencia fuera a través del constructor, veríamos "Segundo". Cuando se construye un objeto, la referencia actual mantenida en la propiedad del prototipo Funciones se transfiere a la referencia oculta del objeto a su prototipo.

3) es la propiedad 'prototipo' de nuestro objeto (fido) alguna vez comprobado? si es así ... ¿por qué 'undefined' no está definido (en la última parte )?

Asignación a un objeto (que no sea una función) una propiedad llamada prototype tiene ningún significado especial, como se dijo anteriormente un objeto no mantiene una referencia a su prototipo a través de un nombre de dicha propiedad.

+1

¡Agradable! Tenga en cuenta que si declaró var x = new Derived() DESPUÉS del Derived.prototype = new Base2(), verá 'Second'. x simplemente está sosteniendo un puntero al prototipo Derived original, pero eso no significa que, de hecho, no lo hayamos redirigido a Base2(). Los indicadores simplemente han cambiado. Nada contradictorio con lo que Anthony está diciendo, solo aclarando eso último. Vea mis ejemplos: http://github.com/roblevintennis/Testing-and-Debugging-JavaScript/blob/master/code/objects/lib/js_inheritance.js – Rob

+0

Ah, y creo que quiso decir: __proto__ not __prototype__ – Rob

+0

Markdown, ergh! guión bajo subrayado guión bajo subrayado subrayado – Rob

7

No se puede cambiar el prototipo de un objeto una vez que se ha instanciado con new.

En el ejemplo anterior, líneas como

fido.prototype = new KillerDog(); 

simplemente crea un nuevo atributo llamado prototype en el objeto fido, y establece que el atributo a un nuevo KillerDog objeto. No es diferente de

fido.foo = new KillerDog(); 

como su código se encuentra ...

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

El comportamiento especial prototype se aplica sólo a los constructores en JavaScript, donde los constructores están function s que serán llamados a new.

+0

Puedes en Mozilla (Firefox, etc.). Pero sí, eso normalmente no es práctico. – thomasrutter

4

respuesta por números a sus preguntas: propiedad prototype

  1. del objeto no se llama prototype. El estándar usa [[prototype]] para designarlo. Firefox hace que esta propiedad sea pública bajo el nombre de __proto__.
  2. La cadena de herencia es [obj][prototype object]. Su hipótesis inicial ([obj][constructor][prototype]) es incorrecta y se puede refutar fácilmente modificando constructor y/o constructor.prototype, y comprobar qué métodos pueden ser llamados en su [obj] — usted descubrirá que estas modificaciones no cambian nada.
  3. prototype propiedad en los objetos no se verifican y no se utilizan. Puedes configurarlo como quieras. JavaScript lo usa en objetos de función solo durante la construcción del objeto.

Para demostrar el # 3 Este es el código de Dojo:

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

Como se puede ver que se aprovecha del hecho de que prototype se utiliza sólo en un lugar mediante la reutilización de la misma función TMP de todos los objetos delegados con diferentes prototipos. De hecho, prototype se asigna directamente antes de invocar la función con new, y se modificará después de que no afecte a ningún objeto creado.

Puede encontrar la secuencia objeto creado en mi respuesta al Relation between [[Prototype]] and prototype in JavaScript.

2

Sé que ya ha sido respondida, pero hay una mejor manera de hacer herencia. Llamar a un constructor solo con el propósito de heredar no es deseable. Uno de los efectos no deseados es.

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

Child.prototype = new Base(); 

Ahora ha agregado la propiedad "a" al prototipo de Niño que no tenía la intención de.

Ésta es la manera correcta (Yo no inventé esto, Ext-JS y otras librerías utilizan este)

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

Una forma aún más fácil es agregar un tercer parámetro para extender donde se especifica el método de la clase derivada de modo que usted no tiene que llamar a extender y luego añadir métodos al prototipo

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

a continuación, hay muchas otras cosas que podría hacer para el azúcar sintáctica.

escribió sobre él: http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

2

A tener en cuenta que, en ECMAScript 5 (es decir, la última versión del lenguaje JavaScript) se puede obtener acceso a la interna [[Prototype]] propiedad de una instancia a través de Object.getPrototypeOf:

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