神奇的函数做用域

前言

今天发现了两个关于函数做用域的神奇例子,这里和你们分享分享:javascript

第一个例子

    var a = 1; function foo() { if (!a) { var a = 2; } alert(a); }; foo();

上面这段代码在运行时会产生什么结果?java

 咱们来分析一下:es6

        1.建立了全局变量 a,定义其值为 1
        2.建立了函数 foo
        3.在 foo 的函数体内,if 语句将不会执行,由于 !a 会将变量 a 转变成布尔的假值,也就是 false
        4.跳过条件分支,alert 变量 a,最终的结果应该是输出 1 闭包

看起来无懈可击的分析,可是实际上,结果错误。答案居然是 2!为何?函数

什么叫申明?spa

        是指你声称某样东西的存在,好比一个变量或一个函数;但你没有说明这样东西究竟是什么,仅仅是告诉解释器这样东西存在而已;code

什么叫定义?blog

        是指你指明了某样东西的具体实现,好比一个变量的值是多少,一个函数的函数体是什么,确切的表达了这样东西的意义。ip

因此上面的代码实际上能够写成这样:作用域

    var a; a = 1; function foo() { var a;    // 关键在这里
      if (!a) { a = 2; } alert(a); // 此时的 a 并不是函数体外的那个全局变量
 } foo();

而后又有人会问,不是有个if吗?if不成立哪就不会为a赋值为2。

由于 JavaScript 没有块级做用域(Block Scoping),只有函数做用域(Function Scoping),因此说不是看见一对花括号 {} 就表明产生了新的做用域,和 C 不同!

当解析器读到 if 语句的时候,它发现此处有一个变量声明和赋值,因而解析器会将其声明提高至当前做用域的顶部(这是默认行为,而且没法更改),这个行为就叫作 Hoisting。

怎样可以alert出那个a=1?

 let a; a = 1; function foo() { let a; // 关键在这里
      if (!a) { a = 2; } alert(a); // 此时的 a 并不是函数体外的那个全局变量
 } foo();

es6的语法,javascript是有块级做用域的。

还能够经过闭包的方式实现:

  var a = 1; function foo() { if (!a) { (function() { var a = 2; }()); }; alert(a); }; foo();

第二个例子

 var a = 1; function test() { foo(); var foo = function() { alert(a); } } test();

这个运行的结果是什么?初略一看,alert(1),可是实际上报错。

  Uncaught TypeError: foo is not a function。

为何会这样?

  提高的仅仅是变量名 foo,至于它的定义依然停留在原处。所以在执行 foo() 以前,做用域只知道 foo 的命名,不知道它究竟是什么,因此执行会报错(一般会是:undefined is not a function)。这叫作函数表达式(Function Expression),函数表达式只有命名会被提高,定义的函数体则不会。

 var a = 1; function test() { var foo; foo(); // 这个时候函数foo,只声明,未赋值。
        foo = function() { alert(a); } } test();

怎么改?   

 var a = 1; function test() { var foo = function() { alert(a); } foo(); } test(); // 1

这个例子也展现了函数声明与函数表达式的差异,函数申明会放到做用域的顶部,函数表达式则不会。

最后引用不少书中的一句话:“请始终保持做用域内全部变量的声明放置在做用域的顶部”,相信如今的你对这句话应该有一个认识了。

相关文章
相关标签/搜索