2010-01-21 13 views
5

He estado jugando con herencia de prototipos después de leer http://javascript.crockford.com/prototypal.html y tengo un pequeño problema para entender cómo podría usarlo de la misma forma que usaría la herencia clásica. A saber, todas las funciones y variables heredadas por el prototipo se vuelven esencialmente estáticas a menos que sean sobrescritas por el objeto hijo. Considere la siguiente:Implementando métodos/variables de instancia en la herencia de prototipales

var Depot = { 
    stockpile : [], 
    loadAmmo : function (ammoType) { 
     this.stockpile.push(ammoType); 
    } 
}; 

var MissileDepot = Object.create(Depot); 
var GunDepot = Object.create(Depot); 

stockpile y loadAmmo definitivamente debe estar en el prototipo, ya que ambos MissileDepot y GunDepot los tienen. Entonces corremos:

MissileDepot.loadAmmo("ICBM"); 
MissileDepot.loadAmmo("Photon Torpedo"); 

alert(MissileDepot.stockpile); // outputs "ICBM,Photon Torpedo" 
alert(GunDepot.stockpile); // outputs "ICBM,Photon Torpedo" 

Se espera porque ni tampoco MissileDepotGunDepot en realidad tienen stockpile o loadAmmo en sus objetos, por lo que mira hacia arriba Javascript a la cadena de herencia a su ancestro común.

Por supuesto que podría establecer las existencias de GunDepot manualmente y como se esperaba, el intérprete ya no tiene que buscar la cadena de

GunDepot.stockpile = ["Super Nailgun", "Boomstick"]; 
alert(GunDepot.stockpile); // outputs "Super Nailgun,Boomstick" 

Pero esto no es lo que quiero. Si esto fuera una herencia clásica (digamos Java), loadAmmo operaría en las reservas de MissileDepot y GunDepot de forma independiente, como un método de instancia y una variable de instancia. Me gustaría que mi prototipo declare cosas que son comunes a los niños, no compartidas por ellos.

Así que tal vez estoy malinterpretando por completo los principios de diseño detrás de la herencia prototípica, pero no sé cómo lograr lo que acabo de describir. ¿Algun consejo? ¡Gracias por adelantado!

+0

no debería usar mayúsculas para el nombre de los objetos Depot, MissileDepot, GunDepot. Esto va en contra de las convenciones comunes y dificulta la comprensión. –

+0

Gracias Eric - Lo usé para denotar una clase, como en Ruby/Java/php/whatever. Entonces, para javascript, ¿usamos camel case para TODOS los objetos? ¿Cuál es la regla de oro – ambertch

+0

disculpa por la puntuación, quise decir "¿cuál es la regla de oro?" – ambertch

Respuesta

8

Javascript proporciona una manera de hacer esto de la forma T se utilizan para tratar esta :) :

function Depot() { 
    this.stockpile = [], 
    this.loadAmmo = function (ammoType) { 
     this.stockpile.push(ammoType); 
    } 
}; 

var MissileDepot = new Depot(); 
var GunDepot = new Depot(); 


MissileDepot.loadAmmo("ICBM"); 
MissileDepot.loadAmmo("Photon Torpedo"); 

alert(MissileDepot.stockpile); // outputs "ICBM,Photon Torpedo" 
alert(GunDepot.stockpile); // outputs "" 

Y U puede añadir las funciones de la marcha después:

MissileDepot.blow = function(){alert('kaboom');} 

Extender objeto con otro objeto es también una opción, pero lo que quería es el hecho, que la programación orientado a objetos en javascript se realiza mediante funciones no objetos con {};)

EDITAR :

me siento mal por escrito que sin mencionar: El javascript "nueva" palabra clave es sólo para hacer más fácil a los veteranos Oo. Por favor, profundiza en la herencia prototípica y la creación de objetos dinámicos ya que allí yace la verdadera magia. :)

+0

+1 para 'kaboom' también – KooiInc

+1

'loadAmmo()' debe residir en 'Depot.prototype' ya que se puede compartir entre instancias; De la forma en que lo haces, creas un nuevo objeto de función para cada instancia de 'Depot' – Christoph

+0

No creo que afecte el problema en esta pregunta, pero ES interesante y podría ser útil. ¿Podrías elaborar más? Tal vez un pequeño ejemplo para ver la diferencia? – naugtur

1

Para lograr lo que desea, necesita un método de clonación. No quiere un prototipo de herencia, quiere un prototipo de clonación. Echar un vistazo a uno de los Object.clone() funciones ya implementadas, como uno de los PrototypeJS: http://api.prototypejs.org/language/object.html#clone-class_method

