首先在了解闭包之前,我们要了解一下环境和作用域
日常生活中我们生活的环境由周边建设如公园,小区,超市构成的。这就构成了环境
在计算机当中环境就是一块内存的数据。
环境是有作用范围的,eg:武汉周边的建设一般只服务武汉生活的人们,这就是作用域。作用域是闭包的基础
环境存在的价值是被需要,当环境不被需要的时候就会被回收
在js中全局的环境是不会被回收的,全局环境在很多时候是被依赖的
- function count(){
- let total=0
- }
- count()
函数在每次执行的时候都会创建一个环境,都会产生一个新的内存地址 【函数没有调用就不会开辟内存空间或者称之为环境】
作用域链只向上查找,找到全局 window 即终止,应该尽量不要在全局作用域中添加变量。
- function mushu () {
- let n = 1
- return function sum () { // 当有return之后,里面的数据就在一直被使用 就不会被摧毁
- let m = 1
- return function show () {
- console.log(++m) // 2 3
- console.log('n', ++n) // 2 3
- }
- show()
- }
- }
- let a = mushu()()
- a()
- a()
- // 调用了两次,被外部引用只会数据不会被销毁,所以一直进行了累加
函数定义的数据,其作用域是函数及其子函数,子函数中的数据不会向父级进行传递,向父级进行传递,有可能会覆盖父级的数据
使用 let/const 可以将变量声明在块作用域中(放在新的环境中,而不是全局中)
let 块作用域中
var 函数作用域护着
- {
- let a = 9;
- }
- console.log(a); //ReferenceError: a is not defined
- if (true) {
- var i = 1;
- }
- console.log(i);//1
-
- //**************************其他例子**********************
- let arr = [];
- for (let i = 0; i < 10; i++) {
- arr.push((() => i));
- }
- console.log(arr[3]()); //3 如果使用var声明将是10
闭包是一种保护私有变量的机制,在函数执行时形成私有的作用域,保护里面的私有变量不受外界干扰。
直观的说就是形成一个不销毁的栈环境。
闭包可以实现属性的私有化
闭包指子函数可以访问外部作用域变量的函数特性,即使在子函数作用域外也可以访问。如果没有闭包那么在处理事件绑定,异步请求时都会变得困难。
- let lessons = [
- {
- title: "媒体查询响应式布局",
- click: 89,
- price: 12
-
- },
- {
- title: "媒体查询响应式布局",
- click: 84,
- price: 120
-
- }, {
- title: "Flex布局",
- click: 68,
- price: 90
-
- }, {
- title: "你好呀!!",
- click: 15,
- price: 34
-
- },
- {
- title: "hello world",
- click: 89,
- price: 67
- }
- ]
-
- function bwtween (a, b) {
- return function (v) { // 这里的v 就是数组里面的元素
- //function (v)这是between函数的子函数 利用闭包特性可以访问到父级的变量
- return v.price >= a && v.price <= b
- }
- }
- console.table(lessons.filter(bwtween(50, 100)));
- // 普通形式 统计函数调用的次数
- let i = 0
- function fn () {
- i++
- console.log(`函数调用了${i}次`)
- }

- function count () {
- let i = 0
- function fn () {
- i++
- console.log(`函数调用了${i}次`)
- }
- return fn
- }
- const fun = count()

- function fn () {
- let count = 1
- function fun () {
- count++
- console.log(`函数被调用了${count}次`) //函数被调用了2次 函数被调用了3次
- }
- return fun
- }
- // res是一个全局变量,代码执行完成之后不会立即销毁,并且res调用了fn函数,fn调用了fun,fun里面使用到了count,count被引用就不会被回收,所以一直存在
- // 此时:闭包引起了内存泄露
- const res = fn()
-
- res()
- res()
- //注意不是所有的内存泄露都要手动回收,react中的很多闭包不能被回收
上级作用域会为函数保存数据,从而造成的如下所示的内存泄漏问题
- <div desc="annanan~~">周日了div>
- <div desc="hahahahah!!!">我也不知道!! div>
- let mushu = document.querySelectorAll("div")
- mushu.forEach(function (item) {
- let desc = item.getAttribute("desc")
- item.addEventListener("click", function () { // 这个函数是事件处理函数,是一直存在的,
- // 根据闭包的特性父级作用域的元素也会一直存在,
- console.log(item.getAttribute("desc")) // getAttribute 属性名
- console.log(item)
- })
- })

下面通过清除不需要的数据解决内存泄漏问题
- <div desc="annanan~~">周日了div>
- <div desc="hahahahah!!!">我也不知道!! div>
- <script>
- let mushu = document.querySelectorAll("div")
- mushu.forEach(function (item) {
- let desc = item.getAttribute("desc")
- item.addEventListener("click", function () { // 这个函数是事件处理函数,是一直存在的,
- // 根据闭包的特性父级作用域的元素也会一直存在,
- // console.log(item.getAttribute("desc")) // getAttribute 属性名
- console.log(desc)
- console.log(item)
- })
- item = null
- })
- script>

闭包可能引起内存泄露,但是不一定所以的内存泄露都会被回收
答案是不一定
闭包经常被函数包裹,里面的变量是局部的,外面不能直接使用,所以我们会用到return,将函数返回出去,外部可以使用内部的变量,但是不能修改里面的变量。