Javascript:给出的函数为空?

时间:2011-10-31 22:07:21

标签: javascript

我们有一个函数调用

function doSomethingAndInvokeCallback(callback){
    // do something
    callback();
}

我可以检查给定的参数是否为函数if(typeof callback == 'function')

如果给定的回调函数是函数且不为空,我该如何发现?

doSomethingAndInvokeCallback(function(){
    //nothing here
})

7 个答案:

答案 0 :(得分:7)

没有完全可靠的方法来知道函数是否为空,因为JS中有多种函数,一些用JS实现,一些用本机代码实现,你无法确定传入的函数是否有什么与否。如果要将传入函数限制为仅非常简单的JS函数,可以使用其他答案中概述的机制(检查函数的来源)。但是,除了严格控制的情况之外,我不建议这样做,因为有很多合法的javascript方法可以打破这种情况。

我建议您应该更改函数参数的约定,并让调用者传递null或不传递任何内容(将生成参数undefined)而不是空函数。然后,很清楚他们是否打算有一个被调用的函数。如果它们然后传递一个空函数而不是null或undefined,它们将获得函数接口指定的行为。调用者可以选择所需的行为,您可以以更安全的方式实现您的功能。

另外,你提出的一个主要假设是不对的。您无法安全地使用typeof x == "function"来确定某些函数是否在某些类型的函数中在某些旧版本的IE中无法可靠地运行。如果你想学习如何检测某些东西是否是一个函数,你可以在这里学习jQuery(即使你没有使用它)。 jQuery有一个函数,它一直在内部使用,称为jQuery.isFunction(),返回一个bool。它主要用于测试参数以查看是否传递了函数。

在内部,它调用:

Object.prototype.toString.call(o)

然后检查结果。如果结果中包含“Function”,则测试参数是一个函数。

因此,使用jQuery中使用的相同技术,您可以构建自己的简单小isFunction例程,如下所示:

function isFunction(test) {
    return(Object.prototype.toString.call(test).indexOf("Function") > -1);
}

当然,如果你有jQuery可用,你可以使用它自己的版本:

jQuery.isFunction(o)

如果对潜在的跨浏览器兼容性问题存在疑问,我会发现它有助于了解其中一个大型库如何解决问题,即使您不打算使用该库也是如此。您可以确定这些库已针对许多浏览器进行了审查,因此他们使用的技术是安全的。你有时必须解开他们可能用来弄清楚他们真正在做什么的所有内部例程(这个函数就是这种情况),但是你可以为自己节省大量的工作。

你可以在这里看到一个工作试验台:http://jsfiddle.net/jfriend00/PKcsM/

在现代浏览器typeof fn === "function"中,但在较旧版本的IE中,某些函数会提供typeof === "object",这可能是jQuery使用其他方法的原因,这种方法适用于那些旧版本的IE。

答案 1 :(得分:6)

似乎您可以定义一个函数来检索函数体(1)。我写了一个小的(非确定的)测试:

http://jsfiddle.net/6qn5P/

Function.prototype.getBody =
  function() {
    // Get content between first { and last }
    var m = this.toString().match(/\{([\s\S]*)\}/m)[1];
    // Strip comments
    return m.replace(/^\s*\/\/.*$/mg,'');
  };

function foo() {
    var a = 1, b = "bar";
    alert(b + a);
    return null;
}

console.log(foo.getBody());
console.log(foo.getBody().length);

答案 2 :(得分:4)

一种可能性是将.toString结果与regexp匹配以获取函数体,然后修剪以检查它是否已成为空字符串:

var f = function foo()    { 


};

/^function [^(]*\(\)[ ]*{(.*)}$/.exec(
     f.toString().replace(/\n/g, "")
)[1].trim() === ""; // true

丑陋的正则表达式确实在名称和左括号之前处理了命名函数以及无关空间的空格。似乎删除了foo ()中的空格,因此没有理由检查这些空格。

答案 3 :(得分:2)

可能能够从.toString()

获取此信息
var blank = function(){};
var f = function(){};
var f2 = function() { return 1; };

f.toString() == blank.toString(); // true
f2.toString() == blank.toString(); // false

但这确实容易出错:

var blank = function(){};
var f = function(){ }; // extra space!
f.toString() == blank.toString(); // false

你可以尝试克服这个问题,但我怀疑这是依赖于浏览器的。如果我是你,我实际上不会尝试在生产环境中这样做。即使你对空白进行规范化,它仍然不会捕获其他的无操作行,包括注释,无用的var语句等。要实际解决这些问题,你可能需要一个完整的标记化系统(或者疯狂正则表达。)

答案 4 :(得分:2)

你无法为主持人功能做到这一点,但对于其他人来说,你可以相当可靠地做到

function isEmpty(f) {
  return typeof f === "function" &&
      /^function[^{]*[{]\s*[}]\s*$/.test(
          Function.prototype.toString.call(f));
}

这样效率不高,但是主要的解释器以这样的方式实现toString函数,虽然它对一些空函数的某些解释器不起作用

function () { /* nothing here */ }
function () { ; }
function () { return; }

答案 5 :(得分:1)

在某些实现中,您可以在函数上执行toString()并获取其内容。虽然它包含评论等。

var foo = function(){ /* Comment */ };
alert(foo.toString());

答案 6 :(得分:-1)

正如我在另一篇duplicated question中所说的那样:

你可以尝试的最好的方法,就是要适应最大的可能性(因为这很难实现),就是添加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  }");
function hasOnlyVariables() {var a, b = 2; var c = 1 + b;}
check(hasOnlyVariables);
<script src="https://cdnjs.cloudflare.com/ajax/libs/esprima/2.7.3/esprima.min.js"></script>

这不会运行函数,只需解析它们,因此使用非安全函数运行是安全的。