2009-05-31 21 views
8

Sé que hay muchas preguntas similares que son toneladas de excelentes respuestas a esto. Traté de ver los métodos de herencia clásicos, o los métodos de cierre, etc. De alguna manera, considero que son métodos más o menos "piratas" para mí, ya que no es realmente lo que el javascript está diseñado para hacer. (Bienvenido, alguien me corrige si estoy equivocado). bien, siempre y cuando funciona, me satisface con el patrón de herencia clásica como:javascript inheritance

PARENTClass = function (basevar) { do something here; }; 
PARENTClass.prototype = { a: b, c: d}; // prototype is auto gen 

// Inheritance goes here 
CHILDClass = function (childvar) { do something; }; 
CHILDClass.prototype = new PARENTClass(*1); // Actual inheritance to the prototype statement 
// Instance 
CHILDInstance = new CHILDClass(whatever); 

Por encima es de alguna manera, a mi entender la herencia de JS. Pero un escenario que no tengo idea de cómo implementar, ¿es eso si quiero hacer algo de inicialización DURANTE la creación de objetos (es decir, dentro del constructor), y el nuevo objeto puede usarse de inmediato ... Mi ilustración del problema podría no ser demasiado clara, por lo que me permitió usar el siguiente C# Psuedo para explicar lo que quiero hacer: (. init elementos de la IU)

class PARENT { 
    public PARENT (basevar) { ... } 
} 
class CHILD : PARENT { 
    public CHILD (basevar) : PARENT (basevar) // constructor of child, and call parent constructor during construct. 
    { ... } 
} 

Por alguna razón, poniéndolos en el constructor parece la mejor manera de hacerlo. Alguien tiene idea de cómo puedo hacerlo.

PD: en el * 1, no tengo idea de lo que debería poner allí. PS2: La situación anterior que Hice encontrar que la biblioteca jquery.inherit puede hacer, solo me pregunto si no usar la biblioteca puede lograrlo. PS3: O mi comprensión es incorrecta. Como javascript no pretende imitar el OOP (por eso lo llamo hack), ¿cuál es la lógica "CORRECTA" para implementar esto?

+0

mira esto: http://stackoverflow.com/questions/7486825/javascript-inheritance/12816953#12816953 –

+0

Uso Object.create() para heredar el prototipo de la clase base. De esa forma, los cambios en la clase base se reducen a la subclase, pero no al revés. Más detalles en mi blog: http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

Respuesta

17

No es un truco como tal; JavaScript es un lenguaje de prototipos, tal como se define por Wikipedia como donde:

..classes no están presentes, y la reutilización comportamiento (conocido como herencia en lenguajes basados ​​en clases) se realiza a través de un proceso de clonación de objetos existentes que sirven como prototipos.

Como dice, las clases no se usan en JavaScript; cada objeto que cree se desciende de JavaScript Object; todos los objetos en JavaScript tienen el objeto prototype y todas las instancias de objetos crean "heredarán" métodos y propiedades del objeto prototipo de su objeto. Eche un vistazo a la referencia de objeto MDC prototype para obtener más información.

A partir de esto, cuando se llama a la línea:

CHILDClass.prototype = new PARENTClass(); 

Esto permite que el objeto CHILDClass para agregar métodos y propiedades a su objeto prototipo del objeto PARENTClass, lo que crea un efecto similar a la idea de la herencia presente en los idiomas basados ​​en la clase. Dado que el objeto prototype afecta a todas las instancias creadas de ese objeto, esto permite que los métodos y propiedades del objeto principal estén presentes en cada instancia de su objeto hijo.

Si desea llamar al constructor de su clase principal en el constructor de su clase secundaria, use la función JavaScript call; esto le permite llamar al constructor de la clase padre en el contexto del constructor de la clase hija, por lo tanto, estableciendo las propiedades recién creadas en su clase secundaria a lo que están configuradas como clase primaria.

Tampoco necesita colocar nada donde haya especificado *1, ya que esa línea se usa meramente para agregar los métodos y propiedades al objeto prototipo de la clase secundaria; sin embargo, tenga en cuenta que llama al constructor de la clase padre, por lo que si hay argumentos que son fundamentales en la operación del constructor de la clase padre, debe verificar que estén presentes para evitar errores de JavaScript.

+0

Respuesta agradable y completa. Solo una cosa más para agregar ... En caso de que, por ejemplo, cuando se llama al constructor del elemento principal, se ejecutará algo que puede ser costoso (como leer desde XML) o tener en cuenta al usuario (como llamar al alerta). Incluso no puse nada en el * 1, que básicamente todavía sería ejecutado; A menos que explícitamente ponga check en el padre (por ejemplo, if (arguments.length == 0) return;). ¿Es una solución "adecuada" para esto? Siento que provengo de un fondo OOP estrictamente estricto, todavía no me acostumbro a la lógica de programación orientada js. – xandy

+0

Sí, así es como lo haría: si alguien crea una instancia de su objeto que requiere los argumentos, entonces no desea realizar esas acciones de todos modos. –

10

Puede invocar manualmente el constructor de los padres en el constructor de la subclase de esta manera:

