关于javascript:当一个变量等于一个函数时,它意味着什么?

What does it mean when a variable equals a function?

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
JavaScript: var functionName = function() {} vs function functionName() {}

在JavaScript中,将变量定义为函数的目的是什么?我以前看过这个会议,但不完全理解。

例如,在脚本中的某个点上,函数的调用方式如下:

whatever();

但我希望看到一个名为whatever的函数,如下所示:

1
2
3
function whatever(){

}

相反,我将看到一个名为whatever的变量,它被定义为一个函数,如下所示:

1
2
3
var whatever = function(){

}

这样做的目的是什么?为什么要这样做而不只是命名函数?


注意:请参阅答案末尾的更新,块内的声明变为有效(但如果不使用严格模式,则相当复杂)。

原因之一是:

1
2
3
4
5
6
7
8
9
10
11
12
13
var whatever;

if (some_condition) {
    whatever = function() {
        // Do something
    };
}
else {
    whatever = function() {
        // Do something else
    };
}
whatever();

在必须处理实现差异的库的初始化过程中,您可能会看到类似这样的代码(例如Web浏览器之间的差异、a'la ie的attachEvent与标准的addEventListener之间的差异)。不能对函数声明执行等效操作:

1
2
3
4
5
6
7
8
9
10
11
if (some_condition) {
    function whatever() {    // <=== DON'T DO THIS
        // Do something
    }
}
else {
    function whatever() {    // <=== IT'S INVALID
        // Do something else
    }
}
whatever();

…它们没有在控制结构中指定,因此允许JavaScript引擎执行它们想要的操作,不同的引擎执行不同的操作。(编辑:同样,请参见下面的注释,它们现在已被指定。)

另外,两者之间有很大的区别

1
2
3
var whatever = function() {
    // ...
};

1
2
3
function whatever() {
    // ...
}

第一个是函数表达式,当代码在上下文的逐步执行中到达该点时(例如,它所在的函数,或全局代码的逐步执行)对其进行计算。它还产生一个匿名函数(引用它的变量有一个名称,但函数没有,这对帮助您的工具帮助您有意义)。

第二个是函数声明,它是在进入上下文时在执行任何分步代码之前进行评估的。(有些人称之为"提升",因为源代码中更深层的内容比源代码中更高层的内容发生得更早。)该函数也有一个适当的名称。

所以考虑一下:

1
2
3
4
5
6
7
8
function foo() {
    doSomething();
    doSomethingElse();
    console.log("typeof bar =" + typeof bar); // Logs"function"

    function bar() {
    }
}

反之

1
2
3
4
5
6
7
8
function foo() {
    doSomething();
    doSomethingElse();
    console.log("typeof bar =" + typeof bar); // Logs"undefined"

    var bar = function() {
    };
}

在第一个示例中,使用声明,在运行doSomething和其他逐步代码之前处理声明。在第二个例子中,因为它是一个表达式,所以它是作为逐步代码的一部分执行的,所以函数没有在上面定义(变量在上面定义,因为var也被"提升")。

结束:目前,您不能在一般的客户端Web内容中这样做:

1
2
3
var bar = function foo() { // <=== Don't do this in client-side code for now
    // ...
};

您应该能够做到这一点,它被称为一个命名函数表达式,它是一个为函数提供正确名称的函数表达式。但各种各样的javascript引擎在不同的时间都犯了错误,IE直到最近才继续犯了错误。

ES2015更新+

截至ES2015(又称"ES6"),块内的函数声明已添加到规范中。

严格模式

在严格模式下,新指定的行为简单易懂:它们的作用域是发生它们的块,并被提升到其顶部。

因此:

1
2
3
4
5
6
7
8
9
10
11
12
13
"use strict";
if (Math.random() < 0.5) {
  foo();
  function foo() {
    console.log("low");
  }
} else {
  foo();
  function foo() {
    console.log("high");
  }
}
console.log(typeof foo); // undefined

(注意对函数的调用如何位于块内函数的上方。)

…基本上等同于:

1
2
3
4
5
6
7
8
9
10
11
12
13
"use strict";
if (Math.random() < 0.5) {
  let foo = function() {
    console.log("low");
  };
  foo();
} else {
  let foo = function() {
    console.log("high");
  };
  foo();
}
console.log(typeof foo); // undefined

松散模式

松散模式行为更为复杂,而且理论上,它在Web浏览器中的javascript引擎和不在Web浏览器中的javascript引擎之间存在差异。我不会在这里谈的。别这么做。如果您坚持在块中声明函数,请使用严格的模式,在这种模式下,它们是有意义的,并且在不同的环境中是一致的。


这样您就可以将函数存储在变量中,例如将它们作为参数传递给其他函数。一个很有用的例子是编写异步函数,这些函数作为参数传递回调。

1
2
3
4
5
6
7
8
var callback = function() { console.log('done', result)}

var dosomething = function(callback) {
    //do some stuff here
    ...
    result = 1;
    callback(result);
}

因为函数是JavaScript中的对象,所以您也可以使用属性和方法来扩展它们。


当你将一个函数赋给一个变量时,你可以把它作为一个参数传递给其他函数,也可以扩展它来利用javascript的对象模型。


如果在函数中使用"var"声明函数变量,则只能在该函数中访问该变量。当您退出函数时,变量将被销毁。这些变量称为局部变量。您可以在不同的函数中使用相同名称的局部变量,因为每个变量只能由声明它的函数识别。


javascript中的函数是对象;换句话说,它们是值。因此,无论函数是如何定义的,都可以将变量设置为引用函数:

1
2
3
4
function foo() { ... }

var anotherFoo = foo;
anotherFoo(); // calls foo

函数是可以用作对象属性、函数参数、数组元素以及一般值在JavaScript中可以执行的任何其他操作的值。它们是对象,也可以有自己的属性。