Si usted quiere meter a algún tipo de prototipos, usted tiene que poner en práctica un método initialize() que dará un Almacene la propiedad en sus Depósitos recién creados. Esa es la forma en que se definen las clases prototypejs: un prototipo clonado y un método initialize(): http://prototypejs.org/learn/class-inheritance

3

Para el método, todo funciona como se espera. Son solo los campos de los que debe encargarse.

Lo que veo mucho en YUI es que el constructor asigna los varialbes de instancia. Las 'clases' que heredan de un padre llaman al constructor de su padre.Mira aquí: http://developer.yahoo.com/yui/docs/DataSource.js.html

Ejemplo clase base:

util.DataSourceBase = function(oLiveData, oConfigs) { 
    ... 
    this.liveData = oLiveData; 

    ... more initialization... 
} 

Ejemplo subclase:

util.FunctionDataSource = function(oLiveData, oConfigs) { 
    this.dataType = DS.TYPE_JSFUNCTION; 
    oLiveData = oLiveData || function() {}; 

    util.FunctionDataSource.superclass.constructor.call(this, oLiveData, oConfigs); 
}; 

// FunctionDataSource extends DataSourceBase 
lang.extend(util.FunctionDataSource, util.DataSourceBase, { 
    ...prototype of the subclass... 
}); 
1

Esto se debe a que estamos tratando de hacer un maullido del gato! Douglas Crockford es bueno, pero la secuencia de comandos que está utilizando básicamente funciona al recorrer el objeto principal y copiar todos sus atributos en la cadena del prototipo, que no es lo que quiere. Cuando coloca las cosas en la cadena de prototipos, son compartidas por todas las instancias de ese objeto, ideal para funciones miembro, no ideal para miembros de datos, ya que desea que cada instancia de objeto tenga su propia colección de miembros de datos.

John Resig escribió un small script para simular la herencia clásica. Quizá quieras echarle una ojeada a eso.

+0

¡Este pequeño script parece tan inquietantemente familiar! – Alsciende

0

El secreto de variables de instancia en JavaScript es que se comparten a través de los métodos definidos en superclases o de módulos incluidos. El lenguaje en sí mismo no proporciona esa característica, y puede no ser posible combinar con la herencia de Prototypal porque cada instancia necesitará su propia cápsula de variable de instancia, pero mediante el uso de la disciplina y la convención es bastante sencillo de implementar.

// Class Depot 
function Depot(I) { 
    // JavaScript instance variables 
    I = I || {}; 

    // Initialize default values 
    Object.reverseMerge(I, { 
    stockpile: [] 
    }); 

    return { 
    // Public loadAmmo method 
    loadAmmo: function(ammoType) { 
     I.stockpile.push(ammoType); 
    }, 
    // Public getter for stockpile 
    stockpile: function() { 
     return I.stockpile; 
    } 
    }; 
} 

// Create a couple of Depot instances 
var missileDepot = Depot(); 
var gunDepot = Depot(); 

missileDepot.loadAmmo("ICBM"); 
missileDepot.loadAmmo("Photon Torpedo"); 

alert(missileDepot.stockpile()); // outputs "ICBM,Photon Torpedo" 
alert(gunDepot.stockpile()); // outputs "" 

// Class NonWeaponDepot 
function NonWeaponDepot(I) { 
    I = I || {}; 

    // Private method 
    function nonWeapon(ammoType) { 
    // returns true or false based on ammoType 
    } 

    // Make NonWeaponDepot a subclass of Depot and inherit it's methods 
    // Note how we pass in `I` to have shared instance variables 
    return Object.extend(Depot(I), { 
    loadAmmo: function(ammoType) { 
     if(nonWeapon(ammoType)) { 
     // Here I.stockpile is the same reference an in the Depot superclass 
     I.stockpile.push(ammoType); 
     } 
    } 
    }); 
} 

var nonWeaponDepot = NonWeaponDepot(); 
nonWeaponDepot.loadAmmo("Nuclear Bombs"); 

alert(nonWeaponDepot.stockpile()); // outputs "" 

Y eso es cómo hacer las variables de instancia en JavaScript. Another instance variable example using the same technique.

+0

Gracias Daniel - ¿Qué es "Object.reverseMerge"? No se pudo encontrar en el navegador, ¿es un método de biblioteca? – ambertch

+0

O nm, véalo en su enlace – ambertch