如何确定一个函数是否为空

28

我希望能够检查一个给定的函数是否为空。也就是说,它的函数体中没有任何内容,例如:

function foo() {}
function iAmEmpty(a) {
    // yep, empty
}

经过一些尝试,我用 toString() 和一些正则表达式得到了一些可能可以的东西。

function foo(a, b, c) {}

/^function[^{]+\{\s*\}/m.test(foo.toString());  // true

function bar(a, b, c) { var d; }

/^function[^{]+\{\s*\}/m.test(bar.toString());  // false

我想知道是否有更好的方法?你能看到上面的代码存在任何问题吗?


1
JS反射的概念让我笑了,但至于这个问题;你已经处理了空格和多行,所以据我所知,这是最好的解决方案。 - annakata
1
不要忘记查找所有注释的方法体(如果这是你的规范) - Brad
我认为这是一个不错的做法。 - WaiLam
2
@Brad,据我所知,从toString()的输出中自动删除了注释。 - nickf
给定函数为空的JavaScript副本 - Fabien
可能是Javascript:给定函数是否为空?的重复。 - Jorge Fuentes González
6个回答

14

这并不是一个好建议。没有明确规定一个函数的 toString() 方法应该返回什么,所以即使您在当前浏览器中实现了这个方法,未来的浏览器可能会有理由更改其实现并破坏您的代码。

Kangax 简要介绍了这个问题:http://perfectionkills.com/those-tricky-functions/


谢谢提供链接!我没有意识到浏览器实现的差异如此之大。 - nickf
@ManasJayanth:关键词是“精确”。两个规范都说返回函数的“实现相关表示”。 - Tim Down
哦,对了。我的错 :) 但是在 Mac 上运行几个简单的 toStrings 函数似乎在浏览器(FF、Safari 和 Chrome)之间返回一致的结果,非本地函数。 - user2065736

8

箭头函数...

相信您已经知道,javascript支持箭头函数,它们非常简洁,但不幸的是无法与您的整洁的regex一起使用。

我迅速将您漂亮的regex转换为自己的function,该函数以function作为输入并返回它是否为空,以便以后更加简单。为了演示箭头函数可以广泛使用,我将其放在了一个箭头函数中:

isEmpty = f => /^function[^{]+\{\s*\}/m.test(f.toString())

现在,我们可以轻松地测试一个空函数:
function eF() {}

正如我们预期的那样,使用 isEmpty(eF) 返回 true

再来看一个实际函数的例子:

function retOne() {return 1;}

如预期一样,isEmpty(retOne)返回false

然而,我遇到的问题是与箭头函数有关,因此要再次初始化一个空的箭头函数,我们有一个更短的语法:

eF = () => {}

而该字符串化版本与之前的版本相差甚远:'stringified'

"() => {}"

当然,在这种情况下调用isEmpty(eF)将返回false,而我们希望返回true。我不确定您是否需要测试所有函数(即包括箭头函数)是否为空,但如果需要,您的正则表达式需要进行修改...
我不擅长编写它们,但已经尝试过一些方法,您可能需要考虑箭头函数的宽容性质,特别是文档中的这部分内容:
(param1, param2,, paramN) => { statements }
(param1, param2,, paramN) => expression
// equivalent to: (param1, param2,, paramN) => { return expression; }

// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }

这里展示了花括号{...}并不总是必需的,因此这个函数:
retOne = () => 1

有效的,这可能会使形成新的正则表达式更加困难。我想到的一个解决方法是使用以下代码从f.toString()中删除所有花括号:

str.replace(/[{}]/g, '')

然后从那里使用正则表达式测试

希望这对于您希望箭头函数也能够被测试时有所帮助。


4

如果您想尽可能地实现最大的可能性(因为这很难实现),最好的方法是添加acornesprima也适用于箭头函数)库并处理JavaScript函数。它将对其进行标记化以供解析,因此您可以根据自己的喜好进行处理,检查其中是否实际上没有代码,或者仅有变量声明而没有任何计算或返回等...

实现起来非常简单:

function check(f) {
  console.log("");
  console.log("Checking", f);
  var syntax = esprima.parse(f);
  if (syntax.body.length != 1) {
    console.log("Weird process");
  } else {
    function processBody(body) {
      if (!body || body.length == 0) {
        console.log("Body seems empty. YAY!");
      } else {
        for (var i = 0, command; command = body[i]; i++) {
          if (command.type != "VariableDeclaration") {
            console.log("Body has a", command.type, ", so is not empty.");
            return;
          }
        }
        console.log("Body has only variable declarations, so is empty! (or not, whatever fit your needs)");
      }
    }
    function process(dec) {
      if (dec.type != "FunctionDeclaration") {
        console.log("Weird declaration");
      } else {
        console.log("Function", dec.id.name, "has", dec.params.length, "params");
        processBody(dec.body.body);
      }
    }
    process(syntax.body[0]);
  }
}

check("function hasReturn(arg1, arg2) {var a = arg1 + arg2;return a;}");
check("function hasArgumentsButNoBody(arg1, arg2) {}");
check("function isCompletelyEmptyWithSpacesAndTabsAndLinefeeds() {   \t    \t   \r\n \r\n  }");
check("function hasOnlyVariables() {var a, b = 2; var c = 1 + b;}");
<script src="https://cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.min.js"></script>

这将不会运行函数,只是解析它们,因此可以安全地运行非安全函数。

1

我不认为这有什么用,但你可以通过将模式锚定到字符串的结尾来简化它。

  /[^{\s]\s*\}$/.test(String(bar))

1
Function.prototype.hasBody = function() {
    return !/{\s*}$/.test(this.toString());
};

0

很简单,只需检查函数包含并检查包含是否为空。
查看Plunker
这是完整的工作代码:

function foo(a, b, c) {}

function bar(a, b, c) {
  var d;
}

function isEmpty(value) {
  return (value == null || value.length === 0);
}

function check(test) {
  var entire = test.toString();
  var body = entire.slice(entire.indexOf("{") + 1, entire.lastIndexOf("}"));
  return body;

}
console.log(isEmpty(check(foo)), isEmpty(check(bar))); //return true false

如果函数覆盖了 toString 方法,则此方法无效。 - Alexis Tyler
@AlexisTyler,请检查一下 Plunker。 - Neji Soltani
@AlexisTyler,你的问题中提到了覆盖(overriding)的内容。无论如何,如果你想要覆盖默认的toString函数,你可以将其保存在另一个函数中。 - Neji Soltani

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接