2011-10-21 19 views
5

Cuando ejecuto este código:herencia JavaScript magia

function foo() { 
    console.log("foo"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
}; 
function bar() { 
}; 
bar.prototype = new foo(); 
var a = new bar(); 
var b = new bar(); 
console.log(a.a()); 
b.a(true); 
console.log(a.a()); 

me sale el siguiente resultado: foo, undefined, true

Lo que no entiendo es cómo el objeto devuelto por la función constructora foo alguna manera se las arregla para asignar una nueva ubicación de memoria para b. Esperaba totalmente que la última salida fuera true porque solo obtengo 1 foo de salida.

Que esto funciona es genial, pero se siente demasiado como magia.

Mi problema realmente aquí es que me gustaría configurar un orden de inicialización (o cadena). Lo que me he dado cuenta es que foo no puede tomar ningún argumento, porque solo se llama una vez, para proporcionar una base (o una especie de plantilla).

me gustaría diseñar un esquema muy simple donde foo tiene parámetros que tiene que ser pasado de bar a foo (porque bar hereda foo). Estoy bien con algo así como llamar al foo.apply(this, arguments) desde bar, pero la llamada para establecer el prototipo debe hacerse sin argumentos, lo que necesito es una forma fluida de diferenciarlos de alguna manera.

Pero realmente no quiero construir todo un servicio en torno a la creación de objetos que permiten algún tipo de herencia, la única cosa que realmente importa es la construcción de los objetos, correctamente ...

+1

Me registro 'foo, undefined, true'. El último '' no definido 'aparece en las herramientas de desarrollo de Chrome, pero ese es solo el valor de retorno de 'console.log'. Solo tienes 3 registros de todos modos. – pimvdb

+0

Sí, es cierto, acabo de copiar de la salida de la consola de Chrome, perdí esa ... –

+0

¿Cuál es exactamente la pregunta? – user123444555621

Respuesta

3

En una nota lateral, a veces está usando self y, a veces usando this ... personalmente, sería más consistente. Me gusta usar that porque this can be confusing y self is part of the BOM, pero es solo una cuestión de gusto.

Trate parasitic inheritance:

function foo(arg) { 
    var that = this; 
    that.a = function() { 
     return arguments.length == 0 ? that.b : (that.b = arguments[0]); 
    }; 
} 

function bar(arg) { 
    return new foo(arg); 
} 

bar.prototype = new foo(); 
var a = new bar(); // the `new` is now optional. Personally I'd remove it... 
var b = bar(); // ... like I did here! 
console.log(a.a()); 
b.a(true); 
console.log(a.a()); 
console.log(b.a()); 

... aquí la herencia se comporta de una forma más clásica, invocando explícitamente un constructor padres cuando se llama al constructor.

+2

Odio "eso", connota algo diferente a esto. Prefiero "instancia" –

+0

@George Jempty - Muy bien. Creo que la parte importante es ser coherente; no use 'this' y luego' self' por ejemplo. –

+0

Estoy de acuerdo con la consistencia. Y sé que Crockford usa "eso", así que estoy un poco desanimado. –

0

Bueno, esto es uno de los puntos débiles (y, sin embargo, puntos fuertes) de JS. Te encuentras con un problema de patrón aquí. Sugiero echar un vistazo a http://ejohn.org/blog/simple-javascript-inheritance/ - hay una 'clase' gratuita que puede usar para ayudar a enderezar lo que uno esperaría con una herencia más 'clásica'.

Solo como un ejemplo rápido, la mayoría de los patrones que he visto tienen un 'objeto' base (que es más o menos una clase personalizada) y el constructor (que es más o menos 'cómo se construye el objeto') algo así como "this.init.apply (this, arguments)". Eso llama a un método 'init' en el objeto con todos los argumentos que se han pasado. Entonces, el init se puede establecer, extender, restablecer, etc., y el constructor siempre llama al que es 'local' para sí mismo con los argumentos que se han pasado. Con buena pinta.

Espero que ayude, o al menos no confunda.

0

La última salida estrue. Estás siendo engañado por tu consola de JavaScript. Ese último undefined es el valor de retorno de su expresión.Haga sus llamadas console.log() más descriptivo para arrojar luz sobre lo que está sucediendo:

function foo() { 
    console.log("foo constructor called"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
} 
function bar() { 
} 
bar.prototype = new foo(); 
var a = new bar(); 
var b = new bar(); 
console.log("value from a.a(): " + a.a()); 
b.a(true); 
console.log("value from a.a(): " + a.a()); 
console.log("done logging"); 

Esto produce la siguiente salida en la consola:

foo constructor called 
value from a.a(): undefined 
value from a.a(): true 
done logging 
undefined 
0

¡No es magia, es herencia de prototipos. Creo que lo que estás tratando de lograr es combinar dos funciones de constructor para que te llamen automágicamente. Probablemente estés mezclando constructores y prototipos.

Pero realmente no quiero construir todo un servicio en torno a la creación de objetos que permiten algún tipo de herencia, la única cosa que realmente tener en cuenta es la construcción de los objetos, correctamente ...

No sé a qué te refieres con "servicio". Pero como en cualquier otro lenguaje de programación orientado a objetos, las llamadas al método de superclase deben realizarse manualmente. Así que, o código el "superclase" de esta manera:

function foo() { 
    console.log("foo"); 
    var self = this; 
    this.a = function() { 
     return arguments.length == 0 ? self.b : (self.b = arguments[0]); 
    }; 
} 
function bar() { 
    foo.apply(this, arguments); // <- ugly 
} 
//bar.prototype = new foo(); // this is not neccessary 

... o se genera un poco de materia envoltorio para conectar sus "clases". Algo como:

function inherit(superconstructor) { 
    return function() { 
     superconstructor.apply(this, arguments); 
    }; 
} 
function foo() {...} 
bar = inherit(foo);