Javascript 你能得到调用函数的属性名吗?

Javascript 你能得到调用函数的属性名吗?,javascript,properties,Javascript,Properties,我已经做了很多搜索和一些游戏,我很确定这个问题的答案是否定的,但我希望JavaScript专家可能有一个可以做到这一点的窍门 JavaScript函数可以由多个属性引用,即使在完全不同的对象上也是如此,因此不存在保存函数的对象或属性。但是,无论何时实际调用函数,都必须至少通过单个对象(全局函数调用的窗口对象)和该对象上的属性来完成 函数也可以通过函数局部变量调用,但是我们可以将函数局部变量看作是作用域的激活对象的属性,因此情况对于这个规则也不例外。 我的问题是,有没有办法从函数体内部获取用于调用

我已经做了很多搜索和一些游戏,我很确定这个问题的答案是否定的,但我希望JavaScript专家可能有一个可以做到这一点的窍门

JavaScript函数可以由多个属性引用,即使在完全不同的对象上也是如此,因此不存在保存函数的对象或属性。但是,无论何时实际调用函数,都必须至少通过单个对象(全局函数调用的窗口对象)和该对象上的属性来完成

函数也可以通过函数局部变量调用,但是我们可以将函数局部变量看作是作用域的激活对象的属性,因此情况对于这个规则也不例外。 我的问题是,有没有办法从函数体内部获取用于调用函数的属性名?我不想将属性名作为参数传入,也不想在封闭范围内围绕变量传递闭包,也不想将该名称作为单独的属性存储在包含函数引用的对象上,并让函数访问该对象上的该名称属性

下面是我想做的一个例子:

var callName1 = function() { var callName = /* some magic */; alert(callName); };
var obj1 = {'callName2':callName1, 'callName3':callName1 };
var obj2 = {'callName4':callName1, 'callName5':callName1 };

callName1(); // should alert 'callName1'
obj1.callName2(); // should alert 'callName2'
obj1.callName3(); // should alert 'callName3'
obj2.callName4(); // should alert 'callName4'
obj2.callName5(); // should alert 'callName5'
从我的搜索中,看起来最接近上述参数的是arguments.callee.name,但这不起作用,因为这只返回定义函数对象时固定到该函数对象的名称,并且仅当该函数定义为命名函数,而我的示例中的函数不是

我还考虑过,也许您可以迭代这个对象的所有属性,并测试其和arguments.callee的相等性,以找到其值是对函数本身的引用的属性,但这在一般情况下也不起作用,因为在对象自己的或继承的属性集中可能存在对函数的多个引用,如我的示例中所示。而且,这似乎是一种低效的解决方案

可以这样做吗?

简短回答:

否,无法获取用于调用函数的属性名称。 可能根本没有名称,或者在不同的作用域中有多个名称,因此属性名称的定义非常糟糕。 arguments.callee已弃用,不应使用。 不存在不使用参数或闭包的解决方案。 长答覆:

正如第四位老师所评论的,你应该重新思考你想做什么,并用一个新问题来代替。但是有一些常见的误解,所以我将尝试解释为什么您无法获得简单的属性名

原因是它并不简单

在我们继续之前,让我们澄清一些事情。激活对象根本不是对象。 ECMAScript 5.1规范称之为环境记录,但更常见的术语是范围链。 在浏览器中,全局范围是窗口对象,但所有其他范围都不是对象。 可能存在用于调用函数的对象,当调用函数时,必须在某个范围内

除了少数例外,作用域不是对象,对象也不是作用域

然后,有很多名字

调用函数时,需要引用它,例如通过对象属性。此引用可能有一个名称。 作用域链有声明,声明总是有一个名称。 函数(不是引用的实函数)也可能有一个函数名—arguments.callee.name—该名称在声明时固定。 它们不仅名称不同,而且并不总是您要查找的属性名称

var obj = { prop : function f(){} }, func = obj.prop;
// "obj" and "func" are declarations.
// Function name is "f" - use this name instead of arguments.callee
// Property name is "prop"
func();     // Reference name is "func"
obj.prop(); // Reference names are "obj" and "prop"
// But they are the same function!
// P.S. "this" in f is undefined (strict mode) or window (non-strict)
因此,函数引用可能来自绑定,例如函数声明、对象arguments.callee或变量。 它们都是参考资料。可以说,reference确实有一个名称

问题是,函数引用并不总是来自对象或作用域链,其名称也不总是定义的。 例如,一种常见的闭合技术:

(function(i){ /* what is my name? */ })(i)
即使引用确实有名称,函数调用也不会以任何方式将引用或其名称传递给函数。 这使得JavaScript引擎保持正常。考虑这个例子:

eval("(new Function('return function a(){}'))()")() // Calls function 'a'.
最后一个函数调用引用了eval函数,它引用了一个新的全局范围的结果,无论如何,它引用了一个函数调用语句,它引用了一个组,它引用了一个匿名函数对象,它包含了表示并返回一个名为“a”的函数的代码

如果要从中获取属性名称,应获取哪一个?评估?作用匿名的A.都是吗? 在回答之前,考虑诸如跨IfRAMS的函数访问等复杂的问题,它具有不同的全局性以及交叉原点限制,或者与本机函数Frase.TyrType .Bin的交互,并且您将看到它如何迅速变成地狱。 这也是为什么arguments.caller、_caller__;和其他类似技术现在都被弃用的原因。 财产 函数名的定义甚至比调用方更不明确,几乎不现实。 至少调用方始终是函数不需要的执行上下文

因此,在不知道真正的问题是什么的情况下,获取属性名称的最佳方法是使用闭包。

简短回答:

否,无法获取用于调用函数的属性名称。 可能根本没有名称,或者在不同的作用域中有多个名称,因此属性名称的定义非常糟糕。 arguments.callee已弃用,不应使用。 不存在不使用参数或闭包的解决方案。 长答覆:

正如第四位老师所评论的,你应该重新思考你想做什么,并用一个新问题来代替。但是有一些常见的误解,所以我将尝试解释为什么您无法获得简单的属性名

原因是它并不简单

在我们继续之前,让我们澄清一些事情。激活对象根本不是对象。 ECMAScript 5.1规范称之为环境记录,但更常见的术语是范围链。 在浏览器中,全局范围是窗口对象,但所有其他范围都不是对象。 可能存在用于调用函数的对象,当调用函数时,必须在某个范围内

除了少数例外,作用域不是对象,对象也不是作用域

然后,有很多名字

调用函数时,需要引用它,例如通过对象属性。此引用可能有一个名称。 作用域链有声明,声明总是有一个名称。 函数(不是引用的实函数)也可能有一个函数名—arguments.callee.name—该名称在声明时固定。 它们不仅名称不同,而且并不总是您要查找的属性名称

var obj = { prop : function f(){} }, func = obj.prop;
// "obj" and "func" are declarations.
// Function name is "f" - use this name instead of arguments.callee
// Property name is "prop"
func();     // Reference name is "func"
obj.prop(); // Reference names are "obj" and "prop"
// But they are the same function!
// P.S. "this" in f is undefined (strict mode) or window (non-strict)
因此,函数引用可能来自绑定,例如函数声明、对象arguments.callee或变量。 它们都是参考资料。可以说,reference确实有一个名称

问题是,函数引用并不总是来自对象或作用域链,其名称也不总是定义的。 例如,一种常见的闭合技术:

(function(i){ /* what is my name? */ })(i)
即使引用确实有名称,函数调用也不会以任何方式将引用或其名称传递给函数。 这使得JavaScript引擎保持正常。考虑这个例子:

eval("(new Function('return function a(){}'))()")() // Calls function 'a'.
最后一个函数调用引用了eval函数,它引用了一个新的全局范围的结果,无论如何,它引用了一个函数调用语句,它引用了一个组,它引用了一个匿名函数对象,它包含了表示并返回一个名为“a”的函数的代码

如果要从中获取属性名称,应获取哪一个?评估?作用匿名的A.都是吗? 在回答之前,考虑诸如跨IfRAMS的函数访问等复杂的问题,它具有不同的全局性以及交叉原点限制,或者与本机函数Frase.TyrType .Bin的交互,并且您将看到它如何迅速变成地狱。 这也是为什么arguments.caller、_caller__;和其他类似技术现在都被弃用的原因。 函数的属性名甚至比调用方定义得更糟糕,几乎不切实际。 至少调用方始终是函数不需要的执行上下文

因此,在不知道真正的问题是什么的情况下,获取属性名称的最佳方法是使用闭包。

是的

有点

这取决于浏览器。Chrome=OK,Firefox=Nope

你可以使用一个工厂来创建函数,一个调用堆栈解析黑客可能会让我被捕

这个解决方案适用于我在Windows7上的Chrome版本,但是如果其他浏览器像Chrome一样支持堆栈并在调用堆栈中显示属性名,那么这种方法可以适用于其他浏览器。我不建议在生产代码中这样做,因为这是一种相当脆弱的黑客行为;而是改进程序的体系结构,这样您就不需要依赖于知道调用属性的名称。你没有发布关于你的问题领域的细节,所以这只是一个有趣的小思想实验;也就是说:

JSFIDLE演示:

可运行代码段:向下滚动并单击运行代码段

函数getCallerNamex{ //解析调用堆栈以查找调用方的名称;假定从对象属性调用 //todo:替换为regex left,作为读者的练习 //这适用于win7上的chrome。其他浏览器的格式可能不同,但未经测试。 //如果已知规则,则很容易将此概念扩展为特定于浏览器的。 //这只是出于教育目的;我不会在生产代码中这样做。 var stack=ex.stack.toString; var idx=stack.indexOf'\n'; 变量行=ex.stack.substringidx+1; var objectSentinel='Object'; idx=lines.indexOfobjectSentinel; var line=lines.substringidx+objectSentinel.length; idx=行。索引为“”; var callerName=line.substring0,idx; 返回callerName; } 变量工厂={ getFunction:函数{ 返回函数{ var callName=; 试一试{ 抛出;//您不必*抛出来获取堆栈跟踪,但这更有趣! }缉捕犯{ callName=GetCallerNamex; } alertcallName; }; } } 变量obj1={ “callName2”:Factory.getFunction, “callName3”:Factory.getFunction }; 变量obj2={ “callName4”:Factory.getFunction, “callName5”:Factory.getFunction }; obj1.callName2;//应提醒“callName2” obj1.callName3;//应提醒“callName3” obj2.callName4;//应提醒“callName4” obj2.callName5;//应提醒“callName5”是

有点

这取决于浏览器。Chrome=OK,Firefox=Nope

你可以使用一个工厂来创建函数,一个调用堆栈解析黑客可能会让我被捕

这个解决方案适用于我在Windows7上的Chrome版本,但是如果其他浏览器像Chrome一样支持堆栈并在调用堆栈中显示属性名,那么这种方法可以适用于其他浏览器。我不建议在生产代码中这样做,因为这是一种相当脆弱的黑客行为;而是改进程序的体系结构,这样您就不需要依赖于知道调用属性的名称。你没有发布关于你的问题领域的细节,所以这只是一个有趣的小思想实验;也就是说:

JSFIDLE演示:

可运行代码段:向下滚动并单击运行代码段

函数getCallerNamex{ //解析调用堆栈以查找调用方的名称;假定从对象属性调用 //todo:替换为regex left,作为读者的练习 //这适用于win7上的chrome。其他浏览器的格式可能不同,但未经测试。 //如果已知规则,则很容易将此概念扩展为特定于浏览器的。 //这只是出于教育目的;我不会在生产代码中这样做。 var stack=ex.stack.toString; var idx=stack.indexOf'\n'; 变量行=ex.stack.substringidx+1; var objectSentinel='Object'; idx=lines.indexOfobjectSentinel; var line=lines.substringidx+objectSentinel.length; idx=行。索引为“”; var callerName=line.substring0,idx; 返回callerName; } 变量工厂={ getFunction:函数{ 返回函数{ var callName=; 试一试{ 抛出;//您不必*抛出来获取堆栈跟踪,但这更有趣! }缉捕犯{ callName=GetCallerNamex; } alertcallName; }; } } 变量obj1={ “callName2”:Factory.getFunction, “callName3”:Factory.getFunction }; 变量obj2={ “callName4”:Factory.getFunction, “callName5”:Factory.getFunction }; obj1.callName2;//应提醒“callName2” obj1.callName3;//应提醒“callName3” obj2.callName4;//应提醒“callName4”
obj2.callName5;//应该提醒“callName5”没有反射,但您可以使用函数行为使添加自己的函数变得相当轻松,而无需诉诸try/catch、arguments.callee、function.caller或其他强烈反对的行为,只是浪费循环:

// returning a function from inside a function always creates a new, unique function we can self-identify later:
function callName() { 
   return function callMe(){ 
             for(var it in this) if(this[it]===callMe) return alert(it);
   }  
};

//the one ugly about this is the extra "()" at the end:
var obj1 = {'callName2':callName(), 'callName3':callName() };
var obj2 = {'callName4':callName(), 'callName5':callName() };

//test out the tattle-tale function:
obj1.callName2(); // alerts 'callName2'
obj2.callName5(); // alerts 'callName5'
如果您真的想让它看起来像一个赋值,并避免每次在对象文本中执行paren,您可以执行以下hacky例程来创建一个调用别名:

function callName() { 
   return function callMe(){ 
             for(var it in this) if(this[it]===callMe) return alert(it);
   }  
};

//make an alias to execute a function each time it's used :
Object.defineProperty(window, 'callNamer', {get: function(){ return callName() }});

//use the alias to assign a tattle-tale function (look ma, no parens!):
var obj1 = {'callName2': callNamer, 'callName3': callNamer };
var obj2 = {'callName4': callNamer, 'callName5': callNamer };

//try it out:
obj1.callName2(); // alerts 'callName2'
obj2.callName5(); // alerts 'callName5'
除此之外,您可能不需要此方法所需的所有循环就可以完成所需的工作

优点:

适用于全局或对象属性 不需要重复传递密钥/名称 不使用专有或不推荐的功能 不使用参数或闭包 周围的代码比优化后的代码执行得更快 试一试的版本 不会被重复使用所混淆 可以处理新的和已删除的重命名属性 注意事项:

不适用于没有属性名称的私有变量 部分循环每个访问的所有者对象 计算速度比记忆的属性或代码时间重复慢 无法通过呼叫/绑定/应用 如果没有绑定或包装函数,则无法在setTimeout中生存 不容易克隆
老实说,礼貌地说,我认为完成这项任务的所有方法都不太理想,我建议您咬紧牙关,传递额外的键名,或者使用一种方法将属性添加到空白对象中,而不是用对象文字对其进行编码,从而实现自动化。

没有反射,但您可以使用函数行为使添加自己的函数变得相当轻松,而无需求助于try/catch、arguments.callee、function.caller或其他强烈反对的行为,这只是浪费性的循环:

// returning a function from inside a function always creates a new, unique function we can self-identify later:
function callName() { 
   return function callMe(){ 
             for(var it in this) if(this[it]===callMe) return alert(it);
   }  
};

//the one ugly about this is the extra "()" at the end:
var obj1 = {'callName2':callName(), 'callName3':callName() };
var obj2 = {'callName4':callName(), 'callName5':callName() };

//test out the tattle-tale function:
obj1.callName2(); // alerts 'callName2'
obj2.callName5(); // alerts 'callName5'
如果您真的想让它看起来像一个赋值,并避免每次在对象文本中执行paren,您可以执行以下hacky例程来创建一个调用别名:

function callName() { 
   return function callMe(){ 
             for(var it in this) if(this[it]===callMe) return alert(it);
   }  
};

//make an alias to execute a function each time it's used :
Object.defineProperty(window, 'callNamer', {get: function(){ return callName() }});

//use the alias to assign a tattle-tale function (look ma, no parens!):
var obj1 = {'callName2': callNamer, 'callName3': callNamer };
var obj2 = {'callName4': callNamer, 'callName5': callNamer };

//try it out:
obj1.callName2(); // alerts 'callName2'
obj2.callName5(); // alerts 'callName5'
除此之外,你可以 无需此方法所需的所有循环即可可靠地完成所需的操作

优点:

适用于全局或对象属性 不需要重复传递密钥/名称 不使用专有或不推荐的功能 不使用参数或闭包 周围的代码比优化后的代码执行得更快 试一试的版本 不会被重复使用所混淆 可以处理新的和已删除的重命名属性 注意事项:

不适用于没有属性名称的私有变量 部分循环每个访问的所有者对象 计算速度比记忆的属性或代码时间重复慢 无法通过呼叫/绑定/应用 如果没有绑定或包装函数,则无法在setTimeout中生存 不容易克隆
老实说,我认为完成这项任务的所有方法都不太理想,礼貌一点,我建议你咬紧牙关,传递额外的密钥名,或者通过使用一种方法将属性添加到空白对象中,而不是用对象文字对其进行编码,从而实现自动化。

听起来像是一个类似于您所寻找的思路的问题:?迭代似乎是唯一不需要源代码转换的解决方案。如果对代码进行预处理没有问题,则可以简单地转换所有CallExpression并将标识符名称注入函数。@PM77-1,否,function.caller返回调用当前执行函数的函数。我要查找的是调用当前正在执行的函数的对象上的属性名称,它与调用当前正在执行的函数的函数没有必要的关系。通过这样做,您试图实现什么?即使有一个API来做这件事,听起来也是个可怕的想法。听起来像是一个符合你所寻找的思路的解决方案:?迭代似乎是唯一不需要源代码转换的解决方案。如果对代码进行预处理没有问题,则可以简单地转换所有CallExpression并将标识符名称注入函数。@PM77-1,否,function.caller返回调用当前执行函数的函数。我要查找的是调用当前正在执行的函数的对象上的属性名称,它与调用当前正在执行的函数的函数没有必要的关系。通过这样做,您试图实现什么?即使有一个API来做这件事,听起来也是个可怕的主意。被捕了吗?不,用火刑把他烧死!我想写下这个确切的答案+1。。。有了重塑的需要,这个就出来了!逮捕?不,用火刑把他烧死!我想写下这个确切的答案+1。。。有了重塑的需要,这个就出来了!