带/不带括号的Javascript函数调用

3

代码_0:

(没有使用圆括号调用foo)

function foo(){
    console.log('hello world');
}

setTimeout(foo, 2000);

这是如何执行code_0的过程:
start -> wait for 2 seconds -> 'hello world' displayed -> end

代码_1:

(使用括号调用 foo)

function foo(){
    console.log('hello world');
}

setTimeout(foo(), 2000);

以下是code_1的执行过程:

start -> 'hello world' displayed immediately -> wait for 2 seconds -> end

为什么在调用函数时加上括号后程序表现会不同?这背后的机制是什么?
抱歉,如果这个问题太简单了。但我在任何初学者的javascript教程中都没有找到解释。
6个回答

5

setTimeout(foo, 2000)将函数foo和数值2000作为参数传递给setTimeout。而setTimeout(foo(), 2000)则会调用foo并将其返回值和数值2000一起传递给setTimeout

在第一个例子中,你只是像其他值一样将函数作为参数传递,而没有实际调用该函数。

举个更简单的例子,只需记录它:

function foo() {
    return 5;
}

console.log(foo);   // console.log is passed a function and prints [Function]

console.log(foo()); // foo() is called and returns 5; console.log is passed 5
                    // and prints 5

1
那么,如果函数foo有一个参数怎么办?像这样:function foo(arg){ console.log('hello' + arg); }我如何在使用setTimeout时将参数'world'传递给foo函数呢? - delsin
1
@Delsin:你可以传递一个调用该函数的函数。例如:setTimeout(function () { foo('some argument'); }, 2000);。还有一个内置函数bind,它可以设置this和一些前置参数:setTimeout(foo.bind(null, 'some argument'), 2000); - Ry-
1
@RyanO'Hara - 这样做有点复杂,是吧?在延迟参数之后,你提供的任何额外参数都会被发送到函数中。例如:setTimeout(foo, 2000, myArg1, myArg2, myArg3);为什么不直接这样做呢? - Jimbo Jonny
@RyanO'Hara - 没事了,我自己回答了我的问题,因为我记得很多IE版本不支持那个。但公平地说,IE对绑定的支持也不是很好。 - Jimbo Jonny
@JimboJonny:我不想把答案局限于一个特定的用法。 - Ry-

2
在第一个代码片段中,函数foo被传递到超时函数中。这意味着当超时到期后,foo将在2秒后被调用。
在第二个代码片段中,函数foo被调用以决定传递给超时函数的内容。因此,在设置超时之前就会调用foo。由于foo()没有返回任何内容,所以当超时到期时什么都不会发生。

2
在你的问题中,foo是什么意思?
function foo(){
    console.log('hello world');
}

foo()

console.log(hello world)

setTimeout() 方法在指定的毫秒数后调用函数或评估表达式,这个函数应该是它的第一个参数,在你的第一个例子中,你传递了一个函数,所以行为是预期的,在第二个例子中,你传递了一个不是函数的 console.log(...),所以它先执行了 foo() 并在控制台打印了 hello world 然后等待 2 秒钟什么都不做,因此显示出奇怪的行为。

参见

typeof foo; // 是函数

typeof foo(); // 先在控制台打印 hello world,然后返回 undefined。


2
< p > foofoo()之间的基本区别如下:

function foo() {
   alert("bar");
   return "baz";
}

console.log(foo); // gives "function"
console.log(foo());  // gives "baz"

foo 是指函数本身的引用,而 foo() 则是执行该函数。因此

setTimeout(foo(), 2000);

将返回值("baz")传递给函数setTimeout(这将导致错误)。setTimeout期望第一个参数是“可执行”函数,因此您需要传递对现有函数的引用。


1

这里的混淆点在于您没有意识到,使用不带括号的foo并不是在调用函数...它只是引用它。

foo 是对函数本身的引用。要调用函数(即实际执行其代码),您需要在末尾加上括号引用它(例如foo())。从实际角度来看,这意味着:

// references the function itself
// ...you could call one() now and it would run the function foo
var one = foo;

// runs/calls the function, and var two will then be whatever foo returns
var two = foo();

在第一个例子中,你没有调用函数,而是将函数foo传递给setTimeoutsetTimeout会在延迟后调用它。
在第二个例子中,你立即调用了foo并将其返回值传递给setTimeout(由于该返回值很可能不是另一个函数,因此setTimeout无法处理它)。 因此,在setTimeout完成延迟后,你的函数立即被执行,然后什么也不会发生(除了可能出现一些错误)。

0
以下是一个简单的解决方案。如果需要,您可以使用括号并将数据传递给函数。

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