2012-09-11 51 views
7

si tengo un objeto de JavaScript definida como:¿Cómo puedo contar las instancias de un objeto?

function MyObj(){}; 

MyObj.prototype.showAlert = function(){ 
    alert("This is an alert"); 
    return; 
}; 

Ahora un usuario puede llamar como:

Hasta aquí todo bien, y también se puede en el mismo código de ejecutar otra instancia de esto:

var b = new MyObj(); 
b.showAlert(); 

Ahora quiero saber, ¿cómo puedo mantener el número de instancias MyObj? ¿hay alguna función incorporada?

Una forma que tengo en mente es incrementar una variable global cuando se inicializa MyObj y esa será la única manera de realizar un seguimiento de este contador, pero ¿hay algo mejor que esta idea?

EDIT:

Tener un vistazo a esto como sugerencia aquí:

enter image description here

Me refiero a cómo puedo hacerlo volver a 2 en lugar de 3

+1

¿Qué ocurre cuando un objeto sale del ámbito? –

+2

No agregue una variable global como contador, agregue una propiedad a 'MyObj.prototype' o' MyObj', o agregue una variable privada a través de un cierre. (Ninguno de los cuales resuelve el problema mencionado por Lee Taylor). – nnnnnn

Respuesta

13

Hay nada incorporado; sin embargo, puede hacer que su función constructora cuente cuántas veces ha sido llamada. Desafortunadamente, el lenguaje de JavaScript no proporciona una forma de saber cuándo un objeto se ha salido del alcance o se ha recolectado basura, por lo que su contador solo subirá, nunca bajará.

Por ejemplo:

function MyObj() { 
    MyObj.numInstances = (MyObj.numInstances || 0) + 1; 
} 
new MyObj(); 
new MyObj(); 
MyObj.numInstances; // => 2 

Por supuesto, si se quiere evitar la manipulación indebida de la cuenta, entonces debería ocultar el contador a través de un cierre y proporcionar una función de acceso a leerlo.

[Editar]

por su pregunta actualizada - no hay manera de hacer un seguimiento de cuándo casos ya no son utilizados o "eliminado" (por ejemplo, mediante la asignación de nula a una variable) porque JavaScript no proporciona finalizer methods para objetos.

lo mejor que podría hacer es crear un método de "disponer" que los objetos se llaman cuando ya no son activos (por ejemplo, mediante un esquema de reference counting) pero esto requiere la cooperación del programador - el lenguaje proporciona ninguna ayuda:

function MyObj() { 
    MyObj.numInstances = (MyObj.numInstances || 0) + 1; 
} 
MyObj.prototype.dispose = function() { 
    return MyObj.numInstances -= 1; 
}; 
MyObj.numInstances; // => 0 
var a = new MyObj(); 
MyObj.numInstances; // => 1 
var b = new MyObj(); 
MyObj.numInstances; // => 2 
a.dispose(); // 1 OK: lower the count. 
a = null; 
MyObj.numInstances; // => 1 
b = null; // ERR: didn't call "dispose"! 
MyObj.numInstances; // => 1 
+0

Pero: 'var x = new MyObj(); x = nulo; alert (MyObj.numInstances); ' – nnnnnn

+0

@nnnnnn: a la derecha, esta estrategia simple no rastrea el ciclo de vida de las instancias de objetos, por lo que el contador nunca se reduce, incluso cuando los objetos son elegibles para la recolección de basura. (Tenga en cuenta que mi propio ejemplo demuestra que las dos instancias anónimas aún se cuentan aunque no sean accesibles). – maerics

+0

Si agrega una pequeña nota de que es un número de instancias * creadas *, entonces nadie trataría de encontrar inconvenientes ;-) – zerkms

3

Cree una propiedad estática en el constructor MyObj llamada say count e increméntela dentro del propio constructor.

function MyObj() { 
    MyObj.count++; 
} 

MyObj.count = 0; 

var a = new MyObj; 
var b = new MyObj; 

alert(MyObj.count); 

Esta es la forma en que normalmente lo haría en Java (usando una propiedad estática).

+1

No estoy seguro si este es un método muy confiable. usted supone que MyObj es accesible desde el espacio global. Esto puede no ser siempre el caso. – webduvet

+0

No entiendo lo que quieres decir. ¿Qué tiene el alcance algo que ver con esta solución? –

0

Finalmente, JS tendrá capacidad de proxy incorporada, que tendrá acceso de bajo nivel a todo tipo de cosas que suceden en segundo plano, que nunca estarán expuestas a los desarrolladores de aplicaciones (excepto a través del proxy - pensar métodos mágicos en lenguajes como PHP).

En ese momento, escribir un método destructor en su objeto, que decrementa el contador, podría ser completamente trivial, siempre y cuando el soporte para la destrucción/recolección de basura como activador esté 100% garantizado en todas las plataformas.

La única manera de momento, de forma fiable qué podría ser algo así como la creación de un registro cerrado de todas las instancias creadas, y luego manualmente destructing ellos (de lo contrario, nunca será recolección de basura).

