You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Variable object of the global contextVO(globalContext)={a: 10,test: <referencetofunction>};// Variable object of the "test" function contextVO(testfunctionContext)={x: 30,b: 20};
vara=newString('test');alert(a);// directly, is found in VO(globalContext): "test"alert(window['a']);// indirectly via global === VO(globalContext): "test"alert(a===this.a);// truevaraKey='a';alert(window[aKey]);// indirectly, with dynamic property name: "test"
functionfoo(x,y,z){// quantity of defined function arguments (x, y, z)alert(foo.length);// 3// quantity of really passed arguments (only x, y)alert(arguments.length);// 2// reference of a function to itselfalert(arguments.callee===foo);// true// parameters sharingalert(x===arguments[0]);// truealert(x);// 10arguments[0]=20;alert(x);// 20x=30;alert(arguments[0]);// 30// however, for not passed argument z,// related index-property of the arguments// object is not sharedz=40;alert(arguments[2]);// undefinedarguments[2]=50;alert(z);// 40}foo(10,20);
VO = {};
VO['x'] = <reference to FunctionDeclaration "x">
// found var x = 10;
// if function "x" would not be already defined
// then "x" be undefined, but in our case
// variable declaration does not disturb
// the value of the function with the same name
VO['x'] = <the value is not disturbed, still function>
介绍
本文中,我们将分析与ECMAScript执行上下文相关的概念
变量对象 Variable Object
先举个栗子🌰,为什么a b x表现大不相同,当引用一个函数或者变量时,解释器是如何以及从哪里找到它们的呢
定义
然变量和执行上下文有关,那它应该知道数据存放在哪以及如何获取,这种机制称为变量对象
示意地示例,可以用ECMAScript的对象来表示变量对象:
变量对象同时也是执行上下文EC的一个属性:
对变量的间接引用(通过VO的属性名)只允许发生在全局上下文中的变量对象上(全局对象本身就是变量对象), 对于其他的上下文,是无法直接引用VO的,因为VO是实现层机制
声明新的变量或函数的过程其实就是用变量或函数的名称和值在VO中创建新的属性的过程:
上面👆代码对应的变量对象如下:
但是,但是,在实现层(标准中定义的),变量对象只是一个抽象概念
不同执行上下文中的变量对象
变量对象上的一些操作(比如:变量的初始化)和行为对于所有的执行上下文类型来说都一样,从这点来说,将变量对象表示成抽象的概念更加便捷
全局上下文中的变量对象
首先对全局对象(Global object)作个定义
全局对象在创建时,Math,String,Date,parseInt等属性也会被初始化,同时,其中一些对象会指向全局对象本身,如BOM中,全局对象上的window属性就指向全局对象
在引用全局对象的属性时,前缀通常可以省略,因为全局对象是不能通过名字直接访问的,然而,通过全局对象上的this值,以及通过BOM中的window对象这样递归引用的方式都可以访问到全局对象
回到全局上下文的变量对象上,这里变量对象就是全局对象本身
准确地理解这个事实是非常必要的,正是由于这个原因,当在全局上下文中声明一个变量时,可以通过全局对象上的属性来间接地引用该变量(比如,当变量名不能提前预知的情况下)
函数上下文中的变量对象
在函数的执行上下文中,VO是不能直接访问的,它主要扮演活跃对象(activation object)(简称AO)的角色
Arguments对象是活动对象的属性,它包含的属性如下:
处理上下文代码的阶段
处理执行上下文代码分为两个阶段:
对变量对象的修改和这两个阶段密切相关
要注意的是,这两个处理阶段的行为是通用的,与上下文类型无关(不管是全局上下文还是函数上下文)
进入执行上下文
一旦进入执行上下文(但是在执行代码之前),VO就会被填充如下的一些属性:
变量对象的属性,其属性名就是形参的名字,其值就是实参的值,对于没有传递的参数,其值为undefined
变量对象的属性,其属性名和值就是函数对象的名称和值,如果变量对象已经包含具有相同名称的属性,则替换它的值
变量对象的属性,其属性名即为变量名,其值为undefined,如果变量名和已经声明的函数名或函数参数名相同,则不会影响已经存在的属性
看个栗子🌰:
当以10为参数进入test函数上下文时,对应的AO如下:
注意,上面的AO并不包含函数
x
,因为这里的x
不是函数声明而是函数表达式(FunctionExpression,简称FE),函数表达式不会影响VO, 尽管函数_e
也是函数表达式,然而,由于它被赋值给变量e
,因此它可以通过e
来访问到至此,处理上下文代码的第一阶段介绍完了,接下来介绍第二阶段:执行代码阶段
执行代码
此时AO/VO的属性已经填充好,(尽管大部分属性都还没有赋予真正的值,都只是初始化时的undefined)
以上一例子为例,到了执行代码阶段,AO/VO会修改为如下形式:
再次强调,这里函数表达式
_e
仍在内存中,因为它被保存在声明的变量e
中,而同样是函数表达式的x
却不在AO/VO中,如果尝试在定义前或者定义后调用x
函数,这时会发生x is not defined
错误,未保存的函数表达式只有在定义或递归时才能调用一个更加典型的例子:
上例中,为何
x
打印出来是函数呢,为何在声明前就可以访问到,为何10或者20不是这样呢,原因在于,根据规则,在进入上下文时,VO会被函数声明填充,同时还有变量声明x
,但是,变量声明是在函数声明和函数形参之后,并且变量声明不会与已经存在的同名的函数声明和函数形参冲突, 因此在进入上下文的阶段,VO填充为如下形式:随后在执行代码阶段,VO被修改为如下:
在如下例子中,再次看到,在进入上下文阶段,变量存储在VO中(因此,尽管else代码块永远都不会执行,而
b
却仍然在VO中)关于变量
一些JavaScript文章甚至是JavaScript书籍经常会说:
声明全局变量的方式有两种,一种是使用var关键字(在全局上下文中),另外一种是不用var关键字(在任何位置)
,这样的描述是错误的,记住:像下面👇的赋值语句:
仅仅是在全局对象上创建了新的属性(而不是变量),
不是变量
并不意味着它无法改变,而是按照ECMAScript中变量的概念不是变量
不同之处如下:
所有这些都取决于VO和其修改的阶段(进入上下文阶段和代码执行阶段)
进入上下文阶段:
这个阶段并没有任何
b
,因为它不是变量,b
在执行代码阶段才出现将上述代码改动一下:
关于变量还有一点非常重要:与简单属性不同,变量是不能删除的
{DontDelete}
,这意味着要想通过delete
操作符来删除一个变量是不可能的注意,在ES5中
{DontDelete}
被重命名为[[Configurable]]
,并且可以通过Object.defineProperty
方法手动管理然而,有一个执行上下文不会被这个规则影响到,这就是
eval上下文
,其中没有为变量设置{DontDelete}
属性引用
ECMA-262-3 in detail. Chapter 2. Variable object.
The text was updated successfully, but these errors were encountered: