资讯专栏INFORMATION COLUMN

彻底理解Javascript中的变量对象

Shimmer / 3268人阅读

摘要:活动对象的变化与处理上下文的两个阶段密切相关。所有变量声明由名称和对应值组成一个变量对象的属性被创建如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

1 定义

如果变量与执行上下文相关,那变量自己应该知道它的数据存储在哪里,并且知道如何访问。这种机制称为变量对象(variable object)。
变量对象(缩写为VO)是一个与执行上下文相关的特殊对象,它存储着在上下文中声明的以下内容:

变量 (var, 变量声明);

函数声明 (FunctionDeclaration, 缩写为FD);

函数的形参

举例来说,我们可以用普通的ECMAScript对象来表示一个变量对象:

VO = {};

就像我们所说的, VO就是执行上下文的属性(property):

activeExecutionContext = {
     VO: {
       // 上下文数据(var, FD, function arguments)
      }
};
2 全局上下文中的变量对象

全局上下文中的变量对象就是全局对象,所以我们声明的变量都是全局对象的属性。

3 函数上下文中的变量对象

函数上下文中的变量对象由活动对象(AO)扮演。

活动对象的变化与处理上下文的两个阶段密切相关。进入执行上下文和执行代码。

3.1 进入执行上下文

当进入执行上下文(代码执行之前)时,VO里已经包含了下列属性:

1. 函数的所有形参(如果我们是在函数执行上下文中)
   由名称和对应值组成的一个变量对象的属性被创建;没有传递对应参数的话,那么由名称和undefined值组成的一种变量对象的属性也将被创建。
2. 所有函数声明(FunctionDeclaration, FD)
由名称和对应值(函数对象(function-object))组成一个变量对象的属性被创建;如果变量对象已经存在相同名称的属性,则完全替换这个属性。
3. 所有变量声明(var, VariableDeclaration)
   由名称和对应值(undefined)组成一个变量对象的属性被创建;如果变量名称跟已经声明的形式参数或函数相同,则变量声明不会干扰已经存在的这类属性。

也就是变量提升和var x = undefined 不会影响形参x的值,之后调用x指向的还是形参。

注意:进入执行上下文时,函数声明和变量声明都提前,但是变量声明的值还都是undefined,而函数声明的变量已经可以指向函数。变量声明的优先级最低。
让我们看一个例子:

function test(a, b) {
  var c = 10;
  function d() {}
  var e = function _e() {};
  (function x() {});
}
  
test(10); // call

当进入带有参数10的test函数上下文时,AO表现为如下:

AO(test) = {
  a: 10,
  b: undefined,
  c: undefined,
  d: 
  e: undefined
};

注意,AO里并不包含函数“x”。这是因为“x” 是一个函数表达式(FunctionExpression, 缩写为 FE) 而不是函数声明,函数表达式不会影响VO。 不管怎样,函数“_e” 同样也是函数表达式,但是就像我们下面将看到的那样,因为它分配给了变量 “e”,所以它可以通过名称“e”来访问。

3.2 代码执行

进入上下文阶段,AO/VO已经拥有了属性(不过,并不是所有的属性都有值,大部分属性的值还是系统默认的初始值undefined )。
还是前面那个例子, AO/VO在代码执行期间被修改如下:

AO["c"] = 10;
AO["e"] = ;

另一个经典例子:

alert(x); // function
  
var x = 10;
alert(x); // 10
  
x = 20;
  
function x() {};
  
alert(x); // 20

为什么第一个alert “x” 的返回值是function,而且它还是在“x” 声明之前访问的“x” 的?为什么不是10或20呢?因为,根据规范函数声明是在当进入上下文时填入的; 同意周期,在进入上下文的时候还有一个变量声明“x”,那么正如我们在上一个阶段所说,变量声明在顺序上跟在函数声明和形式参数声明之后,而且在这个进入上下文阶段,变量声明不会干扰VO中已经存在的同名函数声明或形式参数声明,因此,在进入上下文时,VO的结构如下:

VO = {};
  
VO["x"] = 
  
// 找到var x = 10;
// 如果function "x"没有已经声明的话
// 这时候"x"的值应该是undefined
// 但是这个case里变量声明没有影响同名的function的值
  
VO["x"] = 
//紧接着,在执行代码阶段,VO做如下修改:
VO["x"] = 10;
VO["x"] = 20;

我们可以在第二、三个alert看到这个效果。
在下面的例子里我们可以再次看到,变量是在进入上下文阶段放入VO中的。(因为,虽然else部分代码永远不会执行,但是不管怎样,变量“b”仍然存在于VO中。)

if (true) {
 var a = 1;
} else {
 var b = 2;
}
  
alert(a); // 1
alert(b); // undefined,不是b没有声明,而是b的值是undefined

本文绝大部分内容来自: http://dmitrysoshnikov.com/ec...
仅做少许修改

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86610.html

相关文章

  • 初学者彻底理解javascript闭包以及this关键字

    摘要:理解了这句话,我们就可以来看闭包了闭包前面说过,函数可以访问函数作用域链中的变量,但如果我们想在函数外访问函数内却不行了。 不管是闭包还是this关键字,都是困扰JS初学者的比较难懂的东西,如果你对它们的认识还不足够清晰,那么现在就一起把它们掌握掉。还是那句话,我们从最基本的开始,建立起一个非常清晰的知识结构,好了,开始吧 ? 闭包 当然我们今天说的是javascript里的闭包。要学...

    魏明 评论0 收藏0
  • 彻底理解Javascript中的作用域链

    摘要:全局和上下文中的作用域链这里不一定很有趣,但必须要提示一下。全局上下文的作用域链仅包含全局对象。代码的上下文与当前的调用上下文拥有同样的作用域链。代码执行时对作用域链的影响在中,在代码执行阶段有两个声明能修改作用域链。 1 定义 我们已经知道一个执行上下文中的数据(参数,变量,函数)作为属性存储在变量对象中。 也知道变量对象是在每次进入上下文是创建并填入初始值,值的更新出现在代码执行阶...

    zhunjiee 评论0 收藏0
  • JavaScript之例题中彻底理解this

    摘要:最后重点理解结论箭头函数的,总是指向定义时所在的对象,而不是运行时所在的对象。输出,箭头函数不会绑定所以传入指向无效。原因是,要彻底理解应该是建立在已经大致理解了中的执行上下文,作用域作用域链,闭包,变量对象,函数执行过程的基础上。 本文共 2025 字,看完只需 8 分钟 概述 前面的文章讲解了 JavaScript 中的执行上下文,作用域,变量对象,this 的相关原理,但是我...

    Hwg 评论0 收藏0
  • 面向对象JavaScript

    摘要:是完全的面向对象语言,它们通过类的形式组织函数和变量,使之不能脱离对象存在。而在基于原型的面向对象方式中,对象则是依靠构造器利用原型构造出来的。 JavaScript 函数式脚本语言特性以及其看似随意的编写风格,导致长期以来人们对这一门语言的误解,即认为 JavaScript 不是一门面向对象的语言,或者只是部分具备一些面向对象的特征。本文将回归面向对象本意,从对语言感悟的角度阐述为什...

    novo 评论0 收藏0
  • javascript对象不完全探索记录01:this! which?

    温馨提示:作者的爬坑记录,对你等大神完全没有价值,别在我这浪费生命 这一切,源于阮大神博文学习Javascript闭包(Closure)- 阮一峰中的一道思考题 //问题1: var name = The Window; var object = {   name : My Object,   getNameFunc : function(){     return function(){    ...

    rubyshen 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<