在上一篇文章《Javascript本质第一篇:核心概念》中,对Javascript执行上下文做了解释,但是这些都是基于Javascript标准中对执行上下文的定义,也就是说理论上的东西,本文将在Google Chrome中通过实际代码来展示Javascript的执行上下文。
1. Javascritp运行时内存监控
Google Chrome的开发人员工具包含了Javascript性能监控工具,通过这个工具可以查看Javascript运行时内存占用情况、监控CPU消耗、查找内存泄漏等。
F12打开Chrome的开发人员工具,点击“Profiles”选项卡,选择“Take Heap Snapshot”,点击“Take Snapshot”按钮(或者点击下面黑色的实心圆)就可以拍摄一个当前内存的快照。
在快照中可以浏览当前Javascript运行时中包含的所有对象的信息和对象之间的引用关系,这些对象包括用户创建的对象和系统创建的对象。
由于一个快照中包含的对象数量往往非常大,多数都是页面初始化时创建的页面对象和用户不可访问的系统对象,不便于查找。为此,可以拍摄两个快照,Chrome能够计算出在两个快照之间创建的对象,通过分析新增对象,可以直观的看到代码运行时内存分配和占用情况。
2. 执行上下文
创建一段包含两级嵌套执行上下文的演示代码 :
1 fun = (function () { 2 var a = (Math.random() * 10000).toFixed(); 3 return (function () { 4 var b = (Math.random() * 10000).toFixed(); 5 var f = function () { 6 var c = (Math.random() * 10000).toFixed(); 7 return a + " - " + b + " - " + c; 8 }; 9 return f; 10 }) 11 })(); 12 a = fun(); 13 b = fun();
先在console先运行一遍这段代码,拍摄一个内存快照Snapshot 1。
再在console运行一遍这段代码,拍摄一个内存快照Snapshot 2。
(如果直接上来拍摄内存快照Snapshot 1,运行代码再拍摄Snapshot 2,由于代码第一次运行,会产生大量系统对象,不便于对象查找。)
在Profiles标签页的下方选择Objects allocated between Snapshots 1 and 2。
这时对象窗口就按构造函数分类列出了所有对象。
找到(closure)行,展开,查找被高亮的三行,这三行就对应fun、a、b这三个函数,也就是三个闭包。
...
形如@63185是对象唯一标识。
context就是执行上下文。
previous表示当前执行上下文的上级执行上下文。
可以看到,a和b这两个函数所在的执行上下文的上级执行上下文与fun所在的执行上下文相同,都是标识为@63187的对象。
三个函数,三个闭包,三个执行上下文。函数内部的变量a和b都位于context中。
下面是代码运行结果截图,在之Snapshot 2后又分别运行了a和b三次,以显示输出的结果。
可以看到,7780、 5523 和 8360这三个变量值出现在了前面的对象截图中,最后一个值是每次调用时实时生成。
下面的代码演示了修改执行上下文中变量的值:
1 fun = (function () { 2 var a = (Math.random() * 10000).toFixed(); 3 var a_1 = "data"; 4 return (function () { 5 var b = (Math.random() * 10000).toFixed(); 6 var obj = { 7 getB: function () { 8 return b; 9 }, 10 setB: function () { 11 b = (Math.random() * 10000).toFixed(); 12 }, 13 getA: function () { 14 return a; 15 }, 16 setA: function () { 17 a = (Math.random() * 10000).toFixed(); 18 }, 19 show: function () { 20 var c = (Math.random() * 10000).toFixed(); 21 return a + " - " + b + " - " + c; 22 } 23 24 }; 25 return obj; 26 }) 27 })(); 28 a = fun(); 29 b = fun();
运行结果如下:
如果你在Profiles中监视新fun函数所在的执行上下文的时候,会发现a_1这个变量没有出现在执行上下文中,应该是a_1没有在内部函数中被使用,chrome将它优化掉了。
3. 结论
闭包是Javascript的一个重要编程模式,它利用了Javascript固有的特性——执行上下文,能够模拟面向对象中的私有变量这种封装方式。