代码之家  ›  专栏  ›  技术社区  ›  We Are All Monica

JavaScript函数声明和求值顺序

  •  77
  • We Are All Monica  · 技术社区  · 15 年前

    // 1 - does not work
    (function() {
    setTimeout(someFunction1, 10);
    var someFunction1 = function() { alert('here1'); };
    })();
    
    // 2
    (function() {
    setTimeout(someFunction2, 10);
    function someFunction2() { alert('here2'); }
    })();
    
    // 3
    (function() {
    setTimeout(function() { someFunction3(); }, 10);
    var someFunction3 = function() { alert('here3'); };
    })();
    
    // 4
    (function() {
    setTimeout(function() { someFunction4(); }, 10);
    function someFunction4() { alert('here4'); }
    })();
    
    4 回复  |  直到 15 年前
        1
  •  181
  •   Scimonster    11 年前

    这既不是范围问题,也不是闭包问题。问题在于 表达

    JavaScript代码,即使是Netscape的第一个JavaScript版本和微软的第一个JavaScript副本,也是分两个阶段处理的:

    阶段1:编译-在此阶段,代码被编译成语法树(字节码或二进制代码取决于引擎)。

    函数的语法 是:

    function name (arguments) {code}
    

    但是JavaScript还允许您使用 表达 . 函数表达式的语法与函数声明类似,只是它们是在表达式上下文中编写的。表达式为:

    1. 任何右边的东西 = 标志(或 : 在对象文字上)。
    2. () .
    3. 函数的参数(这实际上已经被2覆盖)。

    表达 声明

    因此,要澄清:


    // 1
    (function() {
    setTimeout(someFunction, 10);
    var someFunction = function() { alert('here1'); };
    })();
    

    第一阶段:编译。编译器看到变量 someFunction 是这样定义的。默认情况下,所有创建的变量的值都是未定义的。注意,编译器此时还不能赋值,因为这些值可能需要解释器执行一些代码来返回要赋值的值。在这个阶段,我们还没有执行代码。

    第二阶段:执行。解释器看到您想要传递变量 某些功能 某些功能


    // 2
    (function() {
    setTimeout(someFunction, 10);
    function someFunction() { alert('here2'); }
    })();
    

    第一阶段:编译。编译器看到您正在声明一个名为someFunction的函数,因此它创建了它。

    阶段2:翻译看到你想通过 某些功能 某些功能 是其已编译的函数声明。


    // 3
    (function() {
    setTimeout(function() { someFunction(); }, 10);
    var someFunction = function() { alert('here3'); };
    })();
    

    某些功能 创造了它。和以前一样,它的值是未定义的。

    第二阶段:执行。解释器将一个匿名函数传递给setTimeout,以便稍后执行。在这个函数中,它看到您正在使用变量 某些功能 仍然没有定义。然后它看到你给 . 在这一点上 某些功能 不再是未定义的。1/100秒后,setTimeout触发并调用someFunction。因为它的值不再是未定义的,所以它可以工作。


    某些功能 被传递到setTimeout,由于它被声明,它已经存在。


    你可能想知道为什么 setTimeout(someFunction, 10) 总是 如果它们是数字或字符串或其他所有内容的引用,则按值传递。因此,setTimeout实际上并没有获取someFunction传递给它的变量(这意味着创建了一个闭包),而是只获取someFunction引用的对象(在本例中是一个函数)。这是JavaScript中用于中断闭包(例如在循环中)的最广泛的机制。

        2
  •  2
  •   Javier    15 年前

    Javascript的作用域是基于函数的,而不是严格的词法作用域。也就是说

    • Somefunction1是从封闭函数的开始定义的,但它的内容在分配之前是未定义的。

    • 在第三个示例中,当定义匿名内部闭包时,变量就存在,但直到10秒后才使用它,此时该值已被分配。

    • 第四个例子有第二和第三个原因

        3
  •  1
  •   matt b    15 年前

    因为 someFunction1 setTimeout() 被处决。

    someFunction3可能看起来类似于一个案例,但是由于您传递的是函数包装 someFunction3() 在这种情况下 一些功能3() 直到稍后才计算。

        4
  •  1
  •   Littm JessicaParker    13 年前

    这听起来像是遵循良好程序避免麻烦的一个基本案例。在使用变量和函数之前声明它们,并声明如下所示的函数:

    function name (arguments) {code}
    

    避免用var声明它们。这只是草率的,会导致问题。如果你养成了在使用前声明一切的习惯,你的大部分问题都会很快消失。当声明变量时,我会立即用一个有效的值初始化它们,以确保没有一个变量是未定义的。我还倾向于包含在函数使用全局变量之前检查其有效值的代码。这是防止错误的额外保护措施。

    所有这些工作的技术细节有点像你玩手榴弹时手榴弹的工作原理。我的简单建议是一开始不要玩手榴弹。

    附加说明:
    我做了一些实验,似乎如果你用这里描述的方式声明所有函数,它们的顺序并不重要。如果函数A使用函数B,则不必在函数A之前声明函数B。

    所以,首先声明所有函数,然后声明全局变量,最后声明其他代码。遵循这些经验法则,你就不会出错。最好将声明放在网页的头部,将其他代码放在正文中,以确保这些规则的执行。