2008-11-04 8 views
17

Tengo un problema intentando que las variables de clase funcionen en javascript.Variables de clase en Javascript

Pensé que entendía el prototipo de modelo de herencia, pero obviamente no. Supuse que como los prototipos se compartirían entre los objetos, también lo harían sus variables.

Es por eso que este código me confunde.

¿Cuál es la forma correcta de implementar variables de clase?

function classA() {}; 

classA.prototype.shared = 0; 

a = new classA; 

//print both values to make sure that they are the same 
classA.prototype.shared; 
a.shared; 

//increment class variable 
classA.prototype.shared++; 

//Verify that they are each 1 (Works) 
classA.prototype.shared; 
a.shared; 

//now increment the other reference 
a.shared++; 

//Verify that they are each 2 (Doesn't Work) 
classA.prototype.shared; 
a.shared; 

ACTUALIZACIÓN: por lo que parece que todo el mundo está confirmando el hecho de que mediante el incremento de la variable de la instancia que no afectan al prototipo. Esto está bien, esto es lo que he documentado en mi ejemplo, pero ¿no parece esto un error en el diseño del lenguaje? ¿Por qué sería este comportamiento deseable? Me parece extraño que cuando la var de la instancia no está definida seguimos el enlace oculto al prototipo donde obtenemos el valor de la var, pero lo copiamos en el objeto instancia.

También entiendo que esto no es java/C++/ruby ​​/ python, es un idioma diferente. Solo tengo curiosidad sobre por qué este comportamiento podría ser bueno.

+0

Le sugiero que lea esto: http://www.3site.eu/doc/ – kentaromiura

Respuesta

8
I assumed that since prototypes will be shared between objects then so will their variables. 

Son, pero esto:

a.shared++ 

no está haciendo lo que creo que está haciendo. Es, de hecho, la sintaxis de azúcar (aproximado):

(a.shared= a.shared+1)-1 

Así que (el -1 siendo para devolver el valor de incremento previo, no es que en realidad estás usando el valor retrun, pero aún así.) esto en realidad está haciendo una asignación a .shared. Cuando asigna a un miembro de instancia, siempre escribe a los miembros de esa instancia, , no a, tocando los miembros de cualquiera de sus prototipos. Es lo mismo que decir:

classA.prototype.shared= 1; 
a.shared= 2; 

Así que su nueva a.shared oculta la prototype.shared sin alterarla. Otras instancias de la clase A continuarían mostrando el valor del prototipo 1. Si eliminaba una.shared, podría volver a ver la variable del prototipo que estaba oculta detrás de ella.

+0

¿Por qué javascript permitiría esto? ¿No lo ves como un error en el diseño del idioma? Quiero decir, cuando incrementamos una instancia, cuya variable existe en el prototipo, primero tenemos que obtener el valor de vars en el prototipo, pero extrañamente, ¿le asignamos el valor a la instancia? EXTRAÑO. – esiegel

+0

Es un poco raro, sí, ¡pero está lejos de ser lo más extraño en JavaScript! Es un lenguaje lleno de trampas. – bobince

+0

* Cuando asigna a un miembro de instancia siempre escribe a los miembros de esa instancia, sin tocar ningún miembro de ninguno de sus prototipos. * Eso era lo que necesitaba saber, gracias. –

1

Si crea una instancia de esa clase (a = new classA), la modificación de la instancia a no cambiará la clase base. Las instancias de classA heredarán todo de classA.prototype, pero eso no se aplica al revés, al cambiar a no cambiará classA.
Si tiene dos instancias como y a2 = new classA, puede realizar cambios en a1 y a2 sin afectar a la otra. Cambiando classA.prototype por otro lado será visible en ambos.
La variable shared de la instancia a tendrá el valor predeterminado hasta que se le dé un nuevo valor. El valor predeterminado es el valor de classA.prototype.shared.

+0

Esto realmente no tiene sentido para mí. Al principio parece que "a" busca la variable compartida, no la tiene y la encuentra en el prototipo. Pero luego en realidad contiene una copia de la variable. Quiero decir, después de ejecutar este código anterior, al incrementar classA.shared, "a" no se ve afectado – esiegel

+0

Además, no es solo que "la instancia de classA heredará todo de classA.prototype". En realidad, está vinculado al principio, como se puede ver cuando incremente los prototipos.share y esto también incrementó el a.shared. – esiegel

+0

Acabo de agregar un tercer párrafo que describe que a.shared tendrá un valor predeterminado de classA.prototype.shared hasta que se le dé un nuevo valor. – Chei

3

Usted acaba de poner el miembro de la derecha en la "clase", que en JavaScript es la función que construye objetos:

function ClassA(x) { this.x = x; } 
ClassA.shared = ""; 
ClassA.prototype.foo = function() { 
    return ClassA.shared + this.x; 
} 

