一、背景:

在JavaScript中,将一个函数作为参数传递给另外一个函数,然后在那个函数中调用传递过来的函数,是非常简单和直接的。如:

function sayHello() {
    alert("Hello");
}

function invoker(functionName) { functionName(); }

invoker(sayHello);

点击这里运行

在上例中,通过将函数名sayHello传递给invoker()函数,然后在invoker()函数体中直接调用传进去的函数。执行结果如下:

image

二、问题:

上例中,sayHello() 函数是不带参数的,所以比较简单。但是,如果我们改写sayHello()函数,让它变得更灵活些,接收两个参数,那么新的invoker()函数应该怎样写才能适应这种情况呢?即,如何才能够动态地调用任何函数呢?不管被调用的函数带不带参数,也不管它究竟需要多少个参数,我们希望都能够以统一的接口来调用。

三、解决方案:

利用JavaScript中Function类的apply()函数,以及结合使用arguments对象,来解决这个问题:

function say(msgTitle, msgBody) {
    alert(msgTitle + "\r\n" + msgBody);
}

function invoker(func) {
    var args = Array.prototype.slice.call(arguments);
    args.splice(0, 1);
    func.apply(window, args);
}

invoker(say, "hello", "world");

点击这里运行

以上代码执行结果如下:

image

注意,上述代码中,有 var args = Array.prototype.slice.call(arguments) 语句,是为了将 arguments 对象转换成一个真正的 Array 对象的实例。

四、更多说明:

以上的invoker()函数,真正实现了灵活的动态调用。它接受的参数是,要被调用的函数名,然后是要被调用的函数的参数列表,这个参数列表有没有,个数是多少,都没有做限制,完全根据要被调用的函数的需求来。

更多例子:

function compute(a, b, c) {
    alert(a + b + c);
}

invoker(compute, 1, 2, 3);

invoker(function (s) { alert(s.split("\r\n").length); }, "FirstLine\r\nSecondLine\r\nThirdLine");

点击这里运行

最后一个例子,演示了可以直接传递匿名函数参数。