如何在Javascript中将类对象的名称作为字符串获取?

42

假设我这样在JavaScript中实例化一个对象:

var myObj = new someObject();
现在,是否有可能从类方法中获取变量对象的名称作为字符串“myObj”?

附加细节(编辑):

我想获取持有该对象引用的变量的名称的原因是,我的新myObj将在页面上创建一个新的可点击的DIV,需要调用函数myObj.someFunction()。当我插入新的DIV时,我需要知道持有该对象引用的变量的名称。也许有更好的方法吗?


您是正确的,术语混淆我很抱歉。

我想获取持有该对象引用的变量的名称的原因是,我的新myObj将在页面上创建一个新的可点击的DIV,需要调用函数myObj.someFunction()。当我插入新的DIV时,我需要知道持有该对象引用的变量的名称。也许有更好的方法吗?


Shog9 是正确的 - 你为什么要尝试这样做?可能有更好的方法来实现你想要的。 - Greg
有点相关的功能请求:建议将 nameof() 添加到 TypeScript 编译器。如果这个请求被接受,你就可以用 nameof(myObj) 的方式来获取字符串 "myObj"。 - styfle
9个回答

43

Shog9说这个问题并没有太多意义,因为一个对象可以被多个变量引用。如果你不关心这一点,而且你只想找到一个全局变量的名称,它引用了该对象,你可以使用以下方法:

function myClass() { 
  this.myName = function () { 
    // search through the global object for a name that resolves to this object
    for (var name in this.global) 
      if (this.global[name] == this) 
        return name 
  } 
}
// store the global object, which can be referred to as this at the top level, in a
// property on our prototype, so we can refer to it in our object's methods
myClass.prototype.global = this
// create a global variable referring to an object
var myVar = new myClass()
myVar.myName() // returns "myVar"

请注意,这是一个丑陋的hack,不应在生产代码中使用。如果有多个变量引用一个对象,则无法确定将获取哪个变量。它只会搜索全局变量,因此如果变量是函数本地的,则无法正常工作。通常,如果需要命名某些东西,您应该在创建时通过构造函数传递名称。
编辑:为了回应您的澄清,如果您需要能够从事件处理程序引用某些内容,则不应按名称引用它,而是添加一个直接引用对象的函数。下面是一个我快速制作的示例,显示了类似于您尝试执行的操作:
function myConstructor () {
  this.count = 0
  this.clickme = function () {
    this.count += 1
    alert(this.count)
  }

  var newDiv = document.createElement("div")
  var contents = document.createTextNode("Click me!")

  // This is the crucial part. We don't construct an onclick handler by creating a
  // string, but instead we pass in a function that does what we want. In order to
  // refer to the object, we can't use this directly (since that will refer to the 
  // div when running event handler), but we create an anonymous function with an 
  // argument and pass this in as that argument.
  newDiv.onclick = (function (obj) { 
    return function () {
      obj.clickme()
    }
  })(this)

  newDiv.appendChild(contents)
  document.getElementById("frobnozzle").appendChild(newDiv)

}
window.onload = function () {
  var myVar = new myConstructor()
}

1
到目前为止,我一直在将变量的名称传递给构造函数,但我想知道是否有其他实现相同功能的不同方法。 - Miro Solanka
1
Brian,感谢您的进一步评论。最终我按照您在修改后的答案中所指出的做法,动态地分配了onclick处理程序,而不是通过将其作为字符串传递给div的onclick属性。 - Miro Solanka

21

简短回答: 不是的。myObj不是对象的名称,它是一个变量名,用于保存对该对象的引用 - 您可以有任意数量的其他变量保存对同一对象的引用。

现在,如果这是您的程序,那么您就制定规则:如果您想说任何给定对象只能由一个变量引用,并在代码中认真执行此操作,则只需在对象上设置具有变量名称的属性即可。

话虽如此,我怀疑您实际上想要的并不是这个。也许您可以稍微详细描述一下您的问题……?


小题大做:JavaScript没有类。someObject是一个构造函数。给定一个对象的引用,您可以使用 constructor 属性获取创建它的函数的引用。


针对你提供的额外细节的回应:

您要寻找的答案可以在这里找到:JavaScript Callback Scope(在SO上,对于那些新接触JS的人来说,这是一个常见的困惑点)。您只需要将对对象成员的调用包装在一个闭包中,以保留对上下文对象的访问。


3
不是学究气,这是理解的关键。 - Marc Stober

11

你可以通过使用构造函数的 .toString() 方法将其转换为字符串来实现:

function getObjectClass(obj){
   if (typeof obj != "object" || obj === null) return false;
   else return /(\w+)\(/.exec(obj.constructor.toString())[1];}

5
我认为OP是在询问变量的名称,而不是构造函数的名称。 - Brian Campbell
2
另外:obj.constructor.name - Steven Almeroth

4

您可以通过在函数中使用它,然后使用 toString() 检查函数的源代码,来实现您的目标:

var whatsMyName;

  // Just do something with the whatsMyName variable, no matter what
function func() {var v = whatsMyName;}

// Now that we're using whatsMyName in a function, we could get the source code of the function as a string:
var source = func.toString();

// Then extract the variable name from the function source:
var result = /var v = (.[^;]*)/.exec(source);

alert(result[1]); // Should alert 'whatsMyName';

2
如果您不想使用像Brian答案中那样的函数构造器,您可以使用Object.create()代替:
var myVar = {
count: 0
}

myVar.init = function(n) {
    this.count = n
    this.newDiv()
}

myVar.newDiv = function() {
    var newDiv = document.createElement("div")
    var contents = document.createTextNode("Click me!")
    var func = myVar.func(this)
    newDiv.addEventListener ? 
        newDiv.addEventListener('click', func, false) : 
        newDiv.attachEvent('onclick', func)
    newDiv.appendChild(contents)
    document.getElementsByTagName("body")[0].appendChild(newDiv)
}

myVar.func = function (thys) {
   return function() {
      thys.clickme()
   }
} 

myVar.clickme = function () {
   this.count += 1
   alert(this.count)
}

myVar.init(2)

var myVar1 = Object.create(myVar)
myVar1.init(55) 

var myVar2 = Object.create(myVar)
myVar2.init(150) 

// etc

奇怪的是,我无法使用newDiv.onClick让上述内容正常工作,但可以通过使用newDiv.addEventListener / newDiv.attachEvent来实现。

由于Object.create是较新的功能,因此需要在旧版浏览器中包含Douglas Crockford的以下代码,包括IE8。

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o
        return new F()
    }
}

