由作用域和声明提前所引发的争论

Author Avatar
GoDotDotDot 3月 10, 2017
  • 在其它设备中阅读本文章

昨天晚上本人在群里偶然看到下面这段代码,有人对结果表示疑问。然而虽然有人能够说出运行结果,但也许是碰巧,并没有说出其中的原理,下面就让我们瞧一下这段代码:

var foo = 1;
function bar() {
    foo = 10;
    return;
    function foo() {}
}
bar();
console.log(foo);

门派

对于上面那段代码,控制台中到底输出什么呢?通过群友的回答,我进行了门派归类,大致可以分为如下两类:

  • 输出10
  • 输出1

输出10

很多对javascript这门语言了解的不够透彻的人来说,这段代码很容易让人想到输出结果就是10。那他们是怎么理解这段代码的呢?通过群友的表述,我做些简单的转述,他们的理解是这样的:

  • 声明foo这个全局变量
  • 当运行bar这个函数的时候,foo变量被重新赋值为10
  • bar函数里面遇到了return关键字,将结束该函数的运行
  • 打印的时候foo自然就成了10

聪明的你是否也是这样想的呢?我们暂且不说这种想法是否正确,继续看其他门派的解释

输出1

我们首先在控制台运行下,看看输出结果是多少。

> var foo = 1;
  function bar() {
      foo = 10;
      return;
      function foo() {}
  }
  bar();
  console.log(foo);
> 1

没错,这个地方输出的是1,但是很遗憾没有人能够解析清楚其中的奥秘。接下来让我们探寻一下其中的原理。

原理

这个地方其实考察的是javascript这门语言的两个知识点,一个是作用域,另一个就是声明提前

这里不打算去讲述作用域声明提前相关的知识点,如果想了解的请自行Google。

JavaScript分为两种声明提前,一种是函数声明提前,另一种是变量声明提前,这道题里面两种都存在。至于为什么会存在这种情况,那是由于JavaScript解析引擎的锅,但是只要我们了解其中的奥妙了之后,对于以后出现的bug会有很大的帮助。在JavaScript中,函数是一等公民,函数的声明提前要大于变量的声明提前。所以了解了这个之后,我们将代码改写成JavaScript解析引擎解析之后的代码。

function bar() {
    function foo() {}
    foo = 10;
    return;
}
var foo;
foo = 1
bar();
console.log(foo);

看到这里,你是不是瞬间明白了为什么结果就是1。博主在这里还是想强调一下它的运行机制:

  • 首先,声明了bar函数,在bar函数里面又声明了一个foo函数
  • 当运行bar函数之后,bar函数里面的成员foo又被重新赋值为10,所以在bar函数里面的foo其实一个局部变量,并不是全局变量中的foo,这两个变量不相关
  • 在调用console.log(foo)的时候,自然取的是全局变量中的foo

如果对于bar函数声明时里面的foo就是个function不确定的可以在控制台中调试便知。

var foo = 1;
function bar() {
      console.log(typeof foo) // 你将会得到'function'
    foo = 10;
    return;
    function foo() {}
}
bar();
console.log(foo);

博主才疏学浅,如果解释不正确的地方还请各位批评指出,谢谢。:-D

基于MIT开源协议
本文链接:http://blog.godotdotdot.com/2017/03/10/由作用域和声明提前所引发的争论/