Javascript 你能得到调用函数的属性名吗?
我已经做了很多搜索和一些游戏,我很确定这个问题的答案是否定的,但我希望JavaScript专家可能有一个可以做到这一点的窍门 JavaScript函数可以由多个属性引用,即使在完全不同的对象上也是如此,因此不存在保存函数的对象或属性。但是,无论何时实际调用函数,都必须至少通过单个对象(全局函数调用的窗口对象)和该对象上的属性来完成Javascript 你能得到调用函数的属性名吗?,javascript,properties,Javascript,Properties,我已经做了很多搜索和一些游戏,我很确定这个问题的答案是否定的,但我希望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。。。有了重塑的需要,这个就出来了!