您的位置:首页 > Web前端 > JavaScript

图解JavaScript执行环境、作用域、闭包

2017-02-23 16:07 232 查看
首先我们要先知道有这三个概念,
执行环境
作用域链
变量对象




var numA = 0;
var numB = 1;

function normal() {

var numC = 3,
numD = 4;

return numC + numD;
}

function main() {

var numE = 5;
console.log('(1) numA = ' + numA + ' , numB = ' + numB);
var numB = 6;

return function() {
numE += 1;
console.log('(2) numE = ' + numE);
}
}

normal();

var result = main();


当开始执行上面这段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函数的执行环境被推出环境栈,当前环境回到全局执行环境。



上图中可以看到,main函数执行环境已经销毁了,但是main函数的变量对象并没有像normal函数的变量对象一样销毁,由于仍然有闭包函数的作用域链的指针指向它,垃圾回收的时候释放不了内存。而闭包也因此能访问外部函数的变量对象,即使外部函数的执行环境已销毁.

原文地址:http://zhangruojun.com/exection-scope-closure/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