在JavaScript中,变量对象是什么?本文首先介绍了变量对象的概念,以及上下文中的变量对象怎样执行的,变量对象中的代码是如何被处理的,最后又介绍了变量是什么?
变量对象就是执行上下文和作用域链中间的桥梁。
剧透一下,神秘的 this 就存在于执行上下文环境之中!
当然,之后我会单独用几节来彻底讲明白 this 到底是什么(其实 this 很简单)。
接下来,我们进入正文。
1. 执行上下文包含什么
一个执行上下文我们可以抽象的理解为对象(object)。
每一个执行上下文都有一些属性(又称为上下文状态),它们用来追踪关联代码的执行进度。
我用一个结构图来说明:
Variable Object 就代表变量对象。
Scope Chain 代表作用域链。
thisValue 代表神秘的 this 。
作用域链和 this 留到后面再讲,今天我们先来弄明白变量对象。
2. 变量对象
A variable object is a scope of data related with the execution context. It’s a special object associated with the context and which stores variables and function declarations are being defined within the context.变量对象(variable object) 是与执行上下文相关的数据作用域(scope of data) 。它是与上下文关联的特殊对象,用于存储被定义在上下文中的 变量(variables) 和 函数声明(function declarations) 。
变量对象(Variable Object — 简写 VO)是一个抽象的概念,指代与执行上下文相关的特殊对象,它存储着在上下文中声明的:
– 变量(var)
– 函数声明 (function declaration,简写 FD)
– 函数的形参(arguments)
我们假设变量对象为一个普通 ECMAScript 对象:
VO = {};
登录后复制
就像前面讲过的,VO 是执行上下文的一个属性:
activeExecutionContext = { VO: { // 上下文数据 (vars, FD, arguments) }}
登录后复制
因为变量对象是一个抽象的概念,所以并不能通过变量对象的名称直接访问,但是却可以通过别的方法来间接访问变量对象,例如在全局上下文环境的变量对象会有一个属性 window (DOM 中) 可以引用变量对象自身,全局上下文环境的另一个属性 this 也指向全局上下文环境的变量对象。
举个例子:
var a = 2;function foo (num) { var b = 5;}(function exp () { console.log(111);})foo(10);
登录后复制
这里对应的变量对象是:
// 全局上下文环境的变量对象VO(globalContext) = { // 一些全局环境初始化时系统自动创建的属性: Math、String、Date、parseInt等等 ··· // 全局上下文的变量对象中有一个属性可以访问到自身,在浏览器中这个属性是 window ,在 node 中这个属性是 global window: global // 自己定义的属性 a: 10, foo: };// foo 函数上下文的变量对象VO(foo functionContext) = { num: 10, b: 5};
登录后复制
注意:函数表达式并不包括在变量对象中。
3. 不同执行上下文中的变量对象
执行上下文包括:全局上下文、函数上下文和 eval() 上下文。
全局上下文中的变量对象
这里我们先来了解一下什么是全局对象:
全局对象(global object)是指在进入任何执行上下文之前就已经创建了的对象。这个对象只有一份,它的属性在程序中的任何地方都可以访问,全局对象的生命周期终止于程序退出的那一刻。
登录后复制
全局对象初始化时系统将创建并初始化一系列原始属性,例如:Math、String、Date、parseInt、window等等,之后是我们在全局上下文中自己定义的全局变量。在 DOM 中,全局对象的 window 属性可以引用全局对象自身,全局上下文环境的 this 属性也可以引用全局对象。
// 全局执行上下文环境EC(globalContext) = { // 全局对象(全局上下文环境的变量对象) global: { Math: , String: , ... ... window: global // 引用全局对象自身 }, // this 属性 this: global // 作用域链 ...}
登录后复制
举个例子:
var a = 10;console.log(a); // 10console.log(window.a); // 10console.log(this.a); // 10
登录后复制
因此,在全局上下文环境中,变量对象用全局对象来表示。
函数上下文中的变量对象
在函数上下文中,变量对象用活动对象 AO(Active Object)来表示。
VO(functionContext) = AO
登录后复制
活动对象是在进入函数上下文时刻被创建的,它是通过函数的 arguments 属性进行初始化。arguments 也是一个对象。
AO = { arguments: { ... }}
登录后复制
arguments 是活动对象的一个属性,它也是一个对象,包括以下属性:
1. callee – 指向当前函数的引用
2. length – 真正传递的参数个数
3. properties-indexes – index 是字符串类型的整数,例如”1″: “aa”,类似于数组类型,也可以通过arguments[1]来访问,但是不能用数组的方法(push, pop等等)。另外,properties-indexes 的值和实际传递进来的参数之间是共享的,一个改变,另一个也随之改变。
举个例子:
function foo (x, y, z) { // 声明的函数参数数量 console.log(foo.length); // 3 // 实际传递进来的参数数量 console.log(arguments.length); // 2 // arguments 的 callee 属性指向当前函数 console.log(arguments.callee === foo) // true // 参数共享 console.log(x === arguments[0]); // true console.log(x); // 10 arguments[0] = 20; console.log(x); // 20 x = 30; console.log(arguments[0]); // 30 // 但是注意,没有传递进来的参数 z ,和第3个索引值是不共享的 z = 40; console.log(arguments[2]); // undefined arguments[2] = 50; console.log(z); // 40}foo(10, 20);
登录后复制
4. 代码是如何被处理的
在第1节中我们讲过js 代码的编译过程,其中有一步叫作预编译,是说在代码执行前的几微秒会首先对代码进行编译,形成词法作用域,然后执行。
那么执行上下文的代码就就可以分成两个阶段来处理:
1. 进入执行上下文(预编译)
2. 执行代码
而变量对象的修改变化和这两个阶段是紧密相关的。
并且所有类型的执行上下文都会有这2个阶段。
进入执行上下文
当引擎进入执行上下文时(代码还未执行),VO 里已经包含了一些属性:
1. 函数的所有形参(如果是函数执行上下文)
由名称和对应值组成的一个变量对象的属性被创建,如果没有传递对应的实参,那么由名称和 undefined 组成的一种变量对象的属性也会被创建。
2.所有的函数声明(Function Declaration – FD)
由名称和对应值(函数对象 function object)组成的一个变量对象的属性被创建,如果变量对象已经存在相同名称函数的属性,则完全替换这个属性。
3.所有的变量声明(Variable Declaration – var)
由名称和对应值(在预编译阶段所有变量值都是 undefined)组成的一个变量对象的属性被创建,如果变量名和已经声明的形参或者函数相同,则变量名不会干扰已经存在的这类属性,如果已经存在相同的变量名,则跳过当前声明的变量名。
注意:变量碰到相同名称的变量是忽略,函数碰到相同名称的函数是覆盖。
举个例子:
function test(a, b, c) { console.log(a); // 函数体a console.log(b); // 20 function a() { console.log(1); } var a = 100; console.log(a); // 100 var b = 2; console.log(b); // 2}test(10,20,30);
登录后复制
function foo (a, b) { var c = 5; function bar () {}; var d = function _d () {}; (function f () {});}foo(10);
登录后复制
当进入带有实参10的 foo 函数上下文时(预编译时,此时代码还没有执行),AO 结构如下:
AO(foo) = { a: 10, b: undefined, c: undefined, bar: , d: undefined };
登录后复制
注意,函数表达式 f 并不包含在活动对象 AO 内。
也就是说,只有函数声明会被包含在变量对象 VO 里面,函数表达式并不会影响变量对象。
行内函数表达式 _d 则只能在该函数内部可以使用, 也不会包含在 VO 内。
这之后,就会进入第2个阶段,代码执行阶段。
代码执行
在这个阶段,AO/VO 已经有了属性(并不是所有的属性都有值,大部分属性的值还是系统默认的初始值 undefined)。
AO 在代码执行阶段被修改如下:
AO['c'] = 5;AO['d'] =
登录后复制
再次要提醒大家,因为函数表达式 _d 已经保存到了声明的变量 d 上面,所以变量 d 仍然存在于 VO/AO 中。我们可以通 d() 来执行函数。但是函数表达式 f 却不存在于 VO/AO 中,也就是说,如果我们想尝试调用 f 函数,不管在函数定义前还是定义后,都会出现一个错误”f is not defined”,未保存的函数表达式只有在它自己的定义或递归中才能被调用。
再来一个经典例子:
console.log(x); // functionvar x = 10;console.log(x); // 10x = 20;function x () {};console.log(x); // 20
登录后复制
这里为什么是这样的结果呢?
上边我们说过,在代码执行之前的预编译,会为变量对象生成一些属性,先是形参,再是函数声明,最后是变量,并且变量并不会影响同名的函数声明。
所以,在进入执行上下文时,AO/VO 结构如下:
AO = { x: // 在碰到变量声明 x 时,因为已经存在了函数声明 x ,所以会忽略}
登录后复制
紧接着,在代码执行阶段,AO/VO 被修改如下:
AO['x'] = 10;AO['x'] = 20;
登录后复制
希望大家可以好好理解变量对象,对于理解我们后边要讲的作用域链有很大的帮助。
5. 变量
有一些文章说过:
不管是使用 var 关键字(在全局上下文)还是不使用 var 关键字(在任何地方),都可以声明一个变量。
请记住,这是错误的观念。
任何时候,变量都只能通过使用 var 关键字来声明(ES6 之前)。
a = 10;
登录后复制
上面的赋值语句,仅仅是给全局对象创建了一个新属性(在非严格模式,严格模式下会报错),但注意,它不是变量。“不是变量”并不是说它不能被改变,而是指它不符合ECMAScript 规范中变量的概念。
让我们通过一个例子来看一下两者的区别:
console.log(a); // undefinedconsole.log(b); // 报错,b is not definedb = 10;var a = 20;
登录后复制
只要我们很好的理解了:变量对象、预编译阶段和执行代码阶段,就可以迅速的给出答案。
预编译(进入上下文)阶段:
VO = { a: undefined}
登录后复制
我们可以看到,因为 b 不是通过 var 声明的,所以这个阶段根本就没有 b ,b 只有在代码执行阶段才会出现。但是在这个例子中,还没有执行到 b 那就已经报错了。
我们稍微更改一下示例代码:
console.log(a); // undefinedb = 10;console.log(b); // 10 代码执行阶段被创建console.log(window.b); // 10console.log(this.b); // 10var a = 20;console.log(a); // 20 代码执行阶段被修改
登录后复制
关于变量,还有一个很重要的知识点。
变量不能用 delete 操作符来删除。
a = 10;console.log(window.a); // 10console.log(delete a); // trueconsole.log(window.a); // undefinedvar b = 20;console.log(window.b); // 20console.log(delete b); // falseconsole.log(window.b); // 20
登录后复制
注意:这个规则在 eval() 上下文中不起作用。
eval('var a = 10;');console.log(window.a); // 10console.log(delete a); // trueconsole.log(window.a); // undefined
登录后复制
相关推荐:
js高级面向对象和组件开发视频教程
js 多种变量定义(对象直接量,数组直接量和函数直接量)_javascript技巧
以上就是什么是JS变量对象?JS变量对象详解以及注意事项的详细内容,更多请关注【创想鸟】其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。
发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2745498.html