🔥🔥🔥浅谈JavaScript闭包

前言

JavaScript中存在着闭包概念,也是前端面试经常提到得知识点,本文将从定义、工作原理、应用场景以及面试题等几个方面,对闭包进行简单而清晰的介绍!!!

什么是闭包

闭包是指一个函数能够记住并访问其词法作用域(Lexical Scope)中的变量,即使这个函数在其定义时的作用域之外被调用。换句话说,闭包让函数可以"携带"其创建时环境中的变量,形成一个封闭的上下文。

简单来说,闭包由两部分组成:

  1. 函数本身:一个内部函数,定义在另一个函数内部。
  2. 外部环境:内部函数能够访问外部函数的变量,即使外部函数已经执行完毕。
javascript 复制代码
function outerFunc(){
    let name = "小猪Passion";
    function innerFunc(){
        console.log(name);
    }
    return innerFunc;
}

const func = outerFunc();
func(); // 输出:小猪Passion

上述代码中,innerFunc函数形成了闭包,保留了对name变量的引用,即使outerFunc函数执行完毕,声明的name变量依旧可以被访问到。

闭包工作原理

闭包的工作原理基于 JavaScript词法作用域垃圾回收机制。以下是闭包的核心机制:

  1. 词法作用域JavaScript中的函数作用域是在定义时决定的,而不是在调用时。这意味着内部函数可以访问外部函数的变量,因为它们在定义时共享了相同的词法环境。
  2. 变量生命周期:当外部函数执行完毕后,通常其变量会被垃圾回收。但如果内部函数被返回并在外部被引用,外部函数的变量会继续保存在内存中,形成闭包。
  3. 环境引用 :闭包会创建一个包含外部函数变量的引用,而不是复制这些变量的值。因此,外部变量的任何变化都会反映在闭包中。
javascript 复制代码
function counter(){
    let count = 0;
    return function(){
        count++;
    }
}

const myCounter = counter();

console.log(myCounter()); // 输出:  0
console.log(myCounter()); // 输出:  1
console.log(myCounter()); // 输出:  2

在上述代码中,counter 函数返回的内部函数形成了闭包,保留了对 count 变量的引用。每次调用 myCounter 时,count 的值都会递增并保留。

闭包应用场景

  1. 构造函数的私有属性:模拟私有变量,隐藏数据,防止外部直接访问。
javascript 复制代码
function createPerson(name) {
  let age = 0;
  return {
	getName: () => name,
	getAge: () => age,
	growUp: () => age++
  };
}

const person = createPerson("小猪Passion");
console.log(person.getName()); // 输出: 小猪Passion
console.log(person.getAge()); // 输出: 0
person.growUp();
console.log(person.getAge()); // 输出: 1
console.log(person.age); // 输出: undefined(age 不可直接访问)
  1. 函数柯里化:闭包可以用来创建柯里化函数,将多参数函数转化为一系列单参数函数。
javascript 复制代码
function add(a) {
  return function(b) {
    return a + b;
  };
}

const add5 = add(5);
console.log(add5(3)); // 输出: 8
  1. 模块开发:闭包被广泛用于模块模式(如 CommonJSIIFE),实现代码的模块化和数据隔离。
javascript 复制代码
const module = (function() {
  let privateVar = "I am private";
  return {
    getVar: () => privateVar
  };
})();

console.log(module.getVar()); // 输出: I am private

经典面试题

  1. 经典循环问题:
javascript 复制代码
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 1000);
}
  • 问题:上述代码会输出什么?如何修复以输出 0, 1, 2?
  • 答案
  • 输出:3, 3, 3(因为 var 是函数作用域,setTimeout 的回调在循环结束后执行,i 已经是 3)。
  • 修复方法 1:使用 let(块作用域): javascript for (let i = 0; i < 3; i++) { setTimeout(() => console.log(i), 1000); }
  • 修复方法 2:使用闭包:
javascript 复制代码
for (var i = 0; i < 3; i++) {
  (function(index) {
        setTimeout(() => console.log(index), 1000);
  })(i);
}
  1. 函数防抖、节流:
    JavaScript的函数的防抖和节流中也对闭包有应用,详细关于防抖、节流的介绍请看这篇文章🤌🤌🤌一文轻松拿捏防抖和节流
  2. 闭包内存问题
    问题 :闭包会导致内存泄漏吗?为什么?
    答案:闭包本身不会导致内存泄漏,但如果闭包引用了大量不必要的变量,且这些变量无法被垃圾回收,可能导致内存占用过高。解决方法是谨慎管理闭包中引用的变量,及时解除不需要的引用。

总结

闭包是 JavaScript 中一个强大且灵活的特性,理解其原理和应用场景对于编写高质量代码和应对面试至关重要。通过掌握闭包的定义、工作原理、实际应用以及常见面试题,开发者可以更深入地理解 JavaScript 的运行机制,并在开发中更高效地解决问题。


文章荐读:

  1. 🔥🔥🔥Vite6 +TypeScript+Vue3+Tailwind+ESlint+Prettier+Husky搭建企业级前端项目
  2. 📸📸📸前端屏幕录制解决方案探索---------WebRTC,html2canvas和rrweb
  3. Vite6 +TypeScript+React18+Tailwind+ESlint+Prettier+Husky搭建企业级前端项目
  4. 🧠🧠🧠由一个BUG引发的对JavaScript运行机制Event Loop的探索
相关推荐
~无忧花开~2 分钟前
CSS学习笔记(五):CSS媒体查询入门指南
开发语言·前端·css·学习·媒体
程序猿小D9 分钟前
【完整源码+数据集+部署教程】【零售和消费品&存货】价格标签检测系统源码&数据集全套:改进yolo11-RFAConv
前端·yolo·计算机视觉·目标跟踪·数据集·yolo11·价格标签检测系统源码
吴鹰飞侠17 分钟前
AJAX的学习
前端·学习·ajax
JNU freshman24 分钟前
vue 技巧与易错
前端·javascript·vue.js
落一落,掉一掉31 分钟前
第十二周 waf绕过和前端加密绕过
前端
Asort32 分钟前
JavaScript设计模式(十六)——迭代器模式:优雅遍历数据的艺术
前端·javascript·设计模式
Coffeeee40 分钟前
Labubu很难买?那是因为还没有用Compose来画一个
前端·kotlin·android jetpack
我是日安40 分钟前
从零到一打造 Vue3 响应式系统 Day 28 - shallowRef、shallowReactive
前端·javascript·vue.js
开源之眼42 分钟前
深入理解 JavaScript 报错:TypeError: undefined is not a function
前端·javascript
LRH43 分钟前
时间切片 + 双工作循环 + 优先级模型:React 的并发任务管理策略
前端·react.js