【前端每天一题】🔥 第 1 题:什么是 闭包(Closure)?它有什么作用?

闭包(Closure)面试详解

本文覆盖概念、本质、示例、常见陷阱、答题模板与练习题,便于背记和变形作答。

0) 一句话高分答案

闭包是函数与其定义时的词法环境(Lexical Environment)的组合;即函数可以"记住并访问"定义时的外层作用域变量,即使外层作用域已结束。常用于封装私有变量、延长变量生命周期、实现工厂函数/模块等。

1) 概念详解

  • 词法作用域:函数定义的位置决定了它能访问哪些外层变量(不是调用位置)。
  • 闭包:当一个函数引用了外部函数的变量并被外部返回或使用时,该函数形成闭包。闭包保存的是"对外层环境的引用",因此被引用的变量不会被回收。

2) 为什么会有闭包(实现角度)

  • 引擎为每个执行上下文维护词法环境,包含变量绑定及指向外层环境的引用。
  • 若某变量被仍可访问的内部函数引用,则其绑定必须留在内存中;闭包就是"函数 + 可访问的词法环境"。

3) 常见用途(面试要点)

  1. 模拟私有变量 / 信息隐藏
  2. 工厂函数 / 生成器模式(返回预配置的函数)
  3. 函数记忆(memoization)
  4. 保持某些状态(如计数器)
  5. 部分应用 / 柯里化(保存部分参数)
  6. 在事件/回调中携带上下文状态

4) 经典示例(逐行解释)

js 复制代码
function createCounter() {
  let count = 0;            // 外层变量
  return function () {      // 内部函数引用了 count
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2

解释:createCounter 执行完后,其执行上下文应被释放。但返回的内部函数仍引用 count,因此 count 继续存在,counter 与其词法环境形成闭包。

5) 面试常见陷阱与解析

题 A:for 循环 + var(最常见)

js 复制代码
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs.push(function () {
    console.log(i);
  });
}
funcs[0](); // 3
funcs[1](); // 3
funcs[2](); // 3

原因:var i 是函数作用域,三个函数引用的是同一个 i,循环结束后 i === 3

修复:

  • 使用 let i(块级作用域):
js 复制代码
for (let i = 0; i < 3; i++) {
  funcs.push(() => console.log(i));
}
  • 或使用 IIFE 捕获当前值:
js 复制代码
for (var i = 0; i < 3; i++) {
  (function (j) {
    funcs.push(function () { console.log(j); });
  })(i);
}

题 B:闭包与内存占用

闭包会延长被引用变量的生命周期。如果大量闭包保留巨大数据结构且长时间不释放,可能导致内存高占用。需避免无谓保留大对象(例如把大量 DOM 节点放入长期闭包)。

题 C:闭包与 this 的误区

闭包与 this 是两回事:闭包是词法环境,this 是运行时绑定。闭包不会改变 this 的行为,但闭包中的函数仍受 this 规则影响(call/apply/bind)。

6) 追问点与回答思路

  • 为什么闭包不会被回收?因为内部函数持有对外层变量的引用,引用存在即不回收。
  • 闭包的性能问题?延长变量生命周期、增加内存占用;避免在全局长期引用大量数据,必要时主动解除引用(如置 null)。
  • 和模块模式关系?模块常利用闭包隐藏私有状态,只暴露必要 API(IIFE + 返回对象)。

7) 标准答题结构(简洁完整)

  1. 一句定义(函数 + 词法环境)
  2. 为什么产生(词法作用域 + 引擎词法环境)
  3. 常见用途(私有变量、工厂函数等)
  4. 简单举例(计数器)
  5. 陷阱/性能补充(如 for/var 示例)

8) 练习题

练习 1:预测输出

js 复制代码
function makeAdd(x) {
  return function(y) {
    return x + y;
  }
}
const add5 = makeAdd(5);
console.log(add5(2)); // 7

练习 2:修复 for 闭包

js 复制代码
var funcs = [];
for (var i = 0; i < 3; i++) {
  funcs.push(function() { console.log(i); });
}
// 改为:
for (let i = 0; i < 3; i++) {
  funcs.push(() => console.log(i));
}
funcs.forEach(f => f());

练习 3:实现带 reset 的计数器

js 复制代码
function createCounter() {
  let count = 0;
  return {
    inc() { count++; return count; },
    value() { return count; },
    reset() { count = 0; }
  };
}

9) 常见变形题(加分项)

  • 闭包在异步场景(setTimeout/Promise)的工作机制
  • 用闭包实现 once(fn)(只执行一次)
  • 用闭包实现 memoize(fn)(结果缓存)
  • 闭包在模块化(CommonJS/ESM)下的角色

10) 答题小技巧

  • 举例时说明"为什么 GC 不会回收该变量"。
  • 对 for+var 陷阱:先说结果,再给两种修复(let 与 IIFE)。
  • 若被追问性能:说明闭包会延长变量生命周期,应及时解除不必要的引用。

闭包(Closure)超精简速记卡片

专门用于快速复习、面试前突击、临场背诵。内容极度浓缩,但覆盖所有核心点,读一遍就能记住。

1. 闭包是什么?(最标准定义)

闭包 = 函数 + 其定义时的词法环境(Lexical Environment)。

即:函数能访问它外层作用域的变量,即使外层作用域已执行结束。

2. 为什么会有闭包?(一句话解释)

因为 JS 是词法作用域,并且内部函数对外层变量形成引用,导致外层变量不会被回收。

3. 闭包的作用(全部面试高频点)

  • 模拟私有变量 / 数据隐藏
  • 延长变量生命周期
  • 工厂函数(返回预配置函数)
  • 函数记忆(memoize)
  • 柯里化/部分应用(curry)
  • 异步回调中保持状态

4. 最典型的闭包例子(必背)

javascript 复制代码
function createCounter() {
  let count = 0;
  return function () {
    return ++count;
  };
}

5. for + var 经典面试题(必考)

问题

javascript 复制代码
var arr = [];
for (var i = 0; i < 3; i++) {
  arr.push(() => console.log(i));
}
arr[0](); arr[1](); arr[2]();

输出: 3 3 3

原因: 三个函数共享同一个 var i,循环结束后 i = 3

修复方法(两种都要会)

  • 方法一: 换成 let

    javascript 复制代码
    var arr = [];
    for (let i = 0; i < 3; i++) {
      arr.push(() => console.log(i));
    }
  • 方法二: IIFE 捕获当前值

    javascript 复制代码
    var arr = [];
    for (var i = 0; i < 3; i++) {
      (function(j) {
        arr.push(() => console.log(j));
      })(i);
    }

6. 闭包常见问题(两句就够)

  • 闭包会让外层变量常驻内存 → 可能造成高内存占用
  • 及时释放:把引用设为 null 或让闭包脱离作用域

7. 面试标准作答模板(背下来就能稳过)

"闭包是函数与其词法环境的组合,它允许函数在外层作用域结束后仍能访问外层变量。

闭包常用于私有变量、工厂函数、柯里化和在异步回调中保持状态。

本质是内部函数对外层变量的引用未释放,因而外层变量的生命周期被延长。"

8. 面试进阶加分句(一句即可)

闭包的根本来源是 JS 的词法作用域规则 + 引擎对词法环境的持久化保存,而不是函数调用位置决定的。

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax