如何从该函数中获取函数名称?


263

如何从该函数内部访问函数名称?

// parasitic inheritance
var ns.parent.child = function() {
  var parent = new ns.parent();
  parent.newFunc = function() {

  }
  return parent;
}

var ns.parent = function() {
  // at this point, i want to know who the child is that called the parent
  // ie
}

var obj = new ns.parent.child();

好吧,在父级中,我便可以按照约定访问其他功能,例如ns [child] [schema]或ns [child] [dbService]。没有它,我必须在每个子类中对这些引用进行硬编码。
Scott Klarenbach 2010年

为什么不只是将子函数作为参数传递给父函数?var parent = new ns.parent(this);
螺旋挤压机

因为有数十个这样的查找和数十个孩子。这是我目前正在做的事情,但是每次都是相同的,并且如果可以基于派生函数将重复的逻辑简单地放在父级内部一次,那将是完美的。
Scott Klarenbach'4

瞧,这不是我想要的子函数,而是使用的命名约定,因为该命名约定可用于加载当前未在子对象上定义但与整个系统相关的子函数。
Scott Klarenbach 2010年

1
@Scott我同意其他所有人的观点,您在管理复杂性和代码结构时需要这样做是错误的。这种硬耦合是一个错误的设计决策,并且将导致混乱。@SimeVidas您的伟大的巫师:)
Raynos 2011年

Answers:


163

在ES5中,最好的做法是:

function functionName(fun) {
  var ret = fun.toString();
  ret = ret.substr('function '.length);
  ret = ret.substr(0, ret.indexOf('('));
  return ret;
}

使用Function.caller是非标准的。Function.caller并且arguments.callee都在严格模式下被禁止。

编辑:下面基于nus的正则表达式的答案达到相同的目的,但具有更好的性能!

在ES6中,您可以使用myFunction.name

注意:请注意,某些JS压缩程序可能会丢弃函数名称,以进行更好的压缩。您可能需要调整其设置以避免这种情况。


尽管这对我来说还不够(由于匿名函数),但确实为我提供了执行此操作的想法:fun.toString().substr(0, 100)对于我的需求,这足以找到有问题的函数。因此,感谢您的启发!
Campbeln

3
myFunction.name在ES6中,Yup 是最好的选择。
Vlad A Ionescu 2015年

15
是什么在点myFunction.name如果键入功能的名称?无法达到魔术变量的目的。
adi518 '18

2
@ adi518:不一定..假设您具有任何功能数组,并且可以使用for / foreach对其进行迭代..那么也可以使用arr[index].name:)
Debasish Chowdhury 18/11/25

2
@ adi518不是没有用的。如果我在我的IDE中重命名该功能,myFunction.name则也会更新。当然,intelliJ支持在字符串中重命名标识符,但是这样做的一致性要差很多,并且如果有人忘记勾选“在字符串中搜索”选项,它将默默地过时。myFunction.name比对字符串进行硬编码更好。
byxor

141

ES6(受下面的sendy halim的回答启发):

myFunction.name

关于MDN的说明。自2015年起,可在nodejs和除IE之外的所有主要浏览器中使用。

注意:在绑定函数上,将给出“ bound <originalName>”。如果要获取原始名称,则必须删除“ bound”。


ES5(受弗拉德的回答启发):

如果对该功能有引用,则可以执行以下操作:

