execution context/execution context stack
当函数运行时,会创建一个执行环境, 这个执行环境就叫做执行上下文 (execution context)
变量对象(variable object) VO中存放着上下文中定义的所有变量和对象
作用域链(scope chain) this
JavaScript引擎创建了执行上下文栈(execution context stack)来管理执行上下
内部上下文可以通过作用域链访问外部上下文中的一切,但外部上下文无法访问内部上下文中的任何东西,每个上下文都可以到上一个上下文中去搜索变量和函数,但任何一个上下文都不能到下一级的上下文中去搜索
变量对象和活动对象
函数执行时,每个执行上下文都会有一个包含其中变量的对象,全局上下文中的叫变量对象,它会在代码执行期间始终存在。而函数局部上下文中的叫活动对象,只在函数执行期间存在。
var a = 10
function test(x) {
var b = 20
}
test(30)
// 全局上下文的变量对象
VO(globalContext) {
a: 10,
test: <reference to function>
}
// test函数上下文的变量对象
VO(functionContext) {
x: 30,
b: 20
}
AO
1. 在函数执行上下文中,VO是不能直接访问的,
执行栈压栈顺序
一开始执行代码的时候,确定一个global execution context 作为默认值,如果你的全局环境中,调用了其他函数,程序将会再创建一个新的EC,然后将此EC推入到执行栈中的execution stack, 一旦一个EC执行完成,会从执行栈中推出(pop)
ECStack = [
globalContext
]
function fun() {
console.log('fun3)
}
栈顶的
ECStack = [
fun,
globalContext
]
scope/scope chain
作用域是根据名称查找变量的一套规则
词法作用域(lexical scoping)
JavaScript采用的是词法作用域(lexical scoping),也就是静态作用域
函数的作用域在函数定义的时候就决定了
主要区别:词法作用域是在写代码或者说定义时确定的,而动态作用域是在运行时确定的(this也是!)
closure
闭包产生的本质就是,当前环境中存在指向父级作用域的引用
当函数可以记住并访问所在的词法作用域时,就产生了闭包,即使当前函数在词法作用域外执行
function foo(){
var a = 2
function bar() {
console.log(a)
}
return bar
}
在foo()执行后,通常会期待foo()的整个内部作用域都被销毁,由于看上去foo()的内容不会再被使用,所以很自然的会考虑对其进行垃圾回收
而闭包正式可以阻止这件事发生。事实上内部作用域依然存在,一次没有被回收
bar()依然持有对该作用域的引用,而这个引用就是闭包
因为闭包会保留它们包含函数的作用域,所以比其他函数更占用内存
循环和闭包
for (var i = 1; i <= 5; i++) {
setTimeout(function () {
console.log(i)
}, i*1000)
}
根据作用域的工作原理,尽管循环中的5个函数是在各自的迭代中分别定义的,但是它们都被封闭在一个共享的全局作用域中,因此实际上只有一个i
for (var i = 1; i <= 5; i++) {
(function() {
setTimeout(function () {
console.log(i)
}, i*1000)
})()
}
IIFE(Immediately Invoked Function Expression)会通过声明并立即执行一个函数来创建作用域
但是这样不行,虽然我们拥有了更多的词法作用域了,每个延迟函数都会将IIFE在每次迭代中创建的作用域封闭起来
如果作用域是空的,那么仅仅将它们进行封闭是不够的,我们IIFE只是一个什么都没有的空作用域,它需要包含一点实质内容才能为我们所用
它需要有自己的变量,用来在每个迭代中存储i的值
for (var i = 1; i <= 5; i++) {
(function(k) {
setTimeout(function () {
console.log(k)
}, i*1000)
})(i)
}
reference
Execution context, Scope chain and JavaScript internals
JavaScript深入之词法作用域和动态作用域
学习Javascript闭包(Closure)
JavaScript深入之执行上下文
mdn闭包