2

对象实例化后,您可以立即将属性(例如name)附加到该对象,并为其分配预期的字符串值:

var myObj = new someClass();
myObj.name="myObj";

document.write(myObj.name);

或者,也可以在类的代码中进行分配,即

var someClass = function(P)
{ this.name=P;
  // rest of the class definition...
};

var myObj = new someClass("myObj");
document.write(myObj.name);

2
作为一个更基本的情况,如果this有一个属性可以引用它的引用变量(headstails),那就太好了,但不幸的是它只引用新的coinSide对象的实例化。
javascript:  /* it would be nice but ... a solution NOT! */
function coinSide(){this.ref=this};
/* can .ref be set so as to identify it's referring variable? (heads or tails) */
heads = new coinSide();
tails = new coinSide();
toss = Math.random()<0.5 ? heads : tails;
alert(toss.ref);
alert(["FF's Gecko engine shows:\n\ntoss.toSource()  is  ", toss.toSource()])

始终显示的内容

[object Object]

而 Firefox 的 Gecko 引擎显示:

toss.toSource()  is  ,#1={ref:#1#}
当然,在这个例子中,要解决#1,从而得到toss,只需测试toss==headstoss==tails。这个问题实际上是在询问JavaScript是否有一个按名称调用的机制,这激发了对于相应的问题的考虑,即有没有一个按值调用的机制来确定变量的实际值?该示例演示了headstails的“值”是相同的,但是alert(heads==tails)false
自我引用可以强制转换如下:
(避免对象空间查找和可能存在的歧义,如如何在JavaScript中将类对象的名称作为字符串获取?中所述)
javascript:
function assign(n,v){ eval( n +"="+ v ); eval( n +".ref='"+ n +"'" ) }
function coinSide(){};
assign("heads", "new coinSide()");
assign("tails", "new coinSide()");
toss = Math.random()<0.5 ? heads : tails;
alert(toss.ref);

展示 正面 或者 反面

对于 Javascript 的语言设计来说,作为一种解释型原型化函数式语言,拥有这样的基本功能可能是违背其本质的。

最后需要考虑的是:

 javascript:
     item=new Object(); refName="item"; deferAgain="refName";
     alert([deferAgain,eval(deferAgain),eval(eval(deferAgain))].join('\n'));

so, as stipulated ...

javascript:
  function bindDIV(objName){ 
    return eval( objName +'=new someObject("'+objName+'")' )
  };
  function someObject(objName){
    this.div="\n<DIV onclick='window.opener."+   /* window.opener - hiccup!! */
               objName+
             ".someFunction()'>clickable DIV</DIV>\n";
    this.someFunction=function(){alert(['my variable object name is ',objName])}
  };
  with(window.open('','test').document){         /*     see above hiccup    */
    write('<html>'+
      bindDIV('DIVobj1').div+
      bindDIV('DIV2').div+
      (alias=bindDIV('multiply')).div+
      'an aliased DIV clone'+multiply.div+
      '</html>');
    close();
  };
  void (0);

有没有更好的方式...?

"better"指更容易吗?更容易编程吗?更容易理解吗?更容易执行速度快吗?还是指“...现在换个完全不同的东西”?


"...'an aliased DIV clone'+multiply.div+..." 和 "...'an aliased DIV clone'+alias.div+..." 生成相同的 HTML 源代码,但后者在上述示例中不驻留。" - Ekim
在 Firefox 中,可以通过“拖放”方式将示例代码拖到 URL 地址栏上来执行:选中示例代码,将其拖动到地址栏上,释放并激活 URI(按回车或单击“转到”等按钮)。需要允许弹出窗口作为生成的 html 源的目标。 - Ekim
“这个评论‘作为一个解释型原型功能语言,拥有原语的能力或许与Javascript的语言设计本质相悖’,有点(很多?)玩笑般的不协调,因为我们可以看到eval原语确实存在!(而且,值得赞扬的是FireFox的Gecko引擎还提供了非常强大的反向eval函数.toSource()!)” - Ekim

0

这篇文章可能有些陈旧,但我是通过谷歌搜索找到这个问题的,所以也许这个解决方案对其他人也有用。

function GetObjectName(myObject){
    var objectName=JSON.stringify(myObject).match(/"(.*?)"/)[1];
    return objectName;
}

它只使用浏览器的JSON解析器和正则表达式,而不会使DOM或您的对象过于混乱。


0

一段时间以前,我使用过这个。

也许你可以尝试:

+function(){
    var my_var = function get_this_name(){
        alert("I " + this.init());
    };
    my_var.prototype.init = function(){
        return my_var.name;
    }
    new my_var();
}();

弹出警告"我得到这个名字"


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接