13

如果我有一个 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 初始化时增加一个全局变量,这将是跟踪这个计数器的唯一方法,但是有什么比这个想法更好的吗?

编辑:

看看这里的建议:

在此处输入图像描述

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

4

9 回答 9

20

没有内置任何东西;然而,你可以让你的构造函数记录它被调用的次数。不幸的是,JavaScript 语言无法判断对象何时超出范围或已被垃圾回收,因此您的计数器只会上升,而不会下降。

例如:

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

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

[编辑]

根据您更新的问题 - 无法跟踪实例何时不再使用或“删除”(例如通过将 null 分配给变量),因为 JavaScript 没有为对象提供终结器方法

您能做的最好的事情是创建一个“dispose”方法,当对象不再处于活动状态时(例如,通过引用计数方案)将调用该方法,但这需要程序员的合作 - 该语言不提供帮助:

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
于 2012-09-11T22:39:44.690 回答
4

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

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

MyObj.count = 0;

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

alert(MyObj.count);

这就是你通常在 Java 中使用的方式(使用静态属性)。

于 2012-09-11T22:45:34.243 回答
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}
于 2015-12-17T16:03:36.430 回答
3

使用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属性,请将其设为私有

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

于 2020-04-03T14:45:34.757 回答
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();           
于 2014-12-24T12:56:07.863 回答
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();

这毕竟是我们的a和变量:b

在此处输入图像描述

于 2019-07-27T10:57:25.417 回答
0

最终,JS 将拥有内置的代理能力,它将对后台发生的各种事情进行低级访问,永远不会暴露给前端开发人员(通过代理除外——think magic -PHP 等语言中的方法)。

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

目前,可靠地做到这一点的唯一方法可能是创建所有已创建实例的封闭注册表,然后手动销毁它们(否则,它们将永远不会被垃圾收集)。

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方法可以访问任何内部变量

...那么是的,这种方法可以很好地让计数器正常工作。您甚至可以编写一个名为 的通用方法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 发子弹,并且在屏幕上的第 1 发子弹击中某人或熄灭之前,他不能射击第 4 发子弹边缘(这就是,比如说,Contra 在当时是如何工作的)。

您也可以将“消失”的子弹从堆栈中移出,并通过重置其轨迹、重置适当的标志并将其推回堆栈来为任何玩家/敌人重复使用该子弹。

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

于 2012-09-11T23:40:43.640 回答
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);
于 2021-04-08T12:08:29.573 回答
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 />');
}

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

于 2016-09-12T06:55:38.730 回答