从JavaScript函数中提取嵌套函数名称

Ate*_*ral 7 javascript regex parsing function

给定一个函数,我试图找出其中嵌套函数的名称(只有一个级别).

一个简单的正则表达式反对toString()工作,直到我开始使用带有注释的函数.事实证明,有些浏览器存储原始源的部分,而其他浏览器则根据编译的内容重建源.输出toString()可能包含某些浏览器中的原始代码注释.顺便说一句,这是我的发现:

考试科目

function/*post-keyword*/fn/*post-name*/()/*post-parens*/{
    /*inside*/
}

document.write(fn.toString());
Run Code Online (Sandbox Code Playgroud)

结果

Browser      post-keyword  post-name  post-parens  inside
-----------  ------------  ---------  -----------  --------
 Firefox      No            No         No           No
 Safari       No            No         No           No
 Chrome       No            No         Yes          Yes
 IE           Yes           Yes        Yes          Yes
 Opera        Yes           Yes        Yes          Yes

我正在寻找一种从给定函数中提取嵌套函数名的跨浏览器方式.解决方案应该能够从以下函数中提取"fn1"和"fn2":

function someFn() {
    /**
     * Some comment
     */
     function fn1() {
         alert("/*This is not a comment, it's a string literal*/");
     }

     function // keyword
     fn2 // name
     (x, y) // arguments
     {
         /*
         body
         */
     }

     var f = function () { // anonymous, ignore
     };
}
Run Code Online (Sandbox Code Playgroud)

解决方案不一定是纯正则表达式.

更新:您可以假设我们始终处理有效的,正确嵌套的代码,并且所有字符串文字,注释和块都已正确终止.这是因为我正在解析一个已经编译为有效函数的函数.

Update2:如果你想知道这背后的动机:我正在开发一个名为jsUnity的新的JavaScript单元测试框架.有几种不同的格式可以编写测试和测试套件.其中一个是功能:

function myTests() {
    function setUp() {
    }

    function tearDown() {
    }

    function testSomething() {
    }

    function testSomethingElse() {
    }
}
Run Code Online (Sandbox Code Playgroud)

由于函数隐藏在闭包内,因此我无法从函数外部调用它们.因此,我将外部函数转换为字符串,提取函数名称,在底部附加"现在运行给定的内部函数"语句,并将其重新编译为new函数Function().如果测试函数中包含注释,则提取函数名称并避免误报会变得棘手.因此,我正在征求SO社区的帮助......

Update3:我想出了一个新的解决方案,不需要大量的语义摆弄代码.我使用原始源本身来探测第一级函数.

Chr*_*oph 3

外观变化和错误修复

必须读取正则表达式\bfunction\b以避免误报!

nested如果不计算为 ,则块中定义的函数(例如循环体中)将被忽略true

function tokenize(code) {
    var code = code.split(/\\./).join(''),
        regex = /\bfunction\b|\(|\)|\{|\}|\/\*|\*\/|\/\/|"|'|\n|\s+/mg,
        tokens = [],
        pos = 0;

    for(var matches; matches = regex.exec(code); pos = regex.lastIndex) {
        var match = matches[0],
            matchStart = regex.lastIndex - match.length;

        if(pos < matchStart)
            tokens.push(code.substring(pos, matchStart));

        tokens.push(match);
    }

    if(pos < code.length)
        tokens.push(code.substring(pos));

    return tokens;
}

var separators = {
    '/*' : '*/',
    '//' : '\n',
    '"' : '"',
    '\'' : '\''
};

function extractInnerFunctionNames(func, nested) {
    var names = [],
        tokens = tokenize(func.toString()),
        level = 0;

    for(var i = 0; i < tokens.length; ++i) {
        var token = tokens[i];

        switch(token) {
            case '{':
            ++level;
            break;

            case '}':
            --level;
            break;

            case '/*':
            case '//':
            case '"':
            case '\'':
            var sep = separators[token];
            while(++i < tokens.length && tokens[i] !== sep);
            break;

            case 'function':
            if(level === 1 || (nested && level)) {
                while(++i < tokens.length) {
                    token = tokens[i];

                    if(token === '(')
                        break;

                    if(/^\s+$/.test(token))
                        continue;

                    if(token === '/*' || token === '//') {
                        var sep = separators[token];
                        while(++i < tokens.length && tokens[i] !== sep);
                        continue;
                    }

                    names.push(token);
                    break;
                }
            }
            break;
        }
    }

    return names;
}
Run Code Online (Sandbox Code Playgroud)