2012-06-17 12 views
5

tengo una función constructora, que actúa como una superclase:objetos no heredan las funciones de prototipos

Bla = function(a){this.a = a;} 

I Modelo para incluir un método simple:

Bla.prototype.f = function(){console.log("f"); 

Y ahora lo hará nueva Bla(1).f(); Inicie sesión "f" en la consola. Pero, digamos que necesito una subclase que hereda de Bla:

Bla2 = function(a) 
{ 
    this.base = Bla; 
    this.base(); 
} 

x = new Bla2(5); 

Ahora, como se esperaba, x.a me da 5. ¡Pero, x.f es undefined! Parece que Bla2 no lo heredó de la clase Bla! ¿Por qué está sucediendo esto y cómo lo corrijo?

+2

Está sucediendo porque nada en su código se encarga de que * no * a suceder.. Si estás pensando que configurar una propiedad llamada "base" hace que una clase herede de otra, bueno, eso es incorrecto. – Pointy

+1

Gracias por el formato, @MarkLinus! – corazza

+1

Intenta configurar 'Bla2.prototype' a' new Bla() '. – Pointy

Respuesta

28

Parece que Bla2 no lo heredó de la clase Bla!

Derecha. No ha hecho nada para conectar la herencia, acaba de crear un miembro de Bla2 llamado base que es una instancia de Bla. base no es un identificador especial en JavaScript.

La forma típica de configurar herencia en JavaScript se parece a esto:

// The base constructor function 
function Base(x) { 
    // Base per-instance init 
    this.x = x; 
} 

// An example Base function 
Base.prototype.foo = function() { 
    console.log("I'm Base#foo, x = " + this.x); 
}; 

// The Derived constructor function 
function Derived(x, y) { 
    // Normally you need to call `Base` as it may have per-instance 
    // initialization it needs to do. You need to do it such that 
    // within the call, `this` refers to the current `this`, so you 
    // use `Function#call` or `Function#apply` as appropriate. 
    Base.call(this, x); 

    // Derived per-instance init 
    this.y = y; 
} 

// Make the Derived.prototype be a new object backed up by the 
// Base.prototype.  
Derived.prototype = Object.create(Base.prototype); 

// Fix up the 'constructor' property 
Derived.prototype.constructor = Derived; 

// Add any Derived functions 
Derived.prototype.bar = function() { 
    console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y); 
}; 

... donde Object.create es de ES5, pero es una de las cosas que pueden ser fácilmente calzar en su mayoría. (O puede utilizar una función que sólo hace lo mínimo, sin tratar de hacer todo Object.create, véase más adelante). Y luego lo utiliza:

var d = new Derived(4, 2); 
d.foo(); // "I'm Base#foo, x = 4" 
d.bar(); // "I'm Derived#bar, x = 4, y = 2" 

Live example | source

En código antiguo a veces se ve la Derived.prototype creó esta forma:

Derived.prototype = new Base(); 

... pero hay un problema con hacerlo de esa manera: Base puede hacer la inicialización por instancia que no es apropiado para la totalidad de Derived heredar. Incluso puede requerir argumentos (como lo hace nuestro Base; ¿qué pasaríamos por x?). Al hacer que Derived.prototype sea un nuevo objeto respaldado por Base.prototype, obtenemos las cosas correctas. Luego llamamos al Base desde Derived para obtener init por instancia.

Lo anterior es muy básico y como puede ver implica una serie de pasos. También hace poco o nada para que las "supercartas" sean fáciles y altamente mantenibles. Es por eso que ve tantos scripts de "herencia", como Prototype's Class, Dean Edwards 'Base2 o (tos) mi propio Lineage.


Si no se puede confiar en tener características ES5 en su entorno, y no desea incluir una cuña que hace los fundamentos de Object.create, sólo puede utilizar esta función en su lugar:

function simpleCreate(proto) { 
    function Ctor() { 
    } 
    ctor.prototype = proto; 
    return new Ctor(); 
} 

Entonces, en lugar de

Derived.prototype = Object.create(Base.prototype); 

que haría:

Derived.prototype = simpleCreate(Base.prototype); 

Por supuesto, puede hacer más para automatizar la conexión   —, que es todo Lineage básicamente lo hace.


... y finalmente: Por encima de que he usado para funciones anónimas simplicidad, ej .:

Base.prototype.foo = function() { 
    // ... 
}; 

... pero yo no hago eso en mi código real, porque I like to help my tools help me. Así que tiendo a usar el patrón de módulo alrededor de cada "clase" (función de constructor y prototipo asociado) y uso la función declaraciones (ya que trabajo para la web, e IE7 e IE8 still have problems con expresiones de funciones nombradas. Así que si no fuera así t usando Lineage, lo haría por encima de la siguiente manera:

// Base 
(function(target) { 
    // Base constructor 
    target.Base = Base; 
    function Base(x) { 
     // Base per-instance init 
     this.x = x; 
    } 

    // An example Base function 
    Base.prototype.foo = Base$foo; 
    function Base$foo() { 
     console.log("I'm Base#foo, x = " + this.x); 
    } 
})(window); 

// Derived 
(function(target, base) { 
    // The Derived constructor function 
    target.Derived = Derived; 
    function Derived(x, y) { 
     // Init base 
     base.call(this, x); 

     // Derived per-instance init 
     this.y = y; 
    } 

    // Make the Derived.prototype be a new object backed up by the 
    // Base.prototype.  
    Derived.prototype = Object.create(base.prototype); 

    // Fix up the 'constructor' property 
    Derived.prototype.constructor = Derived; 

    // Add any Derived functions 
    Derived.prototype.bar = Derived$bar; 
    function Derived$bar() { 
     console.log("I'm Derived#bar, x = " + this.x + ", y = " + this.y); 
    } 
})(window, Base); 

... o algo por el estilo Live copy | source

+6

muy bien estructurado, respuesta muy informativa. +1 – Kaii

Cuestiones relacionadas