作用域链ScopeChain

一、作用域链(Scope Chain)

1.1 作用域是什么?

作用域 = 变量的”活动范围”。在哪个范围内可以访问这个变量,出了这个范围就找不到了。

通俗比喻:你在自己家里(函数作用域)可以随便拿冰箱里的东西;邻居家(另一个函数)的冰箱你进不去。全局作用域 = 小区公共区域,谁都能用。

1.2 三种作用域

前置varletconst 都是 JS 声明变量的关键字。var name = '小明' 意思是”创建一个变量叫 name,里面放 ‘小明’“。三者区别看下表。

作用域类型定义示例说明
全局作用域在任何 {} 外面定义的变量var a = 1哪里都能访问
函数作用域function 内部定义的变量function fn() { var b = 2 }只有函数内部能访问
块级作用域{} 内用 let/const 定义的变量{ let c = 3 }ES6 新增,var 没有块级作用域
 
// === 三种作用域演示 ===
 
var globalVar = '全局';           // 全局作用域
 
function outer() {
 
    var outerVar = '函数内';      // 函数作用域(var)
 
    if (true) {
 
        let blockVar = '块级';    // 块级作用域(let)
 
        var notBlockVar = '还是函数'; // var 无视 {},仍然是函数作用域
 
    }
 
    console.log(globalVar);     // ✅ '全局' — 全局作用域可访问
 
    console.log(outerVar);      // ✅ '函数内' — 函数作用域可访问
 
    console.log(notBlockVar);   // ✅ '还是函数' — var 穿透了 {}
 
    console.log(blockVar);      // ❌ ReferenceError — let 被 {} 限住了
 
}
 

记忆口诀var 是函数级,let/const 是块级。var 能穿透 if{}let/const 不能。


1.3 作用域链的查找规则

作用域链 = 一层套一层的变量查找链。 JS 引擎找变量时,从当前作用域开始,一层层往外找,找到就停,找到最外层(全局)还没找到就报 ReferenceError

 
var a = '全局';
 
function outer() {
 
    var a = 'outer';
 
    function inner() {
 
        var a = 'inner';
 
        console.log(a);   // 'inner' → 先找自己,找到了,停止
 
    }
 
    inner();
 
}
 
outer();
 

查找方向永远是由内向外,不会反过来。

 
inner 作用域 → outer 作用域 → 全局作用域 → 找不到 → ReferenceError
 

1.4 词法作用域(Lexical Scope)— 必问概念

JS 是词法作用域(静态作用域):函数的作用域在定义时就确定了,和在哪里调用无关。

 
var name = '全局';
 
function fn() {
 
    console.log(name);   // 定义时 name 指向全局,所以输出 '全局'
 
}
 
function run() {
 
    var name = 'run内部';  // 这个 name 在 fn 的词法作用域之外
 
    fn();                  // fn 定义在全局,不是在 run 内部定义的
 
}
 
run();  // 输出 '全局',不是 'run内部'
 

一句话:函数记住的是自己出生的地方,不是被调用的地方。闭包正是利用了这个特性。


速记卡(面试闪卡)

Q1:一句话讲清「作用域链ScopeChain」到底是什么?

A:作用域链是 JS 找变量时从当前作用域一层层向外查、找到就停的链路;JS 是词法作用域,函数作用域在定义时就定,与在哪调用无关。

Q2:三种作用域 —— 怎么理解?

A:作用域=变量能活动的范围。全局作用域像小区公共区域谁都能用;函数作用域像自家冰箱,函数外进不去;块级作用域是 let/const 的 {} 内部。记忆:var 函数级能穿透 if 的 {},let/const 块级不能——var 无视花括号。

Q3:作用域链的查找规则 —— 怎么理解?

A:找变量从内往外一层层找:inner→outer→全局,找到就停,最外层还找不到就 ReferenceError。方向永远由内向外,不会反过来。像你先翻自己口袋,没有再问爸妈,再没有问小区,全程单向。

Q4:词法作用域(静态作用域)—— 必问 —— 怎么理解?

A:JS 函数作用域在”定义时”就定,和”在哪调用”无关。fn 定义在全局,哪怕在 run 内部调用,它记住的还是出生地(全局)的 name,输出”全局”不是”run内部”。一句话:函数记住自己出生的地方,闭包正是利用这点。

Q5:为什么这个常被考 —— 怎么理解?

A:这是闭包、this、模块化所有后续概念的地基。弄混词法作用域就会误以为”在哪调就在哪找”,实际是”定义处决定”。面试常拿嵌套函数+外部变量改值来考,认准”定义时定死”四字即可。

Q6:核心速记主线有哪些?

  • 三种作用域:全局 / 函数(var)/ 块级(let、const)

  • 查找规则:由内向外,找到即停,全局外报 ReferenceError

  • 词法作用域:定义时确定,与调用位置无关

  • 闭包基础:函数记住出生地,正赖此特性

  • 坑:var 穿透 {},let/const 被 {} 限住

口诀

A:作用域分三层级,全局函数块级清;

查找由内向外切,找到即停不回程;

词法作用域定义定,调用位置不关情;

闭包根基由此生,var 穿透 let 锁门。

相关链接