var Obj = (function() { 
    var stack = [], 

     removeFromStack = function (obj) { 
      stack.forEach(function (o, i, arr) { 
       if (obj === o) { arr.splice(i, 1); } 
       makeObj.count -= 1; 
      }); 
     }; 

    function makeObj (name) { 
     this.sayName = function() { console.log("My name is " + this.name); } 
     this.name = name; 
     this.explode = function() { removeFromStack(this); }; 
     stack.push(this); 
     makeObj.count += 1; 
    } 

    makeObj.checkInstances = function() { return stack.length; }; 
    makeObj.count = 0; 
    return makeObj; 

}()); 


// usage: 

var a = new Obj("Dave"), 
    b = new Obj("Bob"), 
    c = new Obj("Doug"); 

Obj.count; // 3 

// "Dave? Dave's not here, man..." 
a.explode(); 

Obj.count; // 2 
a = null; // not 100% necessary, if you're never going to call 'a', ever again 
      // but you MUST call explode if you ever want it to leave the page's memory 
      // the horrors of memory-management, all over again 

¿Este patrón hará lo que usted quiere que haga? Mientras:

  1. no se presenta a en otra cosa
  2. no sobrescribir su método explode
  3. que no se metan con Obj de ninguna manera
  4. usted don' t esperar que cualquier método prototype tenga acceso a cualquiera de las variables internas

... entonces sí, este método funcionará bien para hav el mostrador funciona correctamente. Incluso se podría escribir un método general convocada recycle, que llama al método de cualquier objeto que se pasa explode (siempre y cuando su constructor, o de la fábrica, con el apoyo tal cosa).

function recycle (obj) { 
    var key; 
    obj.explode(); 
    for (key in obj) { if (obj.hasOwnProperty(key)) { delete obj[key]; } } 
    if (obj.__proto__) { obj.__proto__ = null; } 
} 

Nota: esto no eliminará el objeto. Lo habrá eliminado del cierre y eliminado todos los métodos/propiedades que alguna vez tuvo.

Así que ahora es una cáscara vacía, que puede reutilizar, establecer expresamente en null después de reciclar sus partes, o dejar que se recoja y olvidarse de ella, sabiendo que ha eliminado las referencias necesarias.

¿Le resultó útil? Probablemente no.

La única vez que realmente veo esto como de utilidad sería en un juego donde tu personaje solo podría disparar 3 balas a la vez, y no puede disparar un 4 hasta que aparezca el 1er en la pantalla alguien o se sale del límite (así es como, digamos, Contra trabajó, en el día).

También podría cambiar una viñeta "desaparecida" de la pila y reutilizar esa bala para cualquier jugador/enemigo restableciendo su trayectoria, restableciendo las banderas correspondientes y empujándola nuevamente a la pila.

Pero, de nuevo, hasta que los proxies nos permitan definir los métodos constructor/destructor "mágico", que se respetan a bajo nivel, esto solo es útil si se va a microgenerizar la creación y destrucción de todos los suyos propios objetos (realmente no es una buena idea).

+0

¿Por qué mantener la pila? Simplemente podría decrementar el contador –

1

¿qué tal tal método?

var Greeter = (function() 
{ 
    var numInstances; 

    function Greeter(message) 
    { 
     numInstances = (numInstances || 0) + 1; 
     this.greeting = message; 
    } 

    Greeter.prototype.greet = function() 
    { 
     return "Hello, " + this.greeting; 
    }; 

    Greeter.prototype.getCounter = function() 
    { 
     return numInstances; 
    }; 

    return Greeter; 

})(); 

var greeter = new Greeter("world"); 
greeter.greet(); 
greeter.getCounter(); 

var newgreeter = new Greeter("new world"); 
newgreeter.greet(); 
newgreeter.getCounter(); 

greeter.getCounter();   
2
var User = (function() { 
    var id = 0; 
    return function User(name) { 
     this.name = name; 
     this.id = ++id; 
    } 
})(); 

User.prototype.getName = function() { 
    return this.name; 
} 

var a = new User('Ignacio'); 
var b = new User('foo bar'); 

a 
User {name: "Ignacio", id: 1} 
b 
User {name: "foo bar", id: 2} 
0

Mi solución es la creación de una instancia de almacenamiento de objetos contar y una función para aumentar en prototipo.

function Person() { 
    this.countInst(); 
} 

Person.prototype = { 
    constructor: Person, 
    static: { 
    count: 0 
    }, 
    countInst: function() { 
    this.static.count += 1; 
    } 
}; 

var i; 

for (i = 0; i < 10; i++) { 
    var p = new Person(); 
    document.write('Instance count: '); 
    document.write(p.static.count); 
    document.write('<br />'); 
} 

Aquí es mi plunker: https://plnkr.co/edit/hPtIR2MQnV08L9o1oyY9?p=preview

Cuestiones relacionadas