Почему я не могу напрямую установить console.log() как функцию обратного вызова
Почему этот код не работает
function callback(num, func) {
for(var i = 0; i < num; i++) {
func();
}
}
callback(4, console.log("Hello"));
Я знаю, что должен сделать так:
callback(4, function() { console.log("hello"); });
Но я до сих пор не понимаю, почему я должен так поступать.
4 ответа
Давайте пройдемся по нему, чтобы помочь вам визуализировать происходящее. Когда интерпретатор читает ваш код, он оценивает выражения функции наизнанку. То есть:
callback(4, console.log("Hello"));
// ^------------------^---- is evaluated before "callback" is called.
Более длинный способ написания этого, который приводит к тому же результату:
var result = console.log("Hello");
callback(4, result);
console.log
функция не имеет определенного возвращаемого значения (или, по крайней мере, не стандартизированного) - хотя, кстати, все функции в javascript возвращают что-то - если не указано, это значение буквально undefined
, Поэтому, когда вы запускаете свой код, вы по сути звоните:
callback(4, undefined);
Это означает, что внутри callback
, пытаясь позвонить func
как функция приведет к:
Ошибка типа: undefined не является функцией
Вот почему вам нужно инкапсулировать свою логику в новом закрытии функции - посредством чего func
получает ссылку на вызываемый Function
объект.
Так как я могу привести в порядок свой код?
Как правило, в таких простых случаях, как это, то, что вы сделали для решения проблемы, вполне приемлемо, но с более сложной логикой вы часто можете получить несколько уровней вложенных обратных вызовов (иногда называемых "обратным вызовом-супом"). Чтобы улучшить читаемость, где повторное использование кода является обычным явлением, вы можете ввести фабрику функций, которая скрывает неприятности, например:
function myLogger(message){
return function(){
console.log(message);
}
}
callback(4, myLogger('hello'));
Это потому, что когда вы упомянули console.log(), вы попросили, чтобы механизм js оценил его, и он делает и передает результат как 2-й аргумент функции обратного вызова, а не объекту функции, как он ожидает, и, следовательно, вызов не ведет себя как ты хочешь. Рабочий случай правильно передает объект функции, когда вы передаете функцию function(){}.
Если вы укажете console.log('foo') в качестве обратного вызова, вы вызовете log return как функцию.
Решение состоит в том, чтобы поместить ваш console.log в функцию:
function callback(num, func) {
for(var i = 0; i < num; i++)
func();
}
callback(4, function(){
console.log("Hello2")
});
Поскольку функции являются первоклассными гражданами в Javascript (то есть функции могут храниться в переменных). передача функции в качестве аргумента вернет ссылку на определение функции, а не на вызов функции, которую вы пытаетесь вызвать. Однако он предварительно оценивает ваш console.log и передает его как переменную, а поскольку вызов функции вашего журнала ничего не возвращает, он передает неопределенную функцию обратного вызова.
function test( logFunction ){
logFunction( 'test' )
}
test( alert )
Это пример такого поведения. Это также является причиной закрытия, подобного тому, которое продемонстрировала работа Реми Дж.
function callback(num, func) {
for(var i = 0; i < num; i++) {
func( "Hello" );
}
}
callback(4, alert);
Это будет работать.