var inst1 = new ClassA("world"); 
var inst2 = new ClassA("mars"); 

ClassA.shared = "Hello "; 
console.log(inst1.foo()); 
console.log(inst2.foo()); 
ClassA.shared = "Good bye "; 
console.log(inst1.foo()); 
console.log(inst2.foo()); 
+0

Esto no funciona. Intente instanciar una instancia a = new classA. Esta variable no tendrá acceso a un miembro compartido. – esiegel

+0

No, porque no es un miembro de la clase, es una propiedad del objeto ClassA en sí. – roryf

+1

¿Por qué se rechaza esto? Es una de las respuestas que da la solución correcta. –

5

Si usted quiere tener una variable de clase, algo así como una variable estática en Java, a continuación, puede declarar una variable en la clase principal, pero luego no debe acceder a ella como una variable de los objetos secundarios. This article tiene un buen ejemplo de la clase Circle que tiene la variable Circle.PI = 3.14, mientras que todas las instancias de Circle tienen acceso como Circle.PI (en lugar de c.PI).

Así que mi respuesta es que si usted quiere tener una clase variable de shared en classA entonces te declare la variable compartida en classA, y más tarde se debe utilizar en lugar de classA.shareda.shared. Cambiar a.shared nunca resultará en el cambio de classA.shared.

15

estática (nivel de clase) las variables se puede hacer como esto:

function classA(){ 
    //initialize 
} 

classA.prototype.method1 = function(){ 
    //accessible from anywhere 
    classA.static_var = 1; 
    //accessible only from THIS object 
    this.instance_var = 2; 
} 

classA.static_var = 1; //This is the same variable that is accessed in method1() 

Su salida parece extraño debido a la forma javascript se encarga de prototipos.Llamar a cualquier método/recuperación de una variable de un objeto instanciado comprueba primero la instancia, LUEGO el prototipo. es decir,

var a = new classA(); 
classA.prototype.stat = 1; 

// checks a.stat which is undefined, then checks classA.prototype.stat which has a value 
alert(a.stat); // (a.stat = undefined, a.prototype.stat = 1) 

// after this a.stat will not check the prototype because it is defined in the object. 
a.stat = 5; // (a.stat = 5, a.prototype.stat = 1) 

// this is essentially a.stat = a.stat + 1; 
a.stat++; // (a.stat = 6, a.prototype.stat = 1) 
+0

¿Has ejecutado tu primer ejemplo? ¡Causa classA.static_var al final no es 1 sino indefinido! – momomo

+0

@Hamidam classA.static_var se establece en 1 después de decir 'a = new classA(); a.method1(); ' –

2

Incrementar la propiedad shared a través de la instancia hace que sea una propiedad de esa instancia, por lo que usted está viendo este comportamiento.

Una vez que haya hecho eso, nunca tendrá acceso al prototipo de esa propiedad a través de la instancia, sino que es de su propiedad.

>>> function ConstructorA() {}; 
>>> ConstructorA.prototype.shared = 0; 
>>> var a = new ConstructorA(); 
>>> ConstructorA.prototype.shared++; 
>>> a.shared 
1 
>>> a.hasOwnProperty("shared") 
false 
>>> a.shared++; 
>>> a.hasOwnProperty("shared") 
true 

Esta es la razón por la solución correcta es utilizar ConstructorA.shared, como se sugiere en muchas de las respuestas hasta el momento, y siempre acceder a él a través de la función constructora, no una instancia.

Podría ser útil considerar que no existe una clase en JavaScript. Las "instancias" creadas con el operador new son solo objetos que han sido creados por una función de constructor particular y tienen una cadena de prototipos particular. Esta es la razón por la cual a.shared no podrá acceder al ConstructorA.shared - el acceso a propiedades implica mirar el objeto en cuestión para la propiedad indicada y, en su defecto, recorrer su cadena de prototipos buscando la propiedad, pero la función del constructor que creó el objeto no es t parte de la cadena de prototipos.

0

Lo que está definiendo no es una variable de clase, es un valor predeterminado para una variable de instancia.

Las variables de clase se deben definir directamente en la clase, lo que significa directamente en la función constrctor.

function ClassA() 
{ 
    ClassA.countInstances = (ClassA.countInstances || 0) + 1; 
} 
var a1 = new ClassA(); 
alert(ClassA.countInstances); 
var a2 = new ClassA(); 
alert(ClassA.countInstances); 

Cuando está declarando una variable en el prototipo, esta variable será heredado por todas las instancias como variables de instancia (al igual que los métodos) y tendrá VE anulado si lo cambia en el caso (al igual que los métodos).

1

Es porque los prototipos no son definiciones de clase. Las variables prototípicas no son variables estáticas. Piensa en la palabra prototipo. No es un modelo utilizado para construir un objeto; es un objeto de ejemplo para ser duplicado.

Cuestiones relacionadas