Почему я не могу напрямую установить 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);

Это будет работать.

Другие вопросы по тегам