190

即使在不同的浏览器中,此代码也始终有效:

function fooCheck() {
  alert(internalFoo()); // We are using internalFoo() here...

  return internalFoo(); // And here, even though it has not been defined...

  function internalFoo() { return true; } //...until here!
}

fooCheck();

不过,我找不到一个关于为什么它应该起作用的单一参考。我首先在 John Resig 的演示文稿中看到了这一点,但只是提到了这一点。那里或任何地方都没有任何解释。

有人可以启发我吗?

4

7 回答 7

238

function声明很神奇,并导致其标识符在其代码块*中的任何内容被执行之前被绑定。

这与带有function表达式的赋值不同,后者以正常的自上而下的顺序进行评估。

如果您将示例更改为:

var internalFoo = function() { return true; };

它会停止工作。

函数声明在语法上与函数表达式完全不同,尽管它们看起来几乎相同并且在某些情况下可能会模棱两可。

这在ECMAScript 标准10.1.3节中有记录。不幸的是,即使按照标准,ECMA-262 也不是一个可读性很强的文档!

*:包含函数、块、模块或脚本。

于 2008-11-04T12:13:30.430 回答
47

它被称为 HOISTING - 在定义之前调用(调用)一个函数。

我想写的两种不同类型的函数是:

表达式函数和声明函数

  1. 表达式函数:

    函数表达式可以存储在变量中,因此它们不需要函数名称。它们也将被命名为匿名函数(没有名称的函数)。

    要调用(调用)这些函数,它们总是需要一个变量名。如果在定义之前调用这种函数将不起作用,这意味着此处不会发生提升。我们必须始终先定义表达式函数,然后再调用它。

    let lastName = function (family) {
     console.log("My last name is " + family);
    };
    let x = lastName("Lopez");
    

    这就是您可以在 ECMAScript 6 中编写它的方式:

    lastName = (family) => console.log("My last name is " + family);
    
    x = lastName("Lopez");
    
  2. 声明函数:

    使用以下语法声明的函数不会立即执行。它们被“保存以供以后使用”并且将在稍后被调用(调用)时执行。如果您在定义了 is 的位置之前或之后调用它,这种类型的函数就会起作用。如果在定义之前调用声明函数,则提升工作正常。

    function Name(name) {
      console.log("My cat's name is " + name);
    }
    Name("Chloe");
    

    吊装示例:

    Name("Chloe");
    function Name(name) {
       console.log("My cat's name is " + name);
    }
    
于 2017-02-16T06:32:57.260 回答
14

浏览器从头到尾读取您的 HTML,并在读取并解析为可执行块(变量声明、函数定义等)时执行它。但在任何时候都只能使用在此之前脚本中定义的内容。

这与处理(编译)所有源代码的其他编程上下文不同,可能会将其与解析引用所需的任何库链接在一起,并构造一个可执行模块,然后开始执行。

您的代码可以引用进一步定义的命名对象(变量、其他函数等),但在所有部分都可用之前,您不能执行引用代码。

随着您对 JavaScript 的熟悉,您将清楚地意识到您需要以正确的顺序编写内容。

修订:要确认接受的答案(上图),请使用 Firebug 单步执行网页的脚本部分。你会看到它从一个函数跳到另一个函数,在它实际执行任何代码之前只访问第一行。

于 2008-11-04T11:58:23.563 回答
4

某些语言要求必须在使用前定义标识符。这样做的一个原因是编译器对源代码使用单次传递。

但是,如果有多次通过(或某些检查被推迟),你可以完全没有这个要求。在这种情况下,可能首先读取(和解释)代码,然后设置链接。

于 2008-11-04T11:47:21.840 回答
1

我只使用了一点 JavaScript。我不确定这是否会有所帮助,但它看起来与您正在谈论的内容非常相似,并且可能会提供一些见解:

http://www.dustindiaz.com/javascript-function-declaration-ambiguity/

于 2008-11-04T11:41:50.867 回答
1

函数“internalFoo”的主体在解析时需要去某个地方,因此当 JS 解释器读取(也称为解析)代码时,会创建函数的数据结构并分配名称。

只是稍后,然后代码运行,JavaScript实际上试图找出“internalFoo”是否存在以及它是什么以及它是否可以被调用等。

于 2008-11-04T11:52:31.783 回答
-4

出于同样的原因,以下内容将始终放在foo全局命名空间中:

if (test condition) {
    var foo;
}
于 2008-11-04T17:38:23.690 回答