如何计算对象的实例?

时间:2012-09-11 22:35:05

标签: javascript

如果我将Javascript对象定义为:

function MyObj(){};

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

现在用户可以将其称为:

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

到目前为止一直很好,也可以在同一个代码中运行另一个这样的实例:

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

现在我想知道,我怎么能保持MyObj实例的数量? 是否有一些内置函数?

我想到的一种方法是在MyObj初始化时增加一个全局变量,这将是跟踪这个计数器的唯一方法,但是有什么比这个想法更好吗?

修改

在这里看一下这个建议:

enter image description here

我的意思是我怎样才能让它回到2而不是3

9 个答案:

答案 0 :(得分:16)

没有任何内置的东西;但是,你可以让你的构造函数保持计数它被调用的次数。不幸的是,JavaScript语言无法判断对象何时超出范围或被垃圾收集,因此您的计数器只会上升,永不停机。

例如:

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

当然,如果你想防止篡改计数,那么你应该通过一个闭包隐藏计数器,并提供一个访问函数来读取它。

<强> [编辑]

根据您更新的问题 - 没有办法跟踪何时不再使用或“删除”实例(例如,通过为变量赋值null),因为JavaScript不为对象提供finalizer methods

你能做的最好的事情就是创建一个“dispose”方法,当对象不再活动时会调用它们(例如通过reference counting方案),但这需要程序员的合作 - 语言不提供任何帮助:

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

答案 1 :(得分:3)

在名为MyObj的{​​{1}}构造函数上创建一个静态属性,并在构造函数本身内递增它。

count

这就像你通常用Java那样(使用静态属性)。

答案 2 :(得分:3)

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}

答案 3 :(得分:2)

使用ES6 Classes MDN语法-我们可以定义static方法:

static关键字为定义了静态方法。静态方法是在不实例化其类的情况下调用的,并且不能通过类实例进行调用。静态方法通常用于为应用程序创建实用程序功能。

class Item {

  static currentId = 0;    
  _id = ++Item.currentId;  // Set Instance's this._id to incremented class's ID
                           // PS: The above line is same as:
                           // constructor () { this._id = ++Item.currentId; }
  get id() {               
    return this._id;       // Getter for the instance's this._id
  }
}

const A = new Item(); // Create instance (Item.currentId is now 1)
const B = new Item(); // Create instance (Item.currentId is now 2)
const C = new Item(); // Create instance (Item.currentId is now 3)

console.log(A.id, B.id, C.id);                     // 1 2 3
console.log(`Currently at: ${ Item.currentId }`);  // Currently at: 3

PS:如果您不想登录-公开内部currentId属性,请将其设为 private

static #currentId = 0; 
_id = ++Item.#currentId;

下面是一个带有constructor而没有getter的示例:

class Item {

  static id = 0;
  
  constructor () {
    this.id = ++Item.id;
  }

  getID() {               
    console.log(this.id);
  }
}

const A = new Item(); // Create instance (Item.id is now 1)
const B = new Item(); // Create instance (Item.id is now 2)
const C = new Item(); // Create instance (Item.id is now 3)

A.getID();   B.getID();   C.getID();       // 1; 2; 3
console.log(`Currently at: ${ Item.id }`); // Currently at: 3

答案 4 :(得分:1)

这种方法怎么样?

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();           

答案 5 :(得分:1)

保留全局计数变量并每次递增都是一种选择。另一个选择是手动创建每个实例后调用counter方法(这是我能想到的最糟糕的事情)。但是还有另一个更好的解决方案。
每次创建实例时,都会调用构造函数。问题在于正在为每个实例创建构造函数,但是我们可以在count中拥有一个__proto__属性,每个实例都可以相同。

function MyObj(){
    MyObj.prototype.addCount();
};

MyObj.prototype.count = 0;

MyObj.prototype.addCount = function() {
    this.count++;
};

var a = new MyObj();
var b = new MyObj();

这毕竟是我们的ab变量:

enter image description here

答案 6 :(得分:0)

最终,JS将具有内置代理功能,该功能将对在后台发生的各种事情进行低级访问,永远不会向前端开发人员公开(除了通过代理 - - 用PHP等语言思考魔术方法。

那时,在你的对象上写一个析构函数方法,这会减少计数器,这可能完全是微不足道的,只要在整个平台上100%保证支持销毁/垃圾收集作为触发器。

当前,可靠地执行此操作的唯一方法可能是创建所有已创建实例的封闭式注册表,然后手动对其进行破坏(否则,它们将永远不会被垃圾回收)。< / p>

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

这种模式会做你想做的吗? 只要:

  1. 您没有将a转换为其他内容
  2. 您不会覆盖其explode方法
  3. 你不会以任何方式混淆Obj
  4. 您不希望任何prototype方法访问任何内部变量
  5. ...然后是的,这个方法适用于让计数器正常工作。 你甚至可以编写一个名为recycle的通用方法,它调用你传递它的任何对象的explode方法(只要它的构造函数或工厂支持这样的东西)。

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

    注意 - 这实际上不会消除该对象。 你只需将它从闭包中删除,并删除曾经拥有的所有方法/属性。

    所以现在它是一个空的外壳,你可以重复使用,在回收它的部件之后明确地设置为null,或者让它被收集并忘记它,知道你删除了必要的引用。

    这有用吗? 可能不是。

    我唯一一次真正看到它被用作游戏时,你的角色可能只允许一次射出3发子弹,并且他不能射击4,直到屏幕上的第1个击中某人或者离开边缘(这就是说,当时Contra工作的方式)。

    你也可以将一个“消失的”子弹从堆叠中移开,并通过重置其轨迹,重置适当的旗帜并将其推回到堆叠中,将该子弹重新用于任何玩家/敌人。

    但是,再次,直到代理允许我们定义“魔术”构造函数/析构函数方法,这些方法在低级别上得到尊重,这仅在您要对所有对象的创建和销毁进行微观管理时才有用。 (真的不是一个好主意)。

答案 7 :(得分:0)

我的解决方案是创建一个对象存储实例计数和一个在原型中增加它们的函数。

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 />');
}

这是我的傻瓜:https://plnkr.co/edit/hPtIR2MQnV08L9o1oyY9?p=preview

答案 8 :(得分:0)

        class Patient{
        
        constructor(name,age,id){        
            Object.assign(this,{name, age, id});    
            
        }
        static patientList = []; // declare a static variable
        static addPatient(obj){       
            this.patientList.push(...obj); // push to array
            return this.patientList.length; // find the array length to get the number of objects
        }
        
    }
    
    let p1 = new Patient('shreyas',20, 1);
    let p2 = new Patient('jack',25, 2);
    let p3 = new Patient('smith',22, 3);
    let patientCount = Patient.addPatient([p1,p2,p3]); // call static method to update the count value with the newly created object
   console.log(Patient.patientList);
    console.log(patientCount);