JS的作用域和变量提升问题
作用域(scoping)
JavaScript是 函数级作用域。块(可以理解成一个“{}”),就像if语句,并不会创建一个新的作用域,即 没有块级作用域 。只有函数才会创建新的作用域。
1 | //例1 |
变量提升–Hoisting(提升)
Hosting 只提升了命名,没有提升定义
看一下这题:
1 | var v='Hello World'; |
为什么会是这样的结果?变量提升,JS中声明的变量,不管在哪个地方声明定义的,其这个变量的声明都会默认被提升到作用域的最前面。像上面说的,JS是函数级作用域,也就是说var v='I love you'中的var v;会被提升到它所处的函数的最前面,上面那段代码等价于:
1 | var v='Hello World'; |
当解析器读到 if 语句的时候,它发现此处有一个变量声明和赋值,于是解析器会将其声明提升至当前作用域的顶部(这是默认行为,并且无法更改),这个行为就叫做 Hoisting。
这样就懂了,alert后自然是undefined。所以写js代码的时候,要把变量的声明写在作用域的最前端,防止出现意外。
这样再来看这个例子就懂了:
1 | function fire(bool) { |
等价于(只提升了声明,没有提升定义)
1 | function fire(bool) { |
函数提升
函数提升是把整个函数都提到前面去。
我们写函数的时候有两种方式:一种是函数表达式、另一种是函数声明方式。只有函数声明形式才能被提升
函数声明方式提升:
1 | //函数 myTest 是一个声明 |
函数表达式方式提升:
1 | //这里被提升的仅仅是变量名 foo,至于它的定义依然停留在原处。因此在执行 foo() 之前 |
let实现块级作用域–解决变量提升问题
es6 之前,JavaScript 并没有块级作用域,所谓的块,就是大括号里面的语句所组成的代码块
let定义的变量只有在代码块内有效。
1 | function fire(bool) { |
其次, let定义的变量不存在变量提升:
1 | function fire(bool) { |
所以应当尽可能的避免用 var,用 let 来代替,除非需要用到变量提升。
再来几个例子
变量提升
1 | var a = 1; |
解释器分析上面的代码时:
1 | var a; |
想要结果是1怎么办?–创建新的作用域
alert(a) 在执行的时候,会去寻找变量 a 的位置,它从当前作用域开始向上(或者说向外)一直查找到顶层作用域为止,若是找不到就报 undefined。
因为在 alert(a) 的同级作用域里,我们再次声明了本地变量 a,所以它报 2;所以我们可以把本地变量 a 的声明向下(或者说向内)移动,这样 alert(a) 就找不到它了。
1 | var a = 1; |