【问题标题】:Why function declarations are always moved to top during evaluation?为什么在评估期间函数声明总是移到顶部?
【发布时间】:2011-07-07 09:02:31
【问题描述】:

这实际上不是我遇到的具体问题,而是我想教育自己的一些事情:

据我了解,JavaScript 中的代码如下

if (true) {
    function greet(){ alert("Hello!"); }
} else {
    function greet(){ alert("Hi!"); }
} 
greet();

输出Hi!,因为代码实际上是这样评估的:

greet = function(){ alert("Hello!"); }
greet = function(){ alert("Hi!"); }

if(true){
    // possibly a no-op assignment, such as greet = greet?
}else{
    // same as the other branch?
} 
greet();

从语言设计的角度来看,为什么 JavaScript 会这样?

【问题讨论】:

    标签: javascript evaluation


    【解决方案1】:

    命名函数是在代码开始之前创建的,因此您不必将函数声明放在使用它的代码之前:

    x(); // this is possible because the function already exists
    
    function x() {}
    

    函数声明在哪里无关紧要,即使它在流控制结构内,它仍然是在代码开始之前创建的。因此,在您的示例中,if 语句中根本没有可执行代码,甚至没有无操作赋值。

    如果你想动态分配函数声明,你必须使用一个变量:

    var greet;
    if (true) {
      greet = function(){ alert("Hello!"); }
    } else {
      greet = function(){ alert("Hi!"); }
    } 
    greet();
    

    您不必使用匿名函数,您可以将两个命名函数之一分配给变量。

    【讨论】:

    • 这实际上不是真正的跨浏览器。 Mozilla 将if 块的内容解释为函数语句而不是函数声明,并且行为会有所不同。试试这个代码:x(); if (true) { function x() {} },你会看到它抛出了一个错误。
    • 这居然回答了这个问题? 从语言设计的角度来看,为什么 JavaScript 会以这种方式运行? 我想他知道它们正在走向顶端。或者是答案:这样你就可以在出现在代码中之前使用它们
    • @davin - 在 ECMA-262 中没有“函数语句”之类的东西。有函数声明和函数表达式(或者严格来说,FunctionDeclarationFunctionExpression - ECMA-262 10.1.1)。
    • @RobG,你是对的,也是错的。事实上,它不是标准定义的,它是标准允许的。请参阅developer.mozilla.org/en/JavaScript/Reference/… 术语“函数语句”是他们的。
    • @Felix Kling:是的,答案是首先创建函数,以便代码可以使用进一步声明的函数。例如,在 Pascal 中,只能使用在代码前面声明的东西。
    【解决方案2】:

    您可能已经发现了该语言的一个怪癖,如果您进一步观察,您还会发现不同的实现。

    ECMAScript 处理有两个阶段 - 首先,处理所有声明以在其声明范围内创建命名属性。第二,代码被执行。因此,声明的函数和变量从执行开始就在其声明的范围内可用,无论它们在代码中的何处声明。

    而那些通过赋值创建或赋值的,只有在赋值代码执行时才会有值。

    所以:

    // Call x
    x();
    
    // Declare x
    function x(){}
    

    正常工作。这通俗地称为“提升”,意思是声明被“提升”到作用域的顶部。

    但是,套用你的例子:

    if (true) {
        function x(){
            alert('true x');
        }
    } else {
        function x(){
            alert('false x');
        }
    }
    
    // In most browsers
    x(); // false x
    
    // But in Firefox
    x(); // true x
    

    对于大多数浏览器,第二个声明会覆盖第一个声明。那是因为 ECMAScript 没有块作用域,只有函数和全局作用域。

    但是,在这种情况下,语言允许扩展。 Firefox 将上述内容视为命名函数表达式,因此显示true x。更重要的是,由于其他浏览器将表达式视为声明,因此 x 可以从代码块上方调用,但在 Firefox 中则不能,您会收到错误。

    底线是,如果您想有条件地将函数分配给命名参数或变量,请明确地这样做:

    var x;
    if (true) {
        x = function (){
              alert('true x');
            };
    } else {
        x = function (){
                alert('false x');
            };
    }
    

    以便您获得一致的行为。而且你还必须记住,在代码执行之前,x 不会是一个函数,它不会被“提升”,因为它不是一个声明。

    【讨论】:

      【解决方案3】:

      首先,它并不总是以这种方式运行。在Function Expressions的情况下,函数对象只能在内部范围内使用。

      其次,当您定义一个函数时,您很可能希望在当前范围内使用它。所以很自然的,它将在当前范围内可用。考虑封闭范围(取决于函数定义位置)是全局对象或封闭函数体,而不是 if 块。

      现在,我可以将您的问题简化为以下问题:

      • 为什么要用函数名作为变量名?

        A :否则开发人员必须始终将函数分配给变量(我更喜欢)。

      【讨论】:

      • 我认为您以错误的方式减少了问题;)从语言设计的角度来看,问题是为什么将功能提升到顶部。这样做有什么好处?而且他只是在谈论函数声明......
      • @Felix Kling 我在第二段中回答了这部分问题。开发人员很可能在封闭范围内使用该函数。并认为封闭范围是全局对象或封闭函数体而不是 if 块。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-08
      • 2017-01-09
      • 2014-03-14
      • 1970-01-01
      • 2015-06-27
      • 1970-01-01
      相关资源
      最近更新 更多