Web前端软件工程师
从代码开始
在我们的认知中,代码都是一行一行顺序去执行的。带着这样的认知,我们读一下以下例子:
console.log(a)
var a = 10
-----------
console.log(a)
a = 10
----------
console.log(a)
let a = 10
复制代码
这三段代码不都是在没有a的时候去输出a吗?都报错呀
var flag = true
if(flag) {
var a = 10
}
console.log(a) //输出10
复制代码
flag为true会输出10, flag为false不会运行if里面的代码,不存在a变量,所以报错
foo()
function foo() {
console.log(a)
}
var a = 10
复制代码
未定义foo的情况下执行foo,报错!
foo()
var foo = function() {
console.log(a)
}
var a = 10
复制代码
解释同上一题
很遗憾,答案都是错的。究其原因,变量提升!
浅谈词法作用域
简单介绍一下词法作用域,向更多了解,请看理解JavaScript的词法作用域
var a = 10
function foo() {
console.log(a)
}
foo(a) // foo运行在全局作用域 输出10
function baz() {
var a = 100
foo() // foo运行在baz包裹的作用域,输出10,并不会输出离自己进的100
}
baz()
复制代码
从上诉输出结果可知,在代码运行前(也就是编译阶段),作用域都已经确定好了。foo永远在全局作用域下,不管他在哪里运行,foo都会输出全局作用域下的a
编译阶段代码发生了什么?
编译阶段,除了会确定作用域,还发生了变量提升。看以下例子
// 很多逻辑代码
var a = 10
复制代码
编译完成之后
var a
// 很多逻辑代码
a = 10
复制代码
由上我们可以发现:
-
var a = 10在编译阶段变成了两句。一句为声明变量:var a,一句为给变量赋值: a=10
-
其中声明变量提升到当前作用域(全局作用域)的最前面,赋值变量的语句留在原地
一下是编译前后的代码,很容易发现答案。
console.log(a)
var a = 10
-----------
// 编译后:
var a
console.log(a)
a = 10
复制代码
输出undefined
console.log(a)
a = 10
---------
// 编译后:
console.log(a)
a = 10
复制代码
没有变化,报错
var flag = true
if(flag) {
var a = 10
}
console.log(a)
-------------
// 编译后
var flag
var a
flag = true
if(flag) {
a = 10
}
console.log(a)
复制代码
flag为false也不会报错,只会输出undefined
函数提升
除了变量声明会提升之后,函数也会提升。函数声明与函数表达式情况会不一样。看以下例子
foo()
function foo() {
console.log(1)
}
---------
// 编译后
function foo() {
console.log(1)
}
foo()
复制代码
函数声明:整个函数会提升,输出1。
foo()
var foo = function() {
console.log(1)
}
-------------
// 编译后
var foo
foo()
foo = function() {
console.log(1)
}
复制代码
报错 foo is not function。跟变量提升的规则一样。
重复声明/函数优先
同一个作用域下,变量名与函数声明名(重复声明)相同的情况下,会优先函数,忽略变量
foo()
function foo() {
console.log(1)
}
var foo
---------
// 编译后
function foo(){
console.log(1)
}
// var foo 优先函数,直接忽略这一句
foo() // 输出1
复制代码
var foo 直接被忽略掉。foo优先为一个函数。
let/const
let与const不仅不会变量提升,而且还会把变量绑定到块作用域(说起块作用域,又要提到函数作用域,又要提到暂时性死区)。看例子
if(true) {
var a = 10
}
console.log(a) // 输出10
if(true) {
let a = 10
}
console.log(a) // 报错, 形成块作用域
复制代码
通过a声明的变量,不经没有变量提升,而且还形成了一个if{}块作用域。全局作用域访问不到a的值
小结
-
编译阶段确定作用域并发生变量提升
-
变量的声明提升,变量的赋值留在原地
-
重复声明下,函数优先
-
let/const不会发生变量提升,而且还会形成块作用域