深入解析JavaScript执行环境、作用域链与垃圾回收机制

作者:青柠时间:2026-10-30 07:54:03浏览:240

本文详细解析JavaScript的执行环境、变量对象及作用域链的构建原理,阐述标识符解析与with/try-catch对作用域的影响。同时深入探讨垃圾回收机制,对比标记清除与引用计数两种策略,并分析循环引用导致的内存泄漏问题,帮助开发者理解JS内存管理核心逻辑。

JavaScript执行环境与变量对象

执行环境界定了变量或函数可访问的数据范围,并决定了其具体行为。每个执行环境均关联一个变量对象。

全局执行环境位于最外层。其表示对象依JavaScript宿主环境而异。在Web浏览器中,全局执行环境对应window对象,故所有全局变量和函数均作为window对象的属性或方法创建。

变量对象用于保存环境中定义的所有变量与函数。

作用域链与标识符解析

作用域链在代码执行时创建,旨在确保对执行环境中所有可访问变量和函数的有序访问。其前端始终为当前执行代码所在环境的变量对象。

活动对象初始仅含arguments变量。作用域链的后续变量对象源自包含(外部)环境,直至全局执行环境,其变量对象始终位于作用域链末端。

标识符解析沿作用域链逐级搜索标识符。搜索自作用域链前端开始,逐级向后回溯直至找到目标标识符。

示例代码:


var color = "blue";
function changeColor() {
  if (color === "blue") {
    color = "red";
  } else {
    color = "blue";
  }
}
changeColor();

alert("Color is now " + color);

函数changeColor()的作用域链包含两个对象:其自身的变量对象(含arguments对象)及全局变量的变量对象。因作用域链中存在该变量,故可在函数内部访问color。

此外,局部作用域中定义的变量可与全局变量互换使用,示例:


var color = "blue";
function changeColor() {
  var anotherColor = "red";

  function swapColors() {
    var tempColor = anotherColor;
    anotherColor = color;
    color = tempColor;

    // 这里可以访问color、anotherColor和tempColor
  }

  // 这里可以访问color、anotherColor,不能访问tempColor
  swapColors();
} 

// 这里只能访问color
changeColor();

上述代码涉及3个执行环境:全局环境、changeColor()的句柄环境以及swapColors()的局部环境。

全局变量包含color变量与changeColor()函数。changeColor()的局部变量含anotherColor变量及swapColors()函数,后者可访问全局变量color。swapColors()的局部变量含tempColor。在swapColors()中可访问全局变量color及anotherColor,因二者为其父执行环境。上述例子的作用域链为:

  JavaScript执行环境与变量对象

内部环境可通过作用域链访问所有外部环境,但外部环境无法访问内部环境的变量或函数。环境变量间联系呈线性有序。每个变量仅能向上级搜索作用域链以查询变量和函数名,即先于当前作用域查询,若无则向上一级查询,直至顶级作用域。任何环境均不能向下搜索作用域链进入另一执行环境。

函数参数视作变量,其访问规则与执行环境中的其他变量一致。

作用域链的延长与块级作用域缺失

1.延长作用域链

当执行流进入以下任一语句时,作用域链得以延长:

• try-catch语句的catch块

• with语句

这两类语句会在作用域前端添加一个变量对象。

with语句将指定变量添加至作用域链。catch语句则创建新变量对象,包含被抛出错误对象的声明。

举个例子:


function buildUrl() {
  var qs = "?debug=true";
  with(location) {
    var url = href + qs;
  }
  return url;
}

with语句接收location对象,故其变量对象包含location对象的所有属性和方法,该变量对象被添加至作用域链前端。在with语句中引用href变量(实为location.href)时,可在当前环境变量中找到。引用qs变量时,引用的是buildUrl()中定义的变量,位于函数环境变量对象中。with语句内部定义了url变量,故url成为函数执行环境的一部分,可作为函数返回值。

2.没有块级作用域

在JavaScript中,封闭的花括号不具备独立作用域。看下面的代码:

 


if(true) {
  var color = "blue";
}
alert(color);  // "blue"

在JavaScript中,if/for语句创建的变量声明会将变量添加至当前执行环境。例如:


for(var i = 0; i < 10; i++) {
  doSomething(i);
}
alert(i);// 10

垃圾回收机制与内存管理

垃圾回收

与Java类似,JavaScript具备自动垃圾回收机制。执行环境负责管理代码执行过程中的内存。编写程序时无需关注内存使用,内存分配及无用内存回收均实现自动管理。垃圾回收机制原理为:找出不再继续使用的变量,释放其占用内存。为此,垃圾回收器按固定时间间隔(或代码执行中预定的收集时间)周期性执行此操作。

执行垃圾回收前,需判断资源是否无用,对不再使用的变量打标记以备将来回收内存。标识无用变量的策略通常有两种实现。

1 标记清除

JavaScript中最常用的垃圾收集方式是标记清除。当变量进入环境,将变量标记为“进入环境”;当变量离开环境时,将变量标记为“离开环境”。垃圾回收器运行时会给所有变量加上标记。随后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。在此之后再被加上标记的变量将被视为准备删除的变量,最后垃圾回收器完成内存清除工作,销毁带标记的值并回收它们所占的内存空间。

2.引用计数

引用计数指跟踪记录每个值被引用的次数。当声明变量并将引用类型值赋给该变量时,该值的引用次数为1。若同一值又被赋给另一变量,则该值的引用次数加1。相反,若包含此值引用的变量又取得另一变量,则此值的引用次数减1。当此变量的引用次数为0时,说明无法再引用此变量,因而可将其内存空间回收。当垃圾回收器下次运行时,将回收这些引用次数为零的值占用的内存。

引用计数可能产生一个问题,即导致循环引用。例如:


function problem() {
  var objA = new Object();
  var objB = new Object();

  objA.someOtherObj = objB;
  objB.someOtherObj = objA;
}

上述例子中,objA和objB通过属性相互引用。函数执行完成后,objA和objB将继续存在,其引用计数不为0。这种情况导致objA和objB所占内存无法回收。

以上这篇浅谈JavaScript:执行环境、作用域及垃圾回收就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

作者最新文章