javascript 之变量对象-09
变量对象
在《javascript 之执行环境-08》文中说到,当JavaScript代码执行一段可执行代码时,会创建对应的执行上下文(execution context)。对于每个执行上下文,都有三个重要属性:
- 变量对象(Variable object,VO)
- 作用域链(Scope chain)
- this
每个执行环境(执行上下文)都有一个对应的变量对象(variable object),环境中形参、函数、定义的所有变量都保存在变量对象中。
创建变量对象过程
因创建执行环境分为两个阶段,所以变量对象也分两个阶段来分析:
- 初始化(当函数被调用,未执行代码之前)
- 代码执行阶段(开始执行代码)
初始化
VO包括:
1、根据函数的参数,创建并初始化arguments object,值为默认值 undefined
2、扫描该执行上下文中的函数声明(不包括函数表达式)
a) 找到所有的function 声明,将函数名当做属性创建,值为函数定义
b) 在扫描过程中如果存在重名的函数声明,那么后面的会覆盖前面的声明,函数声明与变量声明有冲突时,会忽略(以函数声明为主)
3、 扫描该执行上下文中的var变量声明
a) 找到所有的变量声明,将变量名当做属性创建,值初始为undefined
b) 在扫描过程中如果存在重名的变量声明以及重名的函数声明,会忽略;
如下代码:
1 function foo(name) { 2 var age = 20; 3 function run() {} 4 var say = function() {}; 5 age = 22; 6 } 7 foo('Joel');
创建执行环境时初始化的变量对象(vo)如下:
1 foo.EC.VO = { 2 arguments: { 3 0: 'Joel', 4 length: 1 5 }, 6 name: 'Joel',//参数 7 age: undefined, 8 run: reference to function run(){}, 9 say: undefined 10 }
执行阶段
在代码执行阶段,会顺序执行代码,根据代码执行顺序,修改AO(执行阶段执行环境被推入执行环境栈 VO 被激活成为AO)中变量的值 如:
1 foo.EC.VO = { 2 arguments: { 3 0: 'Joel', 4 length: 1 5 }, 6 name: 'Joel',//形参 7 age: 20, 8 run: reference to function run(){}, 9 say: reference to FunctionExpression say 10 }
通过VO创建过程来看变量提升(Hoisting)
变量提升的本质就是函数在创建执行环境时变量对象初始化下了形参、函数声明、var 声明的变量;
1 <script> 2 function run(){ 3 console.log(name); 4 console.log(say); 5 var name='Joel'; 6 function say(){ 7 console.log('say'); 8 } 9 } 10 run(); 11 </script>
如上代码可以理解为这样:
1 function run(){ 2 function say(){ //Hoisting 3 console.log('say'); 4 } 5 var name=undefined; //Hoisting 6 7 console.log(name); 8 console.log(say); 9 name='Joel'; 10 11 } 12 run();
思考题
第一题
1 <script> 2 function run() { 3 console.log(a); 4 a = 1; 5 } 6 run(); // ? 7 function say() { 8 a = 1; 9 console.log(a); 10 } 11 say(); // ? 12 </script>
第一段会报错:Uncaught ReferenceError: a is not defined
。第二段会打印:1
。
1 function foo(name) { 2 3 console.log(run);// 输出run 函数定义 4 console.log(say); //undefined 5 function run() {} 6 var say = function () {}; 7 } 8 foo('Joel');
函数表达式会当做一个var 变量来处理
第二题
1 console.log(run) 2 function run() { 3 console.log(a); 4 a = 1; 5 } 6 var run=1;
输出: ƒ run() {
console.log(a);
a = 1;
}
当声明的变量与函数重名时,声明的变量会忽略;
如果在第6行代码后面添加 console.log(run),那么run 值会被重置为1,因为在上下文对象创建阶段发现已经存在run的函数声明,var 变量会被忽略,当代码在真正执行到6行时run的值被改变了;
1 console.log(run) 2 function run() { 3 console.log(a); 4 a = 1; 5 } 6 var run=1; 7 console.log(run)
全局上下文中的VO
上篇中说到执行环境分为全局执行环境,函数执行环境,在本文开篇说到每个执行环境对应一个变量对象:
每个执行环境(执行上下文)都有一个对应的变量对象(variable object),环境中(执行上下文中)定义的所有形参、变量、函数都保存在这个对象中,那么全局执行环境是不是可以理解为也存在一个全局变量对象。
我们先了解一个概念,什么叫叫全局对象。在 W3School 中也有介绍:
全局对象是预定义的对象,作为 JavaScript 的全局函数和全局属性的占位符。通过使用全局对象,可以访问所有其他所有预定义的对象、函数和属性。
在顶层 JavaScript 代码中,可以用关键字 this 引用全局对象。但通常不必用这种方式引用全局对象,因为全局对象是作用域链的头,这意味着所有非限定性的变量和函数名都会作为该对象的属性来查询。
例如,当JavaScript 代码引用 parseInt() 函数时,它引用的是全局对象的 parseInt 属性。全局对象是作用域链的头,还意味着在顶层 JavaScript 代码中声明的所有变量都将成为全局对象的属性。
1.可以通过 this 引用,在客户端 JavaScript 中,全局对象就是 Window 对象。
console.log(this);
2.全局对象是由 Object 构造函数实例化的一个对象。
console.log(this instanceof Object);
3.预定义了一大堆函数和属性。
// 都生效 console.log(Math.random()); console.log(this.Math.random());
4.作为全局变量的宿主。
var a = 1; console.log(this.a);
5.客户端 JavaScript 中,全局对象有 window 属性指向自身。
var a = 1; console.log(window.a); this.window.b = 2; console.log(this.b);
写了这么多介绍全局对象,其实就是想说:全局上下文中的变量对象就是window!
活动对象(AO)与变量对象(VO)区别
它们其实都是同一个对象,只是处于执行上下文的不同生命周期。未进入执行阶段之前,变量对象(VO)中的属性都不能访问,但是进入执行阶段之后,执行环境被压入执行环境栈变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。
总结
- 全局上下文的变量对象是全局对象
- 函数上下文的变量对象初始化,最开始只包括 arguments 对象
- 在创建执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值
- 在代码执行阶段,会再次修改变量对象的属性值
- 创建执行环境----》初始化变量对象(AO)(参数、函数声明、var 变量)---》执行环境被推入栈----》执行代码-----》VO 激活为AO ------》改变AO 的值
如果您觉得阅读本文对您有帮助,请点一下推荐按钮,您的推荐将是我最大的写作动力,欢迎各位转载!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)