第6章 补充(图解JavaScript执行环境、作用域、闭包)
图解JavaScript执行环境、作用域、闭包
首先我们要先知道有这三个概念:执行环境、作用域链、变量对象
当开始执行上面这段js代码的时候,全局执行环境进栈,执行环境带有与之关联的变量对象,代码在环境中执行时,会创建变量对象的作用域链
代码执行到normal();这一行时,normal函数的执行环境被推入环境栈中。
normal函数执行完之后,它的执行环境将被推出环境栈,当前环境回到全局执行环境。normal函数执行时所创建的活动对象(变量对象)也随之销毁(没有指针指向它,垃圾回收时会将其回收掉,释放内存).
代码继续执行到var result = main();main函数的执行环境被推入环境栈中。
通过上面的图也就知道了为什么main函数里面第一个console的输出结果是: “(1) numA = 0 , numB = undefined”。 numB的值为什么是undefined而不是我们在外面全局环境里面定义的var numB = 1;了。 因为变量名提升,变量对象在执行流进入函数的时候就已经创建了,标识符解析沿着作用域链一级一级查找。
main函数的return语句里有一个匿名函数,当代码执行到return语句时,匿名函数被创建,也就是我们的闭包诞生了。
函数被创建的同时,会创建一个作用域链,这个作用域链包含了外部函数的活动对象。注意,这里的函数的作用域链跟上面所说的环境的作用域链是不同的。函数在被创建的时候,会创建一个作用域链保存在内部的[[scope]]属性里面。函数被调用的时候,通过复制函数的[[scope]]属性中的对象构建起执行环境的作用域链,并把当前活动对象推入执行环境的作用域链的前端。 为了区分,下图用另一个颜色表示了闭包函数的作用域链。
红色箭头,可以看到,闭包函数的作用域链其中指向了main函数的活动对象。
上图中可以看到,main函数执行环境已经销毁了,但是main函数的变量对象并没有像normal函数的变量对象一样销毁,由于仍然有闭包函数的作用域链的指针指向它,垃圾回收的时候释放不了内存。而闭包也因此能访问外部函数的变量对象,即使外部函数的执行环境已销毁.