2025年4月1日 星期二 乙巳(蛇)年 正月初二 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > JavaScript

JS执行上下文和活动对象

时间:03-07来源:作者:点击数:70

在《JS变量》一节中我们曾介绍过变量的作用域,JavaScript 支持全局作用域和局部作用域。这个局部作用域也就是函数作用域,局部变量在函数内可见,也称为私有变量。

作用域

作用域(Scope)表示变量的作用范围、可见区域,包括词法作用域和执行作用域。

  • 词法作用域:根据代码的结构关系来确定作用域。词法作用域是一种静态的词法结构,JavaScript 解析器主要根据词法结构确定每个变量的可见性和有效区域。
  • 执行作用域:当代码被执行时,才能够确定变量的作用范围和可见性。与词法作用域相对,它是一种动态作用域,函数的作用域会因为调用对象不同而发生变化。

JavaScript 支持词法作用域,JavaScript 函数只能运行在被预先定义好的词法作用域里,而不是被执行的作用域里。

执行上下文和活动对象

JavaScript 代码是按顺序从上到下被解析的,当然 JavaScript 引擎并非逐行的分析和执行代码,而是逐段的去分析和执行。当执行一段代码时,先进行预处理,如变量提升、函数提升等。

JavaScript 可执行代码包括 3 中类型:全局代码、函数代码、eval 代码。每执行一段可执行代码,都会创建对应的执行上下文。在脚本中可能存在大量的可执行代码段,所以 JavaScript 引擎先创建执行上下文栈,来管理脚本中所有执行上下文。

执行上下文是一个专业术语,比较抽象,实际上就是在内存中开辟的一块独立运行的空间。执行上下文栈相当于一个数组,数组元素就是一个个独立的执行上下文区域。

当 JavaScript 开始解释程序时,最先遇到的是全局代码,因此在初始化程序的时候,首先向执行上下文栈压入一个全局执行上下文,并且只有当整个应用程序结束的时候,全局执行上下文才被清空。

当执行一个函数的时候,会创建一个函数的执行上下文,并且压入到执行上下文栈,当函数执行完毕,会将函数的执行上下文栈中弹出。

每个执行上下文都有 3 个基本属性:变量对象、作用域链和 this。

变量对象是与执行上下文相关的数据作用域,存储了在上下文中定义的变量和函数声明。JavaScript 代码不能直接访问该对象,但是可以访问该对象的成员(如 arguments)。不同代码段中的变量对象也不相同,简单说明如下。、

1. 全局上下文的变量对象

全局上下文的变量对象,初始化是全局对象。

全局对象是预定义对象,作为 JavaScript 的全局函数和全局属性的占位符。通过全局对象,可以访问其他所有预定义的对象、函数和属性。

在客户端 JavaScript 中,全局对象是 window 对象,通过 window 对象的 window 属性指向自身。

【实例1】下面代码演示了在全局作用域中声明变量 b,并赋值,然后通过 window 对象的属性 b 来读取这个全局变量值。同时演示了使用 this 访问 window 对象,使用 this.window 同样可以访问 window 对象。

  • var b = true;
  • console.log(window.b); //true
  • this.window.b = false;
  • console.log(this.b); //false

2. 函数上下文的变量对象

变量对象是 ECMAScript 规范术语。在一个执行上下文中,变量对象才被激活。只有激活的变量对象,其各种属性才能被访问。

在函数执行上下文中,变量对象常常被称为活动对象,两者意思相同。活动对象是在进入函数上下文时被创建,初始化时只包括 Arguments 对象。它通过函数的 arguments 属性访问,arguments 属性值为 Arguments 对象。

函数执行上下文的代码处理可以分成两个阶段:分析和执行,简单说明如下。

1) 进入执行上下文。当进入执行上下文时,不会执行代码,只进行分析。此时变量对象包括:

  • 函数的所有形参(如果是函数上下文)——由名称和对应值组成的一个变量对象的属性被创建。如果没有实参,属性值设为 undefined。
  • 函数声明——由名称和对应值(函数对象)组成一个变量对象的属性被创建。如果变量对象已经存在相同名称的属性,则会完全替换这个属性。
  • 变量声明——由名称和对应值(undefined)组成的一个变量对象的属性被创建。如果变量名称与已经声明的形参或函数相同,则变量声明不会覆盖已经存在的这类属性。

【实例2】在进入函数执行上下文时,会给变量对象添加形参、函数声明、变量声明等初始的属性值。下面代码简单演示了这个阶段的处理过程。

  • function f(a) { //声明外部函数
  • var b = 1; //声明局部变量,并赋值1
  • function c() {} //声明内部函数
  • var d = function () {}; //声明局部变量,并赋值为匿名函数
  • b = 2; //修改变量b的值为2
  • }
  • f(3); //调用函数,并传入实参值3

在进入函数执行上下文后,活动对象的结构模拟如下。

  • AO = {
  • arguments : {
  • 0 : 3, //实参值
  • length : 1 //实参长度
  • },
  • a : 3, //实参值
  • b : undefined, //声明局部变量b
  • c : function c() {} //声明函数c,引用function c() {}
  • d : undefined //声明局部变量d
  • }

2) 执行代码。在代码执行阶段会按顺序执行代码,这时可能会修改变量对象的值。

【实例3】在代码执行阶段,可能会修改变量对象的属性值。针对上面示例,当代码执行完后,活动对象的结构模拟如下。

  • AO = {
  • arguments : {
  • 0 : 3, //实参值
  • length : 1 //实参长度
  • },
  • a : 3, //实参值
  • b : 1, //初始化赋值
  • c : function () {}, //引用声明的函数c
  • d : function () {} //引用函数表达式"d"
  • }
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门