简单的梳理一下ECMA Variable Object1, Scope Chain2 及 Execution Contexts3 相关的概念,以便在分析devtools heap-snapshot时明白各个属性表示的意义。
变量对象(VO) 是一个与 执行上下文相关的特殊对象,用来存储以下信息:
VO = {
variables, //变量声明
functions, //函数声明
formal_parameters //函数的形参
}
在函数执行上下文中,VO是不能直接访问的,它主要扮演被称作 活跃对象(AO) 的角色。
活动对象是在函数激活(进入函数上下文)时被创建的
它通过函数的 arguments 属性初始化:
AO = {
arguments: <ArgO> //指向Arguments对象
}
Arguments = {
callee //the reference to the current function;
length //quantity of real passed arguments;
properties //indexes of function’s arguments
}
可以理解为 AO = VO + Arguments
Scope chain is related with an execution context a chain of variable objects which is used for variables lookup at identifier resolution.
Scope = withObj|catchObj + AO|VO + [[Scopes]]
[[Scopes]] 是该函数所有父节点VO的层级链,随着函数创建产生,函数生命周期结束时消亡
在下例中,someMethod 的 [[Scopes]] 包含了闭包属性 originalThing 以及全局上下文的VO:
closure = code + [[Scopes]]
[[FunctionLocation]] 指向函数code所在的位置
[[Scopes]] 保存着函数创建时的词法环境,直至函数消亡
Identifier resolution is a process of determination to which variable object in scope chain the variable (or the function declaration) belongs.
(1) on scope chain links,
(2) and on every of scope chain link — deep into on prototype chain links.
AO是没有原型这一说的,下例bar的AO匹配失败后,在foo的AO中找到x:
function foo() {
var x = 20;
function bar() {
alert(x);
}
bar();
}
Object.prototype.x = 10;
foo(); // 20
一系列活动的执行上下文从逻辑上形成一个栈。栈底总是全局上下文,栈顶是当前(活动的)执行上下文。当在不同的执行上下文间切换(退出的而进入新的执行上下文)的时候,栈会被修改(通过压栈或者退栈的形式)。
activeExecutionContext = {
VO: {...}, // or AO
this: thisValue,
Scope: [...]
};
在Eval上下文之前会先压入调用上下文
在1.7以上版本SpiderMonkey的实现中(Firefox,Thunderbird浏览器内置的JS引擎),允许在调用eval函数的时候,将调用上下文作为第二个参数传递给eval函数。因此,如果传入的调用上下文存在的话,就有可能会影响该上下文中原有的私有变量 - goddyzhao
function foo(){
var x = 1;
return function() { alert(x); }
};
var bar = foo();
bar(); // 1
eval('x = 2', bar); //传递上下文,影响了内部变量“var x”
bar(); // 2