如何编写正则表达式来查找字符串中的所有函数名?

问题描述 投票:-3回答:3

我想知道为以下字符串编写正则表达式以查找所有函数名称。

"var sampleFunc = function(){return 'hello';}alert(sampleFunc());function sampleTest(){var sampleTestVar = 'one';};var sampleFunc = function(){return 'hello';}alert(sampleFunc());function sampleTest(){var sampleTestVar = 'one';};"

上面的字符串包含简单的JS程序。我想得到上面的字符串的输出,

["sampleFunc", "sampleTest", "sampleFunc", "sampleTest"]

帮我写上述问题的正则表达式。

javascript regex
3个回答
1
投票

好的,这是另一种方法。在这种更安全可靠的方法中,我使用了acorn,它是CodeMirror的TernJS用于解析javascript的库。 CodeMirror是非常强大的基于Web的代码编辑器,几乎用于eveywhere(即使在这里也是如此)。

代码:

首先,这是代码:

HTML:

<script src="path/to/accorn.js"></script>
<script src="path/to/walk.js"></script>

使用Javascript:

function getFunctionNames(codeString) {
    var names = [];
    acorn.walk.simple(acorn.parse(codeString), {
        AssignmentExpression: function(node) {
            if(node.left.type === "Identifier" && (node.right.type === "FunctionExpression" || node.right.type === "ArrowFunctionExpression")) {
                names.push(node.left.name);
            }
        },
        VariableDeclaration: function(node) {
            node.declarations.forEach(function (declaration) {
                if(declaration.init && (declaration.init.type === "FunctionExpression" || declaration.init.type === "ArrowFunctionExpression")) {
                    names.push(declaration.id.name);
                }
            });
        },
        Function: function(node) {
            if(node.id) {
                names.push(node.id.name);
            }
        }
    });
    return names;
}

例:

function getFunctionNames(codeString) {
    var names = [];
    acorn.walk.simple(acorn.parse(codeString), {
        AssignmentExpression: function(node) {
            if(node.left.type === "Identifier" && (node.right.type === "FunctionExpression" || node.right.type === "ArrowFunctionExpression")) {
                names.push(node.left.name);
            }
        },
        VariableDeclaration: function(node) {
            node.declarations.forEach(function (declaration) {
                if(declaration.init && (declaration.init.type === "FunctionExpression" || declaration.init.type === "ArrowFunctionExpression")) {
                    names.push(declaration.id.name);
                }
            });
        },
        Function: function(node) {
            if(node.id) {
                names.push(node.id.name);
            }
        }
    });
    return names;
}

console.log(getFunctionNames(`

var sampleFunc = function() {
    return 'hello';
}
/*
  function thisIsNotReallyAFunction() {} 
*/

alert(sampleFunc());
function /* undesired comment */ sampleTest() {
    var sampleTestVar = 'one';
};
var sampleFunc=
// still OK!
function() {
    return 'hello';
}
alert(sampleFunc());
function
// all sotrts of comments
sampleTest()
/* Even
 * Block ones
 */

{
    var sampleTestVar = 'one';
};
var sampleFuncEDIT;
sampleFunEDIT = function (){};
var functionNameEDIT = "sampleFunc";

`));
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/acorn.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/walk.js"></script>

说明:

有关详细解释,请查看acorn的github页面here

acorn被分成一堆源文件,每个文件负责一个特定的工作。我们只使用acorn.jswalk.js

acorn.js用于解析。它包含许多用于解析的有用函数,如acorn.parse()accorn.parseExpressionAt()acorn.tokenizer(),......我们只对返回AST的acorn.parse感兴趣(abstract syntax tree。这是节点的树结构。一个节点描述了一个有意义的块代码,它可以是赋值,函数调用,变量声明,...一个节点将是一个具有描述该代码块的属性的对象。它将具有type属性,一个start(其中代码块)开始),一个end(它结束的地方),每种类型的节点都有一些仅用于该类型的附加属性。

现在,我们有了AST树,我们可以自己遍历它(它们只是一堆嵌套对象)。或者使用acorn的方式:acorn为我们提供了一种非常强大的步行方式。这些函数位于文件walk.js中。与acorn.js相同,walk.js也包含一堆有用的功能,我们只需要walk.simple()walk.simple做的是,它需要一棵树和另一个对象作为参数。树是我们的AST树(由acorn.parse返回),对象是这种形式的对象:

{
    [NodeType1]: function(node) { /* node is of type NodeType1 */ },
    [NodeType2]: function(node) { /* node is of type NodeType2 */ },
    ...
}

walk.simple逐节点遍历树时,它会检查当前节点的类型是否有函数,如果有,它将调用该函数(将节点本身传递给它)并继续执行到下一个节点,如果没有它将忽略该节点并继续下一个节点。从我们感兴趣的各种节点类型:

  1. Function

这基本上是一个正常的函数声明,例如:

var codeString = `
function f () {
};

function someName() {
};

() => {
};`;

acorn.walk.simple(acorn.parse(codeString), {
    Function: function(node) {
        console.log(node);
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/acorn.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/walk.js"></script>

一些额外的属性对是:id(这是一个标识符节点,用于此函数声明,如果函数没有,则为null)。标识符节点(如果存在)具有name属性,该属性将是我们函数的名称。

  1. VariableDeclaration

哪个是使用varletconst的变量声明:

var codeString = `
var e, f = function() {}, g = () => {};
`;

acorn.walk.simple(acorn.parse(codeString), {
    VariableDeclaration: function(node) {
        console.log(node);
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/acorn.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/walk.js"></script>

这种类型的节点还将具有一些附加属性,例如declarations,它是所有声明的数组(上面的示例显示3:一个用于e,一个用于f,一个用于g)。声明也是节点,它们具有额外的id(标识符节点)和init(初始化对象,它是描述我们在初始化时分配给变量的值的节点,如果不存在则为null)。我们只对init.type是一个函数节点("FunctionExpression""ArrowFunctionExpression")感兴趣。

  1. AssignmentExpression

哪个是使用=的任务(不要与变量初始化混淆):

var codeString = `
someVar = function() {
}
`;

acorn.walk.simple(acorn.parse(codeString), {
    AssignmentExpression: function(node) {
        console.log(node);
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/acorn.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/acorn/5.2.1/walk.js"></script>

此节点对象将具有额外的left(左侧操作数)和right(右侧操作数)属性,这两个属性都是节点。我们只对left节点是标识符节点并且right节点是函数节点感兴趣。

笔记:

  1. 如果实际代码字符串中包含语法错误,则acorn.parse会抛出错误。因此,您可能希望将其调用包装在try-catch语句中以处理该情况,然后将结果传递给acorn.walk.simple,如果没有抛出错误的话。
  2. 如果您不想包含类型,只需从对象中删除if并仅提供所需的类型。比如说你不想包含AssignmentExpression,然后将它从传递给acorn.walk.simple的对象中删除
  3. 您可以为不同类型的函数使用不同的数组。与我的另一个答案相同:varFunctionsfunctionFunctionassignmentFunctions

我希望这是有帮助的,可以理解的。


1
投票

首先,您必须删除可能包含混淆内容的不需要的注释(请参阅下面的示例),然后删除所有新行,最后删除块注释。然后您可以匹配函数名称。有两种类型,一种是使用funcName = function声明的,另一种是使用function funcName声明的。两者都需要不同的正则表达式。

工作代码:

function getNames(text) {
  text = text.replace(/\/\/.*?\r?\n/g, "")                                 // first, remove line comments
             .replace(/\r?\n/g, " ")                                       // then remove new lines (replace them with spaces to not break the structure)
             .replace(/\/\*.*?\*\//g, "");                                 // then remove block comments
             
  // PART 1: Match functions declared using: var * = function 
  var varFuncs      = (text.match(/[$A-Z_][0-9A-Z_$]*\s*=\s*function[( ]/gi) || []) // match any valid function name that comes before \s*=\s*function
                           .map(function(tex) {                            // then extract only the function names from the matches
                             return tex.match(/^[$A-Z_][0-9A-Z_$]*/i)[0];
                           });

  // PART 2: Match functions declared using: function * 
  var functionFuncs = (text.match(/function\s+[^(]+/g) || [])              // match anything that comes after function and before (
                           .map(function(tex) {                            // then extarct only the names from the matches
                             return tex.match(/[$A-Z_][0-9A-Z_$]*$/i)[0];
                           });
  return {
    var: varFuncs,
    function: functionFuncs
  };
}


var text =
`var sampleFunc = function() {
    return 'hello';
}
/*
  function thisIsNotReallyAFunction() {} 
*/

alert(sampleFunc());
function /* undesired comment */ sampleTest() {
    var sampleTestVar = 'one';
};
var sampleFunc=
// still OK!
function() {
    return 'hello';
}
alert(sampleFunc());
function
// all sotrts of comments
sampleTest()
/* Even
 * Block ones
 */

{
    var sampleTestVar = 'one';
};
var sampleFuncEDIT = function (){};
var functionNameEDIT = "sampleFunc";
`;

var names = getNames(text);
console.log(names);

笔记:

  1. 函数名称可以包含使用上述正则表达式[$A-Z_][0-9A-Z_$]*无法匹配的各种其他unicode字符。 ECMA Specs
  2. 即使我们删除了注释,也可能有其他因素可能会混淆函数(例如字符串)。上面提供了一个简单的用例,如果您正在寻找一种先进的方法,那么您需要解析字符串,而不是使用正则表达式。

以下是函数不起作用的一些示例:

var text = "var dummyString = 'function thisShouldntBeMatchedButWillBe';"

var text = "someString = 'this /* will confuse the comment removal'";

// ...

-1
投票
var re=/(\w+)\(\)/g
// var re=/([\w_-]+)\(\)/g 
var rslt = s.match(re)

仍然保持简单。

© www.soinside.com 2019 - 2024. All rights reserved.