function foo(){
var a = 1
let b = 2
{
let b = 3
var c = 4
let d = 5
console.log(a)
console.log(b)
}
console.log(b)
console.log(c)
console.log(d)
}
foo()
刚执行 foo 时的执行上下文
可以看到 var 声明的变量在「变量环境」中,而 let声明的变量在「词法环境中」,且词法环境是一个栈结构。并且由于 var 没有块级作用域,所以在 {} 里的 c 也在编译阶段就出现在了 foo 的执行上线文中。
然后执行到 {} 代码块里时
代码块里的 bd 入词法环境栈,此时变量环境的 a 已经被置为 1,且块外的 b也被置为了 2。块内的变量并不会影响到块外的同名变量的值。
执行上下文和调用栈,涉及块级作用域
当我们需要执行一段代码:
输出:
这里我们跳过变量提升,就直接说这个当函数名和变量名相同的情况。
浏览器执行代码之前,都会进行一个编译的过程。也就是在这个过程中,产生了执行上下文和执行栈,这个我们后面说。
看一下编译的过程,当前代码会被编译为:
函数声明的优先级比变量提升的优先级高,所以在
foo
被赋值为 2 之前,打印foo
会返回 函数,当 foo 被赋值为 2 以后,就会打印出来 2 了。执行上下文
当执行上面的代码时,浏览器先进行了编译,然后根据执行上下文和可以执行代码来执行出最后的结果:
执行完
addAll
,再次弹出栈定,只剩全局执行上下文块级作用域与词法环境
在上面的执行上下文中,我们一直往变量环境里添加变量和函数,词法环境里空空如也。想让词法环境里装东西,就得先看一下块级作用域。
刚执行
foo
时的执行上下文可以看到
var
声明的变量在「变量环境」中,而let
声明的变量在「词法环境中」,且词法环境是一个栈结构。并且由于var
没有块级作用域,所以在{}
里的c
也在编译阶段就出现在了foo
的执行上线文中。然后执行到
{}
代码块里时代码块里的
b
d
入词法环境栈,此时变量环境的a
已经被置为1
,且块外的b
也被置为了2
。块内的变量并不会影响到块外的同名变量的值。再继续执行,当执行到
console.log(a)
的时候,会从词法环境开始找起,自顶向下,如果找到了就返回它对应的值,如果没有到找,就再去变量环境里去找:当作用域块执行完以后,属于作用域块内部定义的变量就会从词法环境弹出:
参考
极客时间 -> 浏览器与工作原理 -> 浏览器中的JavaScript执行机制