Разница между foo() и function() { foo(); }

#javascript

#javascript

Вопрос:

Есть ли какое-либо преимущество обертывания функции анонимной функцией? Я имею в виду конкретный пример:

 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.
Я понимаю, что такая форма полезна, когда нам нужны замыкания, но здесь?

Ответ №1:

Вторая форма позволяет передавать аргументы 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.

Комментарии:

1. setTimeout(callback.bind(this, 'This works aswell'), 6000);

2. @jAndy: да, но не в старых браузерах без прокладки. Спасибо, что не предложили setTimeout(callback, 6000, 'This works as well') , хотя 😉

3. @AndyE что вы имеете в виду, что это не работает в старых браузерах? у них нет apply in Function.prototype ?

4. @AndyE: lmao, даже не знал, что setTimeout curry — это аргументы функции обратного вызова. Я думаю, это не стандартно, если вы прокомментируете это так, как вы это сделали?

5. @Esailija: apply недостаточно для этой цели. Мы не хотим, чтобы обратный вызов выполнялся немедленно, но мы хотим привязать контекст и аргументы для вызова на более позднем этапе. Это bind() довольно ново.

Ответ №2:

Обертывание функции в анонимную функцию может избежать осложнений с 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);
}
  

Ответ №3:

Перенос полезен, если вам нужно иметь отдельный идентификатор.

 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. 1, это один из тех крайних случаев, о которых большинство не задумывается. Черт возьми, это еще один сценарий, в котором Function.prototype.bind() может быть полезно.