深入理解JavaScript闭包

一、闭包的由来

我们先复习一个知识点:变量的作用域 变量的作用域分为 2 种:全局变量和局部变量。在 js 中,函数内部可以直接读取全局变量,但函数外部无法读取函数内部的局部变量。 那怎么从函数外部读取函数内部的局部变量呢?就是在函数内部再声明一个子函数,子函数内调用函数内的局部变量,然后在函数内把子函数 return 返回,在函数外部调用函数再调用子函数就可以得到函数内部的局部变量了,这种形式的函数就叫做闭包。

解释得有点绕,写个例子说明一下:

javascript 复制代码
function parentFn() {
  let count = 10;
  function sunFn() {
    console.log("count", count);
  }
  return sunFn;
}
let resFn = parentFn();
resFn(); // count 10

上面说明了闭包的由来以及闭包的其中一个作用是可以读取函数内部的局部变量,下面说说它的另一个作用:让这些变量的值始终保存在内存中。

把上面的例子改造一下:

javascript 复制代码
function parentFn() {
  let count = 10;
  function sunFn() {
    console.log("count", count);
    return count++;
  }
  return sunFn;
}
let resFn = parentFn();
resFn(); // count 10
resFn(); // count 11
resFn(); // count 12

为什么调用第二次和第三次 resFn()会自增 1,而不是都返回 10 呢?原因就是闭包会让函数内的值始终保存在内存中,不会被垃圾回收清除。

二、闭包的概念

闭包(closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。------引用自MDN

看完这里,你是不是觉得很懵?那我们再看一下阮一峰大佬的总结:

闭包就是能够读取其他函数内部变量的函数。 由于在 Javascript 语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成"定义在一个函数内部的函数"。所以,在本质上,闭包就是将函数内部和函数外部连接起来的一座桥梁。

好像清楚了一点,知道了函数内部的函数可以读取其他函数内部变量,这样结构的函数就叫做闭包。 那么问题来了,它是怎么捆绑周边环境状态的引用的?普通函数调用完之后会被垃圾回收机制回收,闭包为什么不会被垃圾回收机制回收?

带着问题,我们一一验证一下

三、闭包的存储

  • 普通函数执行结束后

    javascript 复制代码
    function fn() {
      let name = "娜美";
      let size = 60;
      return name;
    }
    fn();
    console.dir(fn);

由上图打印可以看到,普通函数执行结束后,内部变量立即被垃圾回收清除了,并没有保存在内存中。

  • 闭包函数执行结束后

    javascript 复制代码
    function parentFn() {
      let name = "娜美";
      let size = 60;
      let luobinObj = {
        name: "罗宾",
        size: 56,
      };
      return function sunFn() {
        ++luobinObj.size;
        console.log(name, luobinObj);
      };
    }
    let resFn = parentFn();
    resFn();
    console.dir(resFn);

由上图打印可以看到,闭包函数会把闭包函数内使用到的变量(name、luobinObj)放到Scopes数组中以对象的方式存放,没有被闭包函数使用的变量(size)则不会被存放到Scopes中,并且闭包函数调用之后会把最新的值存放到Scopes中。

四、总结

由于闭包所使用到的变量会存放到Scopes数组中以对象的方式存放在堆内存中,这种特殊的机制不会被垃圾回收清除。但是这种特殊的机制也会导致内存消耗很大甚至内存泄露的问题,所以不能滥用闭包。解决这个问题的方法是,在退出函数前把不使用的局部变量全部删除。

相关推荐
道不尽世间的沧桑27 分钟前
第9篇:插槽(Slots)的使用
前端·javascript·vue.js
bin915330 分钟前
DeepSeek 助力 Vue 开发:打造丝滑的滑块(Slider)
前端·javascript·vue.js·前端框架·ecmascript·deepseek
uhakadotcom1 小时前
最新发布的Tailwind CSS v4.0提供了什么新能力?
前端
GISer_Jing1 小时前
Node.js中如何修改全局变量的几种方式
前端·javascript·node.js
秋意钟1 小时前
Element UI日期选择器默认显示1970年解决方案
前端·javascript·vue.js·elementui
bramble1 小时前
Windows使用Trae全程提问来创建一个彩色贪吃蛇游戏,可以设置速度并查看游戏记录。
前端·程序员·trae
我命由我123452 小时前
微信小程序 - 自定义实现分页功能
前端·微信小程序·小程序·前端框架·html·html5·js
程序员黄同学2 小时前
请谈谈 Vue 中的 key 属性的重要性,如何确保列表项的唯一标识?
前端·javascript·vue.js
繁依Fanyi3 小时前
巧妙实现右键菜单功能,提升用户操作体验
开发语言·前端·javascript·vue.js·uni-app·harmonyos
前端御书房3 小时前
前端防重复请求终极方案:从Loading地狱到精准拦截的架构升级
前端·javascript