js面试题每日一题:什么是闭包?

一、闭包的核心定义

闭包是指函数与其引用的外部变量的绑定组合。具体来说,当内部函数被保存到外部时,会形成闭包 ------ 内部函数可访问外部函数的变量和作用域,即使外部函数已执行完毕,这些变量也不会被垃圾回收机制销毁。

二、闭包的三大关键特征

  1. 作用域嵌套:外部函数包含内部函数,内部函数引用外部函数的变量。
  2. 返回内部函数:外部函数返回内部函数,使内部函数能在外部被调用。
  3. 变量持久化:外部函数执行结束后,其变量因被内部函数引用而保留在内存中。

三、闭包的典型体现形式

  1. 函数嵌套返回

    javascript

    javascript 复制代码
    function outer() {
      const count = 0; // 外部函数变量
      function inner() {
        return count++; // 内部函数引用外部变量
      }
      return inner; // 返回内部函数
    }
    
    const counter = outer(); // 调用外部函数,获取内部函数
    console.log(counter()); // 输出 0
    console.log(counter()); // 输出 1
  2. 立即执行函数(IIFE)

    javascript

    javascript 复制代码
    const add = (function() {
      let sum = 0; // IIFE作用域内的变量
      return function(num) {
        sum += num;
        return sum;
      };
    })();
    
    console.log(add(5)); // 输出 5
    console.log(add(3)); // 输出 8

四、闭包的核心作用:保护变量与封装数据

  • 隔离变量,避免全局污染:闭包中的变量仅能通过内部函数访问,防止被全局作用域篡改。

    javascript

    javascript 复制代码
    // 示例:封装计数器,避免全局变量污染
    function createCounter() {
      let count = 0;
      return {
        increment() { return ++count; },
        decrement() { return --count; }
      };
    }
    
    const counter = createCounter();
    console.log(counter.increment()); // 1
    console.log(counter.decrement()); // 0
    // 无法直接访问count变量,避免外部篡改
  • 实现 "私有属性" 效果:在 ES6 类语法出现前,闭包是模拟私有变量的主要方式。

五、闭包解决的经典问题

  1. 循环中绑定事件的变量作用域问题

    javascript

    javascript 复制代码
    // 错误示例:普通循环中点击事件获取的i都是最终值
    for (var i = 0; i < 5; i++) {
      document.getElementById(`btn${i}`).onclick = function() {
        console.log(i); // 输出5(所有按钮都一样)
      };
    }
    
    // 闭包解决方案:用IIFE保存每次循环的i值
    for (var i = 0; i < 5; i++) {
      (function(j) {
        document.getElementById(`btn${j}`).onclick = function() {
          console.log(j); // 正确输出0-4
        };
      })(i);
    }
  2. 循环中定时器的变量取值问题

    javascript

    javascript 复制代码
    // 闭包确保定时器获取正确的循环变量
    for (var i = 0; i < 3; i++) {
      (function(j) {
        setTimeout(() => {
          console.log(j); // 依次输出0,1,2
        }, j * 1000);
      })(i);
    }

六、闭包的缺陷:内存泄漏风险

  • 问题本质:闭包会保留外部函数的变量,若大量使用且未正确释放,可能导致内存占用过高。

  • 典型场景

    1. 长时间持有大型数据的闭包(如缓存函数返回的大数据对象)。
    2. 未清理的事件监听闭包(如组件卸载时未移除的 DOM 事件)。
  • 解决方案

    • 避免不必要的闭包嵌套;
    • 组件卸载时清除闭包引用(如清除定时器、解绑事件)。

七、闭包的内存机制:栈与堆的协同

  • 基本数据类型(栈内存) :存储在栈中,按 "先进后出" 规则管理(如闭包中的数字、字符串)。
  • 引用数据类型(堆内存) :存储在堆中,按引用关系管理,闭包会通过引用保持堆内存中的对象不被释放(如对象、数组)。

总结

闭包是 JavaScript 函数式编程的核心特性,通过 "作用域嵌套 + 变量持久化" 实现数据封装与隐私保护,但需注意内存管理。理解闭包不仅是面试高频考点,更是掌握前端性能优化和复杂逻辑实现的基础。

相关推荐
脑袋大大的26 分钟前
JavaScript 性能优化实战:减少 DOM 操作引发的重排与重绘
开发语言·javascript·性能优化
速易达网络2 小时前
RuoYi、Vue CLI 和 uni-app 结合构建跨端全家桶方案
javascript·vue.js·低代码
耶啵奶膘2 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
JoJo_Way2 小时前
LeetCode三数之和-js题解
javascript·算法·leetcode
视频砖家2 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
GISer_Jing5 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆5 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
我在北京coding6 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
海天胜景7 小时前
vue3 获取选中的el-table行数据
javascript·vue.js·elementui