CHILDClass = function (basevar) { 
    PARENTClass.call(this, basevar); 
    // do something; 
}; 

El truco aquí es utilizar el método call, que le permite invocar un método en el contexto de un objeto diferente. Ver the documentation of call para más detalles.

+0

He buscado esta funcionalidad en todas partes. Nunca lo he visto tan simple. Mi pregunta es, ¿es esta una forma "aceptable" de hacerlo? Como en, ¿esto se consideraría un truco? Además, ¿esto es actual, como no desaprobado como '__proto__'? –

1

JavaScript no tiene soporte integrado para jerarquías de herencia ya que se supone que la extensión de tipo se realiza a través de la agregación, es decir, agregando la funcionalidad deseada directamente al objeto o su prototipo si la propiedad se va a compartir entre instancias.

Sin embargo, JS es lo suficientemente potente como para posibilitar la implementación de otras formas de construcción de objetos, incluida la herencia clásica.

Dado un clone function - que es suficiente para añadir herencia 'verdadero' prototípico, y no bastardization de JavaScript del mismo - su exampe se puede implementar como esto:

function ParentClass(baseVar) { 
    // do stuff 
} 

// don't overwrite the prototype object if you want to keep `constructor` 
// see http://joost.zeekat.nl/constructors-considered-mildly-confusing.html 
ParentClass.prototype.a = 'b'; 
ParentClass.prototype.c = 'd'; 

function ChildClass(childVar) { 
    // call the super constructor 
    ParentClass.call(this, childVar); 
} 

// don't inherit from a ParentClass instance, but the actual prototype 
ChildClass.prototype = clone(ParentClass.prototype); 
ChildClass.prototype.e = 'f'; 

También es posible añadir un poco de azúcar sintáctica para la clase herencia basada en mi propia implementación can be found here.

El ejemplo anterior se leería entonces

var ParentClass = Class.extend({ 
    constructor: function(baseVar) { 
     // do stuff 
    }, 
    a: 'b', 
    c: 'd' 
}); 

var ChildClass = ParentClass.extend({ 
    e: 'f' 
}); 
1

Tengo un envoltorio Javascript programación orientada a objetos que proporciona la herencia 'Clase-como' donde se pueden reemplazar los métodos de base o llame constructores de base o miembros de peso ligero.

a definir sus clases como esto:

//Define the 'Cat' class 
function Cat(catType, firstName, lastName) 
{ 
    //Call the 'Animal' constructor. 
    Cat.$baseNew.call(this, firstName, lastName); 

    this.catType = catType; 
} 
//Extend Animal, and Register the 'Cat' type. 
Cat.extend(Animal, { type: 'Cat' }, { 
    hello: function(text) 
    { 
     return "meaoow: " + text; 
    }, 
    getFullName: function() 
    { 
     //Call the base 'Animal' getFullName method. 
     return this.catType + ": " + Cat.$base.getFullName.call(this); 
    } 
}) 

//It has a built-in type system that lets you do stuff like: 

var cat = new Cat("ginger", "kitty", "kat"); 
Cat.getType()      // "Cat" 
cat.getBaseTypesAndSelf()   // ["Cat","Animal","Class"] 
cat.getType()      // "Cat" 
cat.isTypeOf(Animal.getType()) // "True" 

var dynamicCat = Class.createNew("Cat", ["tab","fat","cat"]) 
dynamicCat.getBaseTypesAndSelf() // ["Cat","Animal","Class"] 
dynamicCat.getFullName()   // tab: fat cat 

código fuente disponible en: Class.js

También tengo más detalles en mi blog acerca de OOP in javascript

1

Sólo quería mencionar algunos de los problemas con el patrón clásico que está buscando:

  1. Los valores de referencia de la (s) superclase (s) estarán disponibles como esencialmente estáticos en TODAS las instancias. Por ejemplo, si tiene var arr = [1,2,3] en el super, y hace instance_1.arr.push (4) instance_2.arr.push (5) TODAS estas instancias "verán" los cambios.
  2. Así que resuelve 1. con la solución de Ayman que Zakas llama "Constructor Robo", pero ahora llama al constructor dos veces: una para su prototipo y otra para el robo del constructor. Solución: para su prototipo use un helper como inheritPrototype (mostré toda la implementación de esto en esta publicación: inheritPrototype method FWIW, esto esencialmente proviene de una combinación de la página 181 del libro de Zakas y algunos estudios de Crockford.

  3. No hay privacidad (pero, de nuevo, que había necesidad de usar algo como el patrón del objeto duradero para conseguir esto y que puede no ser lo que quiere) definición

  4. objeto se deja "colgado": Solución - ponga una declaración if que verifique cualquiera de las funciones de su prototipo y luego defina el prototipo con un prototipo literal.

Tengo ejemplos de todo esto en github !!!

Ha sido un gran desafío para mí asimilar realmente ambos: libros de Zakas y Crockford sobre creación de objetos y herencia. También necesitaba probar algunos marcos de JavaScript TDD diferentes. Así que decidí crear un ensayo sobre Frameworks TDD y JavaScript Object Creation & Herencia. ¡Tiene código de ejecución y pruebas jspec! Aquí está el enlace: * My GitHub Open Source Essay/Book