javascript 作用域,变量提升,函数提升
ES6 之前,js 只有两种作用域:
- 全局作用域
- 函数作用域
是没有 块级作用域的
全局作用域变量:
- 不是在函数内部定义的
- 未使用 var 声明的
函数作用域变量:
只要在函数中定义的变量,函数内部都可以访问到。即使是后定义的,表现为提升到函数顶部,所以函数任何位置都可以访问该变量
什么是 变量提升(hoisting):
因为变量申明是在任意代码执行前处理的(注意,只是声明),在代码区中任意地方申明变量和在最开始(最上面)的地方申明是一样的。也就是说,看起来一个变量可以在申明之前被使用!这种行为就是所谓的“hoisting”,也就是变量提升,看起来就像变量的申明被自动移动到了函数或全局代码的最顶上。
示例:
![](https://img.haomeiwen.com/i13839095/f4b8aa7667961df8.png)
输出: undefined
解析:运行之前,先处理所有变量声明,全局变量会提升到文件开头,函数内声明变量会提升到函数开头。所以 var tmp = 'hello' 会将 var tmp 提升至函数开头,但是此刻并不会赋值。所以 console 输出的是 undefined
理解了上面,那么下面这个就好理解了:
![](https://img.haomeiwen.com/i13839095/f3e7a78d0c959b4c.png)
所以,建议变量声明 全局或者函数最顶上,可读性好。
继续:
![](https://img.haomeiwen.com/i13839095/e2bb7b2bb30f60f7.png)
输出: 1 2 2.
变量被声明后,不会再次声明,相当于:
![](https://img.haomeiwen.com/i13839095/894e3d7f98d9218a.png)
函数和变量同时声明:
![](https://img.haomeiwen.com/i13839095/7b32f47ecbe09b4a.png)
输出: function foo(){}
如果改成下面形式:
![](https://img.haomeiwen.com/i13839095/c8c2622f3c2b0b12.png)
输出: undefined
![](https://img.haomeiwen.com/i13839095/a9376ce5e16ada4c.png)
输出: ReferenceError, 因为调用的时候, foo 为 undefined
如果还有一个同名foo 函数放到第一个 foo 后面:
function foo () {
console.log('foo2')
}
输出: ‘foo2'
为什么?
函数提升分为两种情况:
- 函数声明: function foo(){}
- 函数表达式: var foo = function(){}
第二种就是声明一个 foo 变量,然后将一个匿名函数赋值给 foo, 和上面变量声明是一样的。所以输出 undefined
第一种会将函数声明整个提升到开头,相当于:
![](https://img.haomeiwen.com/i13839095/02f7ae1d94d983b0.png)
并且函数声明优先于变量声明,所以,输出: function foo(){}
同名的函数声明会使用最后一个
- 首先,解释器扫描Function Declarations,也就是function name{},解释器将对每个声明创建一个函数并作为Window的一个变量
- 解释器扫描var declarations,作为window的属性。但是此时变量并没有被赋值,所有的变量此时都是undefined。
要彻底理解JS的作用域和Hoisting,只要记住以下三点即可:
1、所有申明都会被提升到作用域的最顶上
2、同一个变量申明只进行一次,并且因此其他申明都会被忽略
3、函数声明的优先级优于变量申明,且函数声明会连带定义一起被提升
继续下面的示例:
![](https://img.haomeiwen.com/i13839095/b22782c3be0fe374.png)
输出: 'foo'
解析: 函数声明优先于变量声明,再次声明都被忽略。
下面输出什么?
![](https://img.haomeiwen.com/i13839095/f2f97ff537431a19.png)
输出结果:
先弹框:
![](https://img.haomeiwen.com/i13839095/242336c55b6c9313.png)
然后输出 2
然后又弹框:
![](https://img.haomeiwen.com/i13839095/f3d344c257ce23a3.png)
最后:
![](https://img.haomeiwen.com/i13839095/218c4742309984ad.png)
解析:
函数声明比表达式赋值优先级高,所以第一个alert 弹出 声明的函数
a() 输出 2
接下来解析到变量 a 被赋值 var a = function(){},虽然这个时候 a 已经被函数声明赋值,但是可以被后续的赋值覆盖掉,所以之后 a 的值是第一个函数
所以,接下来弹出 1
因为存在函数作用域,所以内部定义的变量,外部是访问不到的,所以报错 d 未定义
alert(c) 由于报错执行不到
![](https://img.haomeiwen.com/i13839095/2a0df3aa79694877.png)
解析:
a = null 将 function a() 置成 null 了,不再是函数了
let, const 都存在变量提升,但是有点不一样。后续会讲到。
关注我,每天更新技术干货!