← 返回软件技术总览 软件技术 · 知识点深化 · 闭包与原型链:作用域链、this、原型继承
知识点深化 · 前端 · JavaScript

闭包与原型链:作用域链、this、原型继承

闭包和原型链是 JS 两大灵魂:闭包让函数"记住"它出生时的作用域,原型链让对象沿 __proto__ 找属性。再加上 this 的动态绑定,构成了 JS 灵活又易错的本质。这一页把它们讲透。

① 小白第一课怎么学(4 步走,约 60 分钟)

别急着背代码,先按这四步建立直觉:

1看图建立直觉(10 分钟)
读②③:函数记住外面变量。
2记规则(15 分钟)
读④:作用域链/原型链/this。
3手推输出(20 分钟)
精读⑤。
4刷题纠错(15 分钟)
做⑦⑩,错题回⑥。
本课小目标学完你要能:① 说清闭包是什么、为什么能记变量;② 画出原型链查找;③ 判断 this 指向。

② 一图看懂:闭包与原型链全地图

JS 核心 闭包 Closure 函数记住外层作用域 原型链 __proto__ 向上找属性 this 绑定 调用时定,非定义时 作用域链 嵌套查找变量 应用:数据私有/工厂函数/继承 模块模式 易错:this 丢失/闭包内存 var 循环问题
读法:中心是 JS,左闭包右原型链,下是 this 与易错。

③ 本质直觉:闭包是函数带着它出生时的"记忆"

闭包:函数 A 里定义了函数 B,B 用到了 A 的变量。即使 A 已经执行完返回,B 仍然"记得"并能访问 A 的变量。这就是闭包——函数 + 它出生时的词法作用域。

原型链:每个对象有个 __proto__ 指向它的原型对象。访问 obj.x 时,obj 自身没有 x,就沿 __proto__ 往上找,直到 null。这就是原型继承。

this:this 不是定义时定的,是调用时定的。谁调用就指向谁:obj.fn() 里 this=obj;直接调 fn() 里 this=undefined(严格模式)/全局。

闭包:函数记住外层变量 外层函数 outer: let count=0 内层 inner: 读 count++ outer 跑完,inner 仍能访问 count 原型链: obj → proto → proto → null this 看调用方式,不看定义位置
经典考题:var 循环for(var i=0;i<3;i++){ setTimeout(()=>console.log(i)) } 输出 3 个 3,因为三个闭包共享同一个 i。用 let 块级作用域就分别输出 0/1/2。

④ 完整体系与对比表

闭包/原型/this 对照

概念本质关键点
闭包函数+词法作用域外层跑完内层仍访问
原型链对象 __proto__ 链找属性沿链向上
this调用时绑定谁调用指谁

this 绑定规则(优先级从高到低)

① new 调用 → this 是新对象;② call/apply/bind → 显式指定;③ 对象方法调用 obj.fn() → this=obj;④ 单独调用 fn() → undefined/全局。箭头函数没有自己的 this,继承外层。

原型链终点obj.__proto__.__proto__... 最终指向 null;Object.prototype 是所有对象的原型终点

⑤ 用法场景与典型例题

例1(闭包计数)function counter(){let n=0; return ()=>++n;} 调三次结果?
闭包保持 n。
每次返回的箭头函数共享同一个 n:1、2、3。答案:1 2 3。n 被闭包保护,外部改不了——数据私有。
例2(原型链)obj={a:1},访问 obj.toString() 走哪?
自身没有 toString 沿原型链找。
obj 没有 toString → obj.__proto__(Object.prototype) 有 → 找到。答案:从 Object.prototype 取到。
例3(this)const obj={name:'A', fn(){console.log(this.name)}}; const f=obj.fn; f(); 输出?
f 单独调用 this 不指 obj。
f() 是单独调用,this 在严格模式下是 undefined,非严格下是全局。输出:undefined(严格模式),不是 A。
做题心法闭包题先问"函数出生在哪";原型链题画 __proto__;this 题看"怎么调用的"。

⑥ 高频错误诊断(4 条)

