2017-05-07 59 views
1

据我所知,Javascript不编译,它只运行。所以应该没有编译时错误,只有运行时错误。那么为什么这个代码不工作?运行一个未定义参考的函数

function show() { console.log(x); } 
(function() { 
    var x = 42; 
    show(); 
})() 

我的问题不在于如何使此代码更好;我意识到这是不好的代码,我已经知道如何解决它(见下文)。

我的问题是,为什么我会得到一个未捕获ReferenceError?如果Javascript只在运行时抛出错误,它应该知道x == 42在它调用show()的时候,它在匿名函数中,是否正确?


工作代码:

(function() { 
    var x = 42; 
    function show() { console.log(x); } 
    show(); 
})() 

工作的代码,最好的选择:在JS

function show(y) { console.log(y); } 
(function() { 
    var x = 42; 
    show(x); 
})() 
+0

'show()'范围内不存在'x',它在匿名函数的范围内声明。是的,匿名函数有自己的范围。对于您的相关问题 - Javascript是一种解释型语言,您不需要编译它,但是在现代JS引擎中,随着代码的解释,它们会将其编译为机器代码。 – skyline3000

回答

1

注意:下面的说明是ES5,不ES6的,因为在ES6范围的规则已经改变(由于引进放开)。

Javascript does compile。就像C++/c#等其他语言一样,没有像exe/IL代码那样的中间事物需要点击才能开始执行。在JS执行后,编译阶段开始。

因此,当编译发生时,编译器会查找函数声明并为变量寻找var declaration。因此,对于此IIFE,

(function() { 
    var x = 42; 
    show(); 
})(); 

它确实找到一个var声明,变量x被注册在IIFE的范围中。这称为变量提升,x在功能级别可用,在这种情况下为IIFE。

在执行时后来,IIFE看起来是这样的(概念):

(function() { 
    //registration of x in this function's scope has already happened at 
    //compile time. Notice absence of `var` 
    x = 42; 
    show(); 
})(); 

现在,在这个时候发动机会谈的范围,并要求x的左值参考。由于x已在IIFE中注册,所以发动机得到一个,然后将42分配给它。

现在对于这部分:

function show() { console.log(x); } 

当节目被调用时,引擎首先会询问放映功能为x的范围,因为它不具有名为X注册的任何变化,全球范围内被要求(右值引用),因为它在编译阶段从未用全局范围注册过,所以在任何范围内都找不到它,并且我们得到参考错误。

it should know that x == 42 at the time it calls show(), which is inside the anonymous function, correct? 

由于范围规则,外部范围中的变量在内部范围内可见,但反之亦然。在IIFE内部可以看到x,而在外部范围内则不在外部。

+0

自从ES6发布以来,我知道'let'的作用域是块,而不是函数,但是var变量的作用域规则是如何改变的?我认为'var'的行为从ES5到ES6保持不变。 – chharvey

+0

@chharvey我的意思是说,由于let关键字(如果使用let声明),函数级别范围规则不再适用于变量。 –

+0

@chharvey顺便说一句,你看到的错误是运行时错误。代码执行并抛出错误。 –

1

变吊装乐趣。

这是说给你的参考错误,因为你已经定义了一个闭包(在另一个函数中定义的函数)的x,这意味着它在全局范围内不可用并且方法不知道它存在。如果你首先在全球范围内定义它,它当然会起作用。这就是说,在ES6 +中,由于使用了letconst,范围已经得到了显着提高,所以除非你坚持使用vanilla JS,否则你可能会发现那些提供更加一致和可预测的编码体验。

这是关于这个问题的一个有用的阅读:How do JavaScript closures work?

1

show函数获取它声明的范围,而不是调用。