🔥🔥🔥浅谈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的探索
相关推荐
GISer_Jing10 分钟前
[总结篇]个人网站
前端·javascript
ss.li11 分钟前
TripGenie:畅游济南旅行规划助手:个人工作纪实(二十二)
javascript·人工智能·python
疯狂的沙粒31 分钟前
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
前端·uni-app·html
小妖66635 分钟前
html 滚动条滚动过快会留下边框线
前端·html
heroboyluck1 小时前
Svelte 核心语法详解:Vue/React 开发者如何快速上手?
前端·svelte
海的诗篇_1 小时前
前端开发面试题总结-JavaScript篇(二)
开发语言·前端·javascript·typescript
琹箐1 小时前
ant-design4.xx实现数字输入框; 某些输入法数字需要连续输入两次才显示
前端·javascript·anti-design-vue
程序员-小李1 小时前
VuePress完美整合Toast消息提示
前端·javascript·vue.js
Uyker2 小时前
从零开始制作小程序简单概述
前端·微信小程序·小程序
还有几根头发呀3 小时前
UDP 与 TCP 调用接口的差异:面试高频问题解析与实战总结
网络·网络协议·tcp/ip·面试·udp