foo()和function() { foo(); }之间的区别是什么?

8
有没有将函数包装在匿名函数中的优势?我的意思是一个特定的例子:
function asyncFuntion(callback) {
    setTimeout(callback, 6000);
};

asyncFuntion(function() {
    console.log('Calling after 6 s.');
});   

并且使用封装的函数:

function asyncFuntion(callback) {
    setTimeout(function() {
        callback();
    }, 6000);
};

asyncFuntion(function() {
    console.log('Calling after 6 s.');
});

在这两种情况下输出结果是相同的。那么它们有什么区别呢? 第二个版本是我学习js时发现的。 我意识到当我们需要闭包时,这样的形式是有用的,但在这里呢?

3个回答

11
第二种形式允许你向callback传递参数,而第一种形式则不行。
// 1st form
setTimeout(callback("This doesn't work as you might expect"), 6000);

// 2nd form
setTimeout(function() {
    callback("This works");
}, 6000);

如果你没有传递参数,那么在任何情况下都没有包装函数的优势。
更加彻底的说法是,Function.prototype.bind 可以帮助我们使用第一种形式:
setTimeout(callback.bind(this, "This works fine too"), 6000); 

// Even with Richard JP Le Guen's example by specifying the thisObj
setTimeout(customObj.alert.bind(customObj), 6000);

然而,您需要为不支持此事件的浏览器(即Opera、Safari和IE 8、7、6)提供此方法。可以在MDN文档页面上获取用于模拟该方法的代码。

2
setTimeout(callback.bind(this, 'This works aswell'), 6000); - jAndy
1
@jAndy:没错,但是在没有 shim 的旧浏览器中不行。感谢您没有建议使用 setTimeout(callback, 6000, 'This works as well') ;) - Andy E
1
@Esailija:apply 对于那个目的不够用。我们不希望回调立即执行,但是我们想要在稍后的某个时间点绑定上下文和参数进行调用。因此,bind() 是相对较新的。 - jAndy
@jAndy:是的,这是非标准的。IE接受不同的第三个参数——用于评估代码的脚本语言(在字符串第一个参数的情况下)。 - Andy E
@jAndy 我知道 apply 不同,我的意思是如果老版本的浏览器有 apply,那么创建一个 bind 方法是如此微不足道,以至于我不会称其为 "无需 shim" :P。 - Esailija
显示剩余7条评论

5

将一个函数包装在匿名函数中可以避免与this关键字的复杂性。(在quirksmode上阅读有关它们的内容)

例如:

function CustomObject() {
    this.msg = "Hello world from my custom object";
    this.alert = function() {
        alert(this.msg);
    };
}

var customObj = new CustomObject();

setTimeout(customObj.alert, 1000); // the alert message says `undefined`
setTimeout(function() {
    customObj.alert();
}, 2000); // the alert message says "Hello world from my custom object"

在JavaScript中,将函数包装在匿名函数中也是使用闭包的关键:

var arr = ['a','b','c','d','e'];

// will always alert undefined
for(var i=0; i<arr.length; i++) {
    setTimeout(function() {
        console.log(arr[i]);
    }, 1000*i);
}

// outputs the values of `arr`
for(var j=0; j<arr.length; j++) {
    setTimeout((function(indx) {
        return function() {
            console.log(arr[indx]);
        }
    }(j)), 1000*j);
}

5

如果你需要拥有独立的身份,包装是很有用的。

var x = function () { cleanup(); };
var y = function () { cleanup(); };
if (x === y) ... // not true

例如,一些函数如addEventListener基于身份进行操作。
element.addEventListener("myEvent", beep, false);
element.addEventListener("myEvent", beep, false);

第二次调用addEventListener时,它会说:“我已经有一个beep了;我不需要再添加一个。”当myEvent事件被触发时,你只会听到一声哔哔声。如果你想要两个哔哔声,你需要确保回调函数是不同的。
element.addEventListener("myEvent", function() { beep(); }, false);
element.addEventListener("myEvent", function() { beep(); }, false);

每个匿名函数都是不同的,因此这次您注册了两个函数(它们恰好执行相同的操作)。现在它将会响铃两次。

+1,这是大多数人不考虑的一种边缘情况。顺便说一下,这是另一种可以使用Function.prototype.bind()的情况。 - Andy E

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