2010-06-30 9 views
14

Estoy escribiendo un juego de plataforma simple usando javascript y html5. Estoy usando javascript de una manera OO. Para que la herencia funcione, estoy usando lo siguiente;Herencia de Javascript - instanceof no funciona?

// http://www.sitepoint.com/blogs/2006/01/17/javascript-inheritance/ 
function copyPrototype(descendant, parent) { 
    var sConstructor = parent.toString(); 
    var aMatch = sConstructor.match(/\s*function (.*)\(/); 
    if (aMatch != null) { descendant.prototype[aMatch[1]] = parent; } 
    for (var m in parent.prototype) { 
     descendant.prototype[m] = parent.prototype[m]; 
    } 
}; 

Por el bien de esta publicación, considere el siguiente ejemplo;

function A() { 
this.Name = 'Class A' 
} 
A.prototype.PrintName = function() { 
alert(this.Name); 
} 

function B() { 
this.A(); 
} 
copyPrototype(B, A); 

function C() { 
this.B(); 
} 
copyPrototype(C, B); 

var instC = new C(); 

if (instC instanceof A) 
    alert ('horray!'); 

como lo entiendo yo esperaría a ver un cuadro de alerta horray, debido a que C es una instancia de C & B & A. ¿Me equivoco? ¿O solo estoy usando el método incorrecto para verificar? ¿O CopyPrototype ha manipulado el operador instanceof?

¡Gracias como siempre por tomarse el tiempo para leer esto!

Shaw.

Respuesta

6

En estos días no debería necesitar .prototype = new Thing(), creo que llego tarde a la fiesta pero puede usar Object.cree en el prototipo del padre y luego anule los métodos que le interesan. Un ejemplo:

var IDataSource = function(){ 
    throw new Error("Not implemented, interface only"); 
}; 

IDataSource.prototype.getData = function(){ 
    throw new Error("Not implemented."); 
}; 

var BasicDataSource = function(){}; 
BasicDataSource.prototype = Object.create(IDataSource.prototype); 
BasicDataSource.prototype.getData = function(){ 
    //[do some stuff, get some real data, return it] 
    return "bds data"; 
}; 

var MockDataSource = function(){}; 
MockDataSource.prototype = Object.create(IDataSource.prototype); 
MockDataSource.prototype.getData = function(){ 
    //[DONT DO some stuff return mock json] 
    return "mds data"; 
}; 

MockDataSource.prototype.getDataTwo = function(){ 
    //[DONT DO some stuff return mock json] 
    return "mds data2"; 
}; 


var MockDataSource2 = function(){}; 
MockDataSource2.prototype = Object.create(MockDataSource.prototype); 




var bds = new BasicDataSource(); 
console.log("bds is NOT MockDataSource:", bds instanceof MockDataSource); 
console.log("bds is BasicDataSource:", bds instanceof BasicDataSource); 
console.log("bds is an IDataSource:", bds instanceof IDataSource); 
console.log("bds Data:", bds.getData()); 


var mds = new MockDataSource(); 
console.log("mds is MockDataSource:", mds instanceof MockDataSource); 
console.log("mds is NOT a BasicDataSource:", mds instanceof BasicDataSource); 
console.log("mds is an IDataSource:", mds instanceof IDataSource); 
console.log("mds Data:", mds.getData()); 
console.log("mds Data2:",mds.getDataTwo()); 


var mds2 = new MockDataSource2(); 
console.log("mds2 is MockDataSource2:", mds2 instanceof MockDataSource2); 
console.log("mds2 is MockDataSource:", mds2 instanceof MockDataSource); 
console.log("mds2 is NOT a BasicDataSource:", mds2 instanceof BasicDataSource); 
console.log("mds2 is an IDataSource:", mds2 instanceof IDataSource); 
console.log("mds2 Data:", mds2.getData()); 
console.log("mds2 Data2:",mds2.getDataTwo()); 

Si ejecuta este código en el nodo obtendrá:

bds is NOT MockDataSource: false 
bds is BasicDataSource: true 
bds is an IDataSource: true 
bds Data: bds data 
mds is MockDataSource: true 
mds is NOT a BasicDataSource: false 
mds is an IDataSource: true 
mds Data: mds data 
mds Data2: mds data2 
mds2 is MockDataSource2: true 
mds2 is MockDataSource: true 
mds2 is NOT a BasicDataSource: false 
mds2 is an IDataSource: true 
mds2 Data: mds data 
mds2 Data2: mds data2 

no hay que preocuparse acerca de los parámetros a constructores o cualquier locura.

+0

Hola j03m, tienes razón, esa sería una gran solución en estos días, pero esta pregunta ya es bastante antigua , y en aquel entonces no había un amplio soporte para object.create, ya que esta era una nueva característica de la secuencia de comandos de ECMA 5 – Shawson

+0

ah note a self, read timestamps. – j03m

+0

¡es bueno tener una respuesta actualizada como referencia! :) – Shawson

