为啥我可以在 JavaScript 中定义函数之前使用它?

Posted

技术标签:

【中文标题】为啥我可以在 JavaScript 中定义函数之前使用它?【英文标题】:Why can I use a function before it's defined in JavaScript?为什么我可以在 JavaScript 中定义函数之前使用它? 【发布时间】:2010-09-20 16:02:43 【问题描述】:

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

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 的演示文稿中看到了这一点,但它只是被提及。那里或任何地方都没有任何解释。

有人可以告诉我吗?

【问题讨论】:

在较新版本的 firefox 中,如果代码在 try/catch 中,这将不起作用。看到这个小提琴:jsfiddle.net/qzzc1evt 【参考方案1】:

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

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

表达式函数和声明函数

    表达式函数:

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

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

    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");
    

    声明函数:

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

    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);
    
    

【讨论】:

let fun = theFunction; fun(); function theFunction() 也可以工作(节点和浏览器) 提升实际上是“在定义之前调用函数”吗?提升已声明名称的范围,以便为该范围内的其他代码定义它——这大致就是 javascript 所做的,它允许您在声明之前调用函数,这就是提升。【参考方案2】:

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

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

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

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

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

【讨论】:

【参考方案3】:

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

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

【讨论】:

链接不再失效。 链接已失效。 这是来自archive.org 的snapshot。看起来作者took down他们的整个网站都有过时的内容,而不是这篇博文属于这一类。【参考方案4】:

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

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

如果您将示例更改为:

var internalFoo = function()  return true; ;

它会停止工作。

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

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

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

【讨论】:

我猜它真的不可读。我刚刚阅读了您指出的 10.1.3 部分,但不明白为什么那里的规定会导致这种行为。谢谢你的信息。 @bobince 好吧,当我在此页面上找不到任何提及“提升”一词时,我开始怀疑自己。希望这些 cmets 有足够的 Google Juice™ 来解决问题:) 这是一个流行的问答组合。考虑使用 ES5 注释规范的链接/摘录进行更新。 (这更容易获得。) 这篇文章有一些例子:JavaScript-Scoping-and-Hoisting 我发现很多库在定义之前就使用了这个函数,甚至有些语言官方允许它,例如。哈斯克尔。老实说,这可能不是一件坏事,因为在某些情况下你可以写得更有表现力。【参考方案5】:

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

if (test condition) 
    var foo;

【讨论】:

实际上,这是出于非常不同的原因。 if 块不创建范围,而 function() 块总是创建一个。真正的原因是全局 javascript 名称的定义发生在编译阶段,因此即使代码没有运行,名称也会被定义。 (抱歉这么久才评论)【参考方案6】:

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

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

【讨论】:

【参考方案7】:

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

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

【讨论】:

以上是关于为啥我可以在 JavaScript 中定义函数之前使用它?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的函数在严格模式(函数形式)下未定义?

为啥这个异步函数在它之前定义的等效 Promise.then 链之前执行?

c ++:为啥执行回调但回调定义之前的函数没有?

C语言问题,宏定义中的参数为啥要定义,不是主函数中的参数直接替换吗?

为啥我的script函数不执行?

JavaScript 函数定义