function functionName( func )
{
    // Match:
    // - ^          the beginning of the string
    // - function   the word 'function'
    // - \s+        at least some white space
    // - ([\w\$]+)  capture one or more valid JavaScript identifier characters
    // - \s*        optionally followed by white space (in theory there won't be any here,
    //              so if performance is an issue this can be omitted[1]
    // - \(         followed by an opening brace
    //
    var result = /^function\s+([\w\$]+)\s*\(/.exec( func.toString() )

    return  result  ?  result[ 1 ]  :  '' // for an anonymous function there won't be a match
}
  • 我没有对此进行任何单元测试,也没有验证实现上的差异,但是原则上,如果不加评论,它应该可以工作。
  • 注意:不适用于绑定函数
  • 注意:callercallee被认为已弃用。

[1] 之所以将其包含在此处,是因为它是合法的,并且经常有足够的语法突出显示工具未能考虑到函数名和括号之间的空白。另一方面,我不知道.toString()的任何实现都将在此处包含空格,因此这就是为什么您可以忽略它的原因。


作为对原始问题的解答,我将放弃寄生继承,而采用一些更传统的OOP设计模式。我写了一个TidBits.OoJs,它以类似于 C ++的功能集舒适地用JavaScript编写OOP代码(尚未完成,但大多数情况下)。

从评论中可以看出,您希望避免将信息parent需求传递给它的构造函数。我必须承认,传统的设计模式不会使您摆脱那种模式,因为通常使您的依赖关系清晰可见并强制执行是一件好事。

我还建议不要使用匿名函数。他们只进行调试和配置PITA,因为所有内容都显示为“匿名函数”,而我所知道的对他们没有任何好处。


3
好的RegEx!这是一项性能测试,表明您的代码在许多情况下是最快的。通常,RegEx在这里似乎平均比本地JS快2倍。jsperf.com/get-function-name/2
Beejor 2015年

@Tomasz嗨,谢谢你指出这一点。我不知道 绑定函数实际上是包装原始函数的新函数。ecmascript标准§19.2.3.2定义新功能的名称应为“ bound” + originalName。toString方法在绑定函数上也将不起作用...您将不得不删除单词bound。

如果键入函数名称,myFunction.name中有什么意义?无法达到魔术变量的目的。
Borjovsky '18

请注意,如果您使用的是React Native,这可能无法正常工作。我有一个正在使用expo 36版的RN项目.name,无论调用什么名称,组件方法的属性都显示为字符串“ value”。奇怪的是,在expo应用程序中进行测试时还不错,但是,一旦编译成真实的应用程序,它就会无声地崩溃。
安迪·科曼

64

您正在做的是将未命名的函数分配给变量。您可能需要使用命名函数表达式(http://kangax.github.com/nfe/)。

var x = function x() {
    console.log( arguments.callee.name );
}
x();

但是我不确定这是多少跨浏览器;IE6有一个问题,使您函数的名称泄漏到外部作用域。另外,arguments.callee也已过时,如果您使用,则会导致错误strict mode


1
这不适用于旧版本的Safari浏览器。
2013年

17

任何都constructor公开一个属性name,即函数名称。您可以constructor通过实例(使用new)或来访问prototype

function Person() {
  console.log(this.constructor.name); //Person
}

var p = new Person();
console.log(p.constructor.name); //Person

console.log(Person.prototype.constructor.name);  //Person

6
第一次console.log通话会记录日志,window或者Object对我来说取决于我在哪里运行,而不是Person
Bart S

您确定使用“ new”实例化了吗?否则它将无法正常工作。
罗兰

14

看来这是我一生中最愚蠢的事情,但这很有趣:D

function getName(d){
  const error = new Error();
  const firefoxMatch = (error.stack.split('\n')[0 + d].match(/^.*(?=@)/) || [])[0];
  const chromeMatch = ((((error.stack.split('at ') || [])[1 + d] || '').match(/(^|\.| <| )(.*[^(<])( \()/) || [])[2] || '').split('.').pop();
  const safariMatch = error.stack.split('\n')[0 + d];

  // firefoxMatch ? console.log('firefoxMatch', firefoxMatch) : void 0;
  // chromeMatch ? console.log('chromeMatch', chromeMatch) : void 0;
  // safariMatch ? console.log('safariMatch', safariMatch) : void 0;

  return firefoxMatch || chromeMatch || safariMatch;
}

d-堆栈深度。0-返回此函数名称,1-父级,等等;
[0 + d]-只是为了理解-会发生什么;
firefoxMatch-适用于野生动物园,但我确实有一点时间进行测试,因为mac的所有者吸烟后已经回来了,开车把我赶走了:'(

测试:

function limbo(){
  for(let i = 0; i < 4; i++){
    console.log(getName(i));
  }
}
function lust(){
  limbo();
}
function gluttony(){
  lust();
}

gluttony();

结果:
Chrome:
在此处输入图片说明

Fitefox:
在此处输入图片说明

这个解决方案只是为了好玩而已!不要将其用于实际项目。它不取决于ES规范,而仅取决于浏览器的实现。下次更新chrome / firefox / safari后,它可能会损坏。
除此之外,没有错误(ha)处理-如果d将超过堆栈长度-您将得到一个错误;
对于其他wrowsers错误的消息模式-您将得到一个错误;
它必须适用于ES6类(.split('.').pop()),但是您可能会遇到麻烦;


13

这可能对您有用:

function foo() { bar(); }

function bar() { console.log(bar.caller.name); }

如果您从匿名函数调用,运行foo()将输出“ foo”或未定义。

它也与构造函数一起使用,在这种情况下,它将输出调用构造函数的名称(例如“ Foo”)。

此处的更多信息:https : //developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/Caller

他们声称这是非标准的,但是所有主要浏览器都支持它:Firefox,Safari,Chrome,Opera和IE。


在严格模式下不可用...嗯
Ka Mok)

8

你不能 函数没有按照标准命名(尽管mozilla具有这样的属性)-它们只能分配给具有名字的变量。

还有你的评论:

// access fully qualified name (ie "my.namespace.myFunc")

在函数my.namespace.myFunc.getFn中

您可以做的是返回由new创建的对象的构造函数

所以你可以说

var obj = new my.namespace.myFunc();
console.info(obj.constructor); //my.namespace.myFunc

1
我需要在func内部使用它,因为我将其沿继承链传递了,为了简洁起见,我省略了这些内容。
Scott Klarenbach 2010年

1
“ this”将是调用该函数的实例。因此,如果您在父函数中使用“ this”,它将为您提供调用它的函数实例。这就是您所需要的-不确定为什么也需要这个名称
-plodder

1
好吧,在父级中,我便可以按照约定访问其他功能,例如ns [child] [schema]或ns [child] [dbService]。没有它,我必须在每个子类中对这些引用进行硬编码。
Scott Klarenbach 2010年

1
查找名称的用例是使构造console.error消息更容易,该消息可以指示消息的来源,而不必恢复到堆栈转储。例如console.error({'error':{self.name reference} +“输入参数错误”)。如果不必每次都停止并更改其中的文本,则剪切/粘贴在多个功能中重复出现的错误消息变得更加容易。就我而言,我是从多个Promise调用返回Promise错误-很高兴知道是什么Promise / Function产生了错误。
斯科特,

7

您可以将其用于支持Error.stack的浏览器(可能不是全部)

function WriteSomeShitOut(){ 
  var a = new Error().stack.match(/at (.*?) /);
  console.log(a[1]);
} 
WriteSomeShitOut();

当然这是针对当前功能的,但是您知道了。

编码时开心流口水


4

name除非使用匿名函数,否则可以使用property获取函数名称。

例如:

var Person = function Person () {
  this.someMethod = function () {};
};

Person.prototype.getSomeMethodName = function () {
  return this.someMethod.name;
};

var p = new Person();
// will return "", because someMethod is assigned with anonymous function
console.log(p.getSomeMethodName());

现在让我们尝试命名函数

var Person = function Person () {
  this.someMethod = function someMethod() {};
};

现在您可以使用

// will return "someMethod"
p.getSomeMethodName()


3

作为一部分,ECMAScript 6您可以使用Function.name方法

function doSomething() {}

alert(doSomething.name); // alerts "doSomething"

在ReactJS中,您需要添加以下内容:console.log(this.doSomething.name);
Bitclaw

2
功能之外
Scott

3

您可以使用Function.name

在大多数JavaScript实现中,一旦在范围内有了构造函数的引用,就可以从其name属性(例如Function.name或Object.constructor.name)中获取其字符串名称。

您可以使用Function.callee

arguments.caller不推荐使用本机方法,但是大多数浏览器都支持Function.caller,它将返回实际的调用对象(其代码体):https : //developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/函数/调用方?redirectlocale = zh-CN&redirectslug = JavaScript%2FReference%2FGlobal_Objects%2FFunction%2Fcaller

您可以创建一个源地图

如果您需要的是文字函数签名(它的“名称”),而不是对象本身,则您可能不得不诉诸一些定制化的工作,例如创建API字符串值的数组引用。经常访问。您可以使用Object.keys()和字符串数组将它们映射在一起,或者在GitHub上查看Mozilla的源地图库,以获取更大的项目:https : //github.com/mozilla/source-map


2

我知道这是一个老问题,但是最近我在尝试装饰一些React Component的方法以进行调试时遇到了类似的问题。正如人们已经说过的,arguments.caller并且arguments.callee在严格模式下被禁止,这可能在您的React转堆中默认启用。您可以禁用它,或者我也可以提出另一种建议,因为在React中所有类函数都被命名了,您实际上可以这样做:

Component.prototype.componentWillMount = function componentWillMount() {
    console.log('Callee name: ', this.__proto__.constructor.toString().substr(0,30));
...
}

1

这对我有用。

function AbstractDomainClass() {
    this.className = function() {
        if (!this.$className) {
            var className = this.constructor.toString();
            className = className.substr('function '.length);
            className = className.substr(0, className.indexOf('('));
            this.$className = className;
        }
        return this.$className;
    }
}

测试代码:

var obj = new AbstractDomainClass();
expect(obj.className()).toBe('AbstractDomainClass');

1

我遇到了类似的问题,并按如下方式解决了它:

Function.prototype.myname = function() {
   return this.toString()
       .substr( 0, this.toString().indexOf( "(" ) )
       .replace( "function ", "" ); 
}

该代码以一种更加舒适的方式实现了我在本讨论顶部已经阅读的一个响应。现在,我有一个成员函数,用于检索任何函数对象的名称。这是完整的脚本...

<script language="javascript" TYPE="text/javascript">

    Function.prototype.myname = function() { 
        return this.toString()
            .substr( 0, this.toString().indexOf( "(" ) )
            .replace("function ", "" ); 
    }
    function call_this( _fn ) { document.write( _fn.myname() ); }
    function _yeaaahhh() { /* do something */ }
    call_this( _yeaaahhh ); 

</script>


0

这将在ES5,ES6,所有浏览器和严格模式下运行。

这就是命名函数的外观。

(function myName() {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at myName (<anonymous>:2:15)

这是带有匿名函数的外观。

(() => {
  console.log(new Error().stack.split(/\r\n|\r|\n/g)[1].trim());
})();
at <anonymous>:2:15

每个浏览器产生不同的结果。Chrome:at myName (<anonymous>:2:15)Firefox:@debugger eval code:3:3
jkdev

(函数myName(){console.log(new Error()。stack.split(/ \ r \ n | \ r | \ n / g)[1] .trim()。split(“”)[1])) ;})();
Zibri



-3

从正在运行的函数中获取函数名称的简便方法。

function x(){alert(this.name)};x()


1
给我一个GUID
Tamir Daniely

1
要知道this它的内容基本上可以是任何东西。试试(function(){console.log(this.name);}).bind({name: "put basically anything here even an object maybe a 1TB string maybe a class definition"})()
Valen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.