16

El problema es que la función copyPrototype sólo copias las propiedades de un prototipo constructores a otro, por ejemplo, al final, la INTENAL [[Prototype]] enlace de C.prototype simplemente apunta a Object.prototype.

La cadena de prototipos de instC y prototipos del constructor tener este aspecto:

 
       [[Prototype]] 
    A.prototype -------------->|-------------------| 
           |     | 
    B.prototype -------------->| Object.prototype | ---> null 
           |     | 
    C.prototype -------------->|-------------------| 
     ^
     | 
     instC 

El operador instanceof atraviesa la cadena de prototipo, el objeto instC, como se puede ver, tendrá en su cadena de prototipo solamente C.prototype y Object.prototype .

Puede lograr lo que quiere mediante la creación de prototipos de su constructor para ser instancias de objetos de sus constructores "padre", por ejemplo:

function A() { 
    this.Name = 'Class A' 
} 

A.prototype.PrintName = function() { 
    alert(this.Name); 
} 

function B() { 
    //.. 
} 
B.prototype = new A(); 
B.prototype.constructor = B; // fix constructor property 


function C() { 
    //.. 
} 

C.prototype = new B(); 
C.prototype.constructor = C; // fix constructor property 

var instC = new C(); 
if (instC instanceof A) 
    alert('horray!'); 

Ahora la cadena de prototipo del objeto instC se ve así:

 
      ---------------  ---------------  --------------- 
instC --> | C.prototype | -----> | B.prototype | -----> | A.prototype | 
      ---------------  ---------------  --------------- 
                   | 
                   V 
                 -------------------- 
                 | Object.prototype | 
                 -------------------- 
                   | 
                   V 
                   null 

artículo Recomendado:

+0

esa forma de herencia es ridículamente fácil de entender, mucho más que la técnica de Crockford ['begetObject()'] (http://javascript.crockford.com/prototypal.html). ¿Hay algún inconveniente en su método o alguna ventaja en 'begetObject()'? ¿Sigue siendo "esto" lo que esperas que sea? ¿Y usas esta técnica tú mismo? – Andrew

+0

sí, esa es una buena solución. He leído muchos enfoques diferentes de OO en JavaScript. Voy a dar esto! Aclamaciones. – Shawson

+0

hmm- el problema con este método, que debería haber señalado realmente en mi ejemplo, es que todos mis constructores realmente aceptan un parámetro (posición) que necesita volver a pasar a través de la cadena. ¿Alguna idea de cómo sería posible usar este método? – Shawson

1

Ok He encontrado una solución que mantiene funcionando la instancia de función, además de permitirme pasar parámetros de constructor a través de la cadena de herencia. La solución se detalla aquí; https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Model - mi estructura de clase ahora se ve así;

function A(p) { 
this.Position = p || new Vector2d(0,0); 
} 

function B(p) { 
this.base = A; 
this.base(p); 
} 
B.prototype = new A; 

function C(p) { 
this.base = B; 
this.base(p); 
} 
C.prototype = new B; 

if(C instanceof A) 
    alert (' it worked!! '); // you now see this alert box! 

Gracias CMS por destacarme por qué esto no funcionaba !!

Puede ver el proyecto completo (y una compilación anterior que, al momento de escribir este documento aún no ha visto este nuevo método OO en su totalidad) hasta http://8weekgame.shawson.co.uk/ - solo echa un vistazo a mis últimas publicaciones.

+0

Hola @shawson, una alternativa en lugar de asignar 'this.base' en cada constructor, si esa propiedad no es útil para ti, puedes invocar directamente al otro constructor, establecer el valor' this' correcto y pasar el argumento 'p' , puede hacerlo utilizando el método ['call'] (https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Function/call), por ejemplo, dentro de' B': 'A.call (esto, p); ' – CMS

+0

Esto no funciona para mí, por ejemplo no se muestra ninguna alerta cuando ejecuto este código. –

+0

Curiosamente, acabo de pegar esto en la consola en cromo y tampoco lo hizo para mí? Usaría el método resig ahora de todos modos http://ejohn.org/blog/simple-javascript-inheritance/ – Shawson