为什么不能直接设置console.log()为回调函数

Why I can't directly set console.log() as callback function

为什么这段代码不起作用

function callback(num, func) {
    for(var i = 0; i < num; i++) {
        func();
    }
}

callback(4, console.log("Hello"));

我知道我必须这样做:

callback(4, function() { console.log("hello"); });

但我还是不明白我为什么要那样做。

那是因为当你提到 console.log() 时,你要求 js 引擎对其求值,它确实将结果作为第二个参数传递给回调函数,而不是它期望的函数对象,并且因此,电话的行为并不如您所愿。 当您传递 function(){} 构造时,工作案例会正确传递函数对象。

如果您将 console.log('foo') 作为回调,您将调用日志 return 作为函数。

一个解决方案是将你的 console.log 放在一个函数中:

function callback(num, func) {
   for(var i = 0; i < num; i++)
      func();
}
callback(4, function(){
   console.log("Hello2")
});

因为函数是 Javascript 中的第一个 class 公民(意思是函数可以存储在变量中)。将函数作为参数传递将 return 对函数定义的引用,而不是您尝试调用的函数调用。 但是,它确实会事先评估您的 console.log 并将其作为变量传递,并且由于您的日志函数调用没有 return 任何未定义的传递给您的回调函数。

function test( logFunction ){
    logFunction( 'test' )
}

test( alert )

是这种行为的一个例子。这也是像 Remy J 所演示的那种闭包起作用的原因。

function callback(num, func) {
    for(var i = 0; i < num; i++) {
        func( "Hello" );
    }
}

callback(4, alert);

这行得通。

让我们逐步了解它以帮助您直观地了解正在发生的事情。当解释器读取您的代码时,它会从内到外计算函数表达式。也就是说:

callback(4, console.log("Hello"));
//          ^------------------^---- is evaluated before "callback" is called.

产生相同结果的更长形式的写法是:

var result = console.log("Hello");
callback(4, result);

console.log 函数没有定义的 return 值(或者至少不是标准化值)——尽管 javascript return return something - 当未指定时,此值字面意思是 undefined。因此,当您 运行 您的代码实际上是在调用:

callback(4, undefined);

这意味着在 callback 中,尝试将 func 作为函数调用将导致:

TypeError: undefined is not a function

这就是为什么您需要将您的逻辑封装在一个新的函数闭包中 - func 借此获得对可调用 Function 对象的引用。


那么如何整理我的代码?

通常在像这样的简单情况下,您为解决问题所做的工作是完全可以接受的,但是对于更复杂的逻辑,您通常会以多层嵌套回调(有时称为 "callback-soup")。为了提高代码重用常见的可读性,您可以引入一个隐藏脏东西的函数工厂,例如:

function myLogger(message){
    return function(){
        console.log(message);
    }      
}

callback(4, myLogger('hello'));