在上一篇文章《Javascript本质第一篇:核心概念》中,对Javascript执行上下文做了解释,但是这些都是基于Javascript标准中对执行上下文的定义,也就是说理论上的东西,本文将在Google Chrome中通过实际代码来展示Javascript的执行上下文。

1. Javascritp运行时内存监控

Google Chrome的开发人员工具包含了Javascript性能监控工具,通过这个工具可以查看Javascript运行时内存占用情况、监控CPU消耗、查找内存泄漏等。

F12打开Chrome的开发人员工具,点击“Profiles”选项卡,选择“Take Heap Snapshot”,点击“Take Snapshot”按钮(或者点击下面黑色的实心圆)就可以拍摄一个当前内存的快照。

在快照中可以浏览当前Javascript运行时中包含的所有对象的信息和对象之间的引用关系,这些对象包括用户创建的对象和系统创建的对象。

由于一个快照中包含的对象数量往往非常大,多数都是页面初始化时创建的页面对象和用户不可访问的系统对象,不便于查找。为此,可以拍摄两个快照,Chrome能够计算出在两个快照之间创建的对象,通过分析新增对象,可以直观的看到代码运行时内存分配和占用情况。

2. 执行上下文

创建一段包含两级嵌套执行上下文的演示代码 :

         fun = (function () {
var a = (Math.random() * 10000).toFixed();
return (function () {
var b = (Math.random() * 10000).toFixed();
var f = function () {
var c = (Math.random() * 10000).toFixed();
return a + " - " + b + " - " + c;
};
return f;
})
})();
a = fun();
b = fun();

先在console先运行一遍这段代码,拍摄一个内存快照Snapshot 1。

再在console运行一遍这段代码,拍摄一个内存快照Snapshot 2。

(如果直接上来拍摄内存快照Snapshot 1,运行代码再拍摄Snapshot 2,由于代码第一次运行,会产生大量系统对象,不便于对象查找。)

在Profiles标签页的下方选择Objects allocated between Snapshots 1 and 2。

这时对象窗口就按构造函数分类列出了所有对象。

找到(closure)行,展开,查找被高亮的三行,这三行就对应fun、a、b这三个函数,也就是三个闭包。

Javascript本质第二篇:执行上下文-LMLPHP

...

Javascript本质第二篇:执行上下文-LMLPHP

形如@63185是对象唯一标识。

context就是执行上下文。

previous表示当前执行上下文的上级执行上下文。

可以看到,a和b这两个函数所在的执行上下文的上级执行上下文与fun所在的执行上下文相同,都是标识为@63187的对象。

三个函数,三个闭包,三个执行上下文。函数内部的变量a和b都位于context中。

下面是代码运行结果截图,在之Snapshot 2后又分别运行了a和b三次,以显示输出的结果。

Javascript本质第二篇:执行上下文-LMLPHP

可以看到,7780、 5523 和 8360这三个变量值出现在了前面的对象截图中,最后一个值是每次调用时实时生成。

下面的代码演示了修改执行上下文中变量的值:

         fun = (function () {
var a = (Math.random() * 10000).toFixed();
var a_1 = "data";
return (function () {
var b = (Math.random() * 10000).toFixed();
var obj = {
getB: function () {
return b;
},
setB: function () {
b = (Math.random() * 10000).toFixed();
},
getA: function () {
return a;
},
setA: function () {
a = (Math.random() * 10000).toFixed();
},
show: function () {
var c = (Math.random() * 10000).toFixed();
return a + " - " + b + " - " + c;
} };
return obj;
})
})();
a = fun();
b = fun();

运行结果如下:

Javascript本质第二篇:执行上下文-LMLPHP

如果你在Profiles中监视新fun函数所在的执行上下文的时候,会发现a_1这个变量没有出现在执行上下文中,应该是a_1没有在内部函数中被使用,chrome将它优化掉了。

3. 结论

闭包是Javascript的一个重要编程模式,它利用了Javascript固有的特性——执行上下文,能够模拟面向对象中的私有变量这种封装方式。

04-29 01:52