深入理解JavaScript中的作用域链与块级作用域
本文详解JavaScript中的作用域机制,对比ES6前后的全局与函数作用域差异。通过执行期上下文、[[scope]]属性及具体代码示例,解析作用域链的形成原理。重点阐述ES6中let和const带来的块级作用域及其对全局对象的影响,帮助开发者深入理解变量访问规则。
JavaScript作用域基础:全局与函数作用域
在ES6标准发布之前,JavaScript仅具备全局作用域和函数作用域两种形式。所谓作用域,是指变量被定义后能够被访问的有效范围。具体而言,若变量定义在全局环境(即window对象)中,则程序的任何位置均可访问该变量;若变量定义在函数内部,则其访问权限仅限于该函数体内。
全局作用域的生命周期与页面生命周期一致,只要页面未关闭,全局作用域便一直存在。相比之下,函数作用域仅在函数执行期间存在,一旦函数执行结束,该作用域即被销毁。此外,每次调用函数时,系统都会创建一个新的、独立的作用域实例。
作用域链与执行期上下文解析
那么,什么是作用域链呢? 在深入探讨作用域链之前,我们需要先理解“执行期上下文”这一核心概念。
执行期上下文是指当函数被执行时,JavaScript引擎会创建一个内部对象,通常称为活动对象(AO)或全局对象(GO)。这个执行期上下文定义了函数当前的执行环境。由于每次调用函数时,其对应的执行期上下文都是独一无二的,因此每次函数调用都会生成一个新的执行期上下文。当函数执行完毕后,该执行期上下文会被立即销毁。
作用域链本质上是函数内部[[scope]]属性所存储的执行期上下文对象的集合。这些上下文对象以链式结构相互连接,这种链式的连接方式即被称为作用域链。
作用域链体现的是一种包含与依赖关系。例如,若函数A内部定义了函数B,则函数B的定义依赖于函数A。这意味着函数B处于函数A的作用域内部,因此函数B可以访问函数A中的变量和方法。这种层层向上依赖的关系构成了完整的作用域链。
为了更直观地理解上述概念,我们直接通过代码示例进行分析。
var name = 'xiaoyu';
function fn1() {};
function fn2() {
var num = 10;
function fn3() {
var num1 = 10;
console.log(num);
};
return fn3;
}
var fn4 = fn2();

闭包与函数返回示例分析
在前面的例子中,我们知道当fn2执行并返回fn3时,产生了闭包现象。然而,是否只要一个函数执行后返回另一个函数,就一定会产生闭包呢?我们通过下面的代码来验证这一假设。
var name = 'xiaoyu';
function fn1() {};
function fn2() {
var num = 10;
function fn3() {//fn3函数没有依赖fn2函数内的变量
var num1 = 10;
console.log(num1);
};
return fn3;
}
var fn4 = fn2();

作用域链查找机制实例
在理解了作用域链的基本原理后,我们通过一个具体的小例子来巩固所学知识。
var age = 10;
var obj = {
age: 12,
test: function() {
console.log(age);
console.log(obj.age);
console.log(this.age);
}
}
obj.test();
代码中console.log(this.age)打印出12,这不难理解。但为什么console.log(age)没有打印出12,而是输出了其他值呢?
当test函数执行时,JavaScript引擎首先会在test函数的局部作用域内查找是否存在age变量。如果未找到,则沿着作用域链向上查找,直到全局作用域。在全局作用域中,存在age变量(值为10)和data变量。因此,console.log(age)打印出的结果是10。若要打印出obj内部的age值(即12),则需要通过obj.age进行访问。
ES6块级作用域与变量声明
ES6标准引入了块级作用域的概念。
在ES6之后,通过let和const关键字引入了块级作用域。这意味着通过let和const声明的变量,其有效范围仅限于声明所在的代码块(如if语句、for循环或大括号包裹的代码块)内。此外,虽然通过let声明的变量在语法上属于全局变量,但它不再挂在全局对象window上,无法通过window.变量名直接访问。
我们通过一段代码来观察引入块级作用域后,函数作用域链发生的具体变化。
var age = 10;
let obj = {
age: 12,
test: function() {
console.log(age);
console.log(obj.age);
console.log(this.age);
}
}
obj.test();

JavaScript闭包与类(Class)的区别及适用场景对比
2026-11-05 15:48:57
JavaScript入门要学ES6吗?let与箭头函数等新特性解析
2026-11-05 14:49:16
JavaScript 8种数据类型详解:基本类型与引用类型的区别
2026-11-05 14:25:15
怎么在JavaScript中使用async/await优化Ajax异步流程
2026-11-05 13:56:15
JavaScript replace回调函数实现动态正则替换逻辑
2026-11-05 13:52:13