错误1:以为 this 在定义时定this 是调用时定的,同一函数不同调用方式 this 不同。
错误2:var 循环里 setTimeout 想得到 0/1/2var 没有块级作用域,三个回调共享同一 i。改 let。
错误3:闭包会导致内存泄漏闭包持有外层变量,不用时应置 null,避免不必要常驻。
错误4:以为箭头函数有自己的 this箭头函数没有 this,继承外层作用域的 this,不能当构造函数。

⑦ 考点真题演练(4 题)

考点分布

考法出题形式应对
闭包外层跑完内层还访问函数+作用域
原型链找属性沿 __proto__终点 null
this调用时绑定谁调用指谁
var 循环setTimeout 输出let 解决

真题基础1. 闭包的本质是?

真题中档2. 对象访问自身不存在的属性时会?

真题中档3. 关于 this,正确的是?

真题拔高4. for(var i=0;i<3;i++) setTimeout(()=>console.log(i)) 输出?

⑧ 必背知识点卡

闭包:函数记住外层变量 数据私有
作用域链:嵌套查找变量
原型链:__proto__ 向上找 终点 null
this:调用时定 谁调用指谁
箭头函数:无自己 this,继承外层 不能 new
var 循环:共享同一个 i 改 let
优先级:new>call/apply>对象调用>单独

⑨ 应用输出:用闭包实现一个私有计数器

场景:想做一个外部不能直接修改的计数器,只能通过 inc/get 操作。
① 用闭包:函数里 let count=0,返回 {inc, get} 两个方法。
② 封装:外部拿不到 count 变量,只能通过 inc() 加、get() 读。
③ 效果:const c=makeCounter(); c.inc(); c.inc(); c.get()=2。
④ 意义:这就是 JS 模块模式,实现数据私有(没 class 也能封装)。
⑤ 注意:闭包会持有 count 不释放,不用时删除引用。
口述思路合上书说:"闭包让函数带着外层变量,用来做私有状态和工厂函数。"

⑩ 分层练习(基础 + 中档 + 拔高)

▍基础 6 题

基础1闭包是什么?
函数+词法作用域,记住外层变量。
基础2this 什么时候定?
调用时。
基础3原型链终点?
null。
基础4箭头函数有自己的 this 吗?
没有,继承外层。
基础5对象方法 obj.fn() 中 this 指谁?
obj。
基础6var 循环 setTimeout 输出什么?
都输出最后一个值。

▍中档 6 题

中档7闭包能用来做什么?
数据私有、函数工厂、模块模式。
中档8obj.__proto__ 指向谁?
创建它的构造函数的 prototype。
中档9call/apply/bind 作用?
显式指定 this。
中档10为什么用 let 替代 var 循环?
let 有块级作用域,每次迭代独立绑定。
中档11Function.prototype 是谁的原型?
所有函数的原型。
中档12单独调用 fn() 严格模式 this?
undefined。

▍拔高 6 题

拔高13new 一个对象做了什么?
创建空对象→this 指向它→执行构造→返回。
拔高14原型链和 class 关系?
class 语法糖,底层仍是原型链继承。
拔高15闭包为什么可能内存泄漏?
闭包持有外层变量不释放,即使外层函数已返回。
拔高16如何打断闭包引用?
把闭包函数置 null,让 GC 回收。
拔高17Object.create 作用?
以指定对象为原型创建新对象。
拔高18作用域链和原型链区别?
作用域链找变量(词法),原型链找对象属性(运行)。

⑪ 记忆口诀 + 7 天复习计划

三句口诀① 闭包带记忆,原型链向上找。② this 看调用不看定义,箭头函数继承外层。③ var 循环共享 i,let 块级各管各。
天任务自检
第 1 天读②③④,画闭包作用域能说清
第 2 天背规则 + 基础 1-6this 规则对
第 3 天做中档 7-12,手推原型链查找路径对
第 4 天做拔高 13-18,讲 new 过程能讲清
第 5 天做⑦真题 4 题限时每题 2 分钟
第 6-7 天合上书默写 this 优先级不看资料全默对

← 返回软件技术总览