我对闭包的理解

闭包是什么
  • 闭包是指有权访问另一个函数作用域中的变量的函数`

在我看来就是内嵌函数,在函数中嵌套函数

由于在JS中,变量的作用域属于函数作用域,在函数执行后作用域就会被清理、内存也随之回收,但是由于闭包是建立在一个函数内部的子函数,由于其可访问上级作用域的原因,即使上级函数执行完,作用域也不会随之销毁,这时的子函数——也就是闭包,便拥有了访问上级作用域中的变量的权限,即使上级函数执行完后作用域内的值也不会被销毁。

闭包的作用
  • 内部函数调用外部变量
function Father() {
    var counter = 0
    function Son() {
        counter += 1
        console.log(counter);
    }
    return Son
}

var father = Father()() // 1
  • 创建变量或者函数
var counter = (function () {
    var private = 0
    function changeBy(val) {
        private += val
    }
    return {
        zj: function() {
            changeBy(1)
        },
        js: function() {
            changeBy(-1)
        },
        value: function() {
            return private
        }
    }
})()

counter.zj()
let a = counter.value()
console.log(a); // 1
counter.js()
let b = counter.value()
console.log(b); // 0 
  • 将函数作为参数
var a = 'whl'
function f1() {
    var a = 'foo'
    function fo() {
        console.log(a);
    }
    return fo
}
function f2(f) {
    var a = 'qwe'
    f()
}
f2(f1()) // foo

图解闭包
function first(first) {
      const firstName = first
      let dept = 1
      debugger  // 第一个 debugger
      return function second(second) {
        const secondName = firstName + '/' + second
        dept = 2
        debugger // 第二个 debugger
        return function third(third) {
          const thirdName = secondName + '/' + third
          dept = 3
          debugger  //  第三个 debugger
        }
      }
  }
const x = first('白')('小')('唯')
const y = first('白')('小')
debugger // 第四个 debugger

first函数内部return 回来一个second 函数,second 函数里面又return 回来一个名为 thrid 函数,并且我们分别在合适的时机debugger 了一下,下面让我们在浏览器中跑一下,看看会出现什么情况。

  • 第一次debugger
    在这里插入图片描述
    在first 函数入栈时,我们发现局部变量有 dept=1,first:'白’,firstName:'白',局部变量以及函数参数都在,没有任何特殊的事情发生

  • 第二次debugger
    在这里插入图片描述
    second 函数进入了调用栈,我们scope作用域中多了一个名为 first 的闭包,里面存储的正是 first 函数里面的变量对象,因此通过这里我们可以知道,内部的 second 函数是可以访问到 first 函数里的属性的。

  • 第三次debugger
    在这里插入图片描述
    third 函数进入了调用栈,并且跟第二次 debugger 一样,作用域不仅将外部 second 函数的变量对象包含了起来,还包括了first 函数的变量对象,因此它可以访问并且修改 dept,并且成功置为 3。

  • 第四次debugger
    在这里插入图片描述
    我们可以看到成功获取了 third 函数,并且有了一个惊人的发现,在 firstsecond 的执行环境被弹出的情况下,我们的 third 函数的 [[scopes]]属性仍然包含着他们的变量对象,这就是我们的作用域链。


闭包的优缺点
  • 优点:全局变量可以重用,局部变量不会被污染,仅函数内部可用
  • 缺点:比普通函数占用更多的内存,内存泄漏导致项目运行变得卡顿

经典例题
var data = []
for(var i = 0; i < 3 ; i++) {
    data[i] = function() {
        console.log(i);
    }
}

data[0]() // 3
data[1]() // 3
data[2]() // 3 

这里的 i 是全局下的 i,共用一个作用域,当函数被执行的时候这时的 i=3,导致输出的结构都是3。

如何修改让它输出的是0、1、2呢

  • Method1: 自执行函数和闭包
var data = []
for (var i = 0; i < 3; i++) {
    (function (j) {
        data[j] = function () {
            console.log(j);
        }
    })(i)
}

data[0]() // 0
data[1]() // 1
data[2]() // 2 
  • Method2: 使用let
var data = []
for (let i = 0; i < 3; i++) {
    data[i] = function () {
        console.log(i);
    }
}

data[0]() // 0
data[1]() // 1
data[2]() // 2

let 具有块级作用域,形成的3个私有作用域都是互不干扰的。

function fun(n,o){
    console.log(o);
    return {
        fun:function(m){
            return fun(m,n);
        }
    };
 }

 var a = fun(0);a.fun(1);a.fun(2);a.fun(3);
 var b = fun(0).fun(1).fun(2).fun(3);
 var c = fun(0).fun(1);c.fun(2);c.fun(3);
 //问:三行a,b,c的输出分别是什么?

分析:var a = fun(0);a.fun(1);a.fun(2);a.fun(3);

  • 第一次调用fun(0)时,o为undefined
  • 第二次调用fun(1)时,m为1,此时fun闭包了外层函数的n,可以获取到外层函数的n的值,即m=1,n=0,并且调用第一层fun函数fun(1,0),所以o为0
  • 第三次调用fun(2)时,m为2,调用a.fun,所以闭包的还是第一次调用时的n的值,为0,所以内部调用第一层f(2,0),所以o为0
  • 第四次调用fun(3)时,m为3,仍然是调用a.fun,所以闭包的还是第一次调用时候的n的值,为0,所以o为0

即a的答案是undefined 0 0 0

分析:var b = fun(0).fun(1).fun(2).fun(3);

  • 第一次调用第一层fun(0)时,o为undefined
  • 第二次调用.fun(1)时候m为1,此时函数闭包了第一次调用函数时候的n值,即m=1,n=0,并在内部调用第一层fun函数fun(1,0),所以o为0
  • 第三次调用.fun(2)时候m为2,此时函数闭包了第二次调用函数时候的n值,即m=2,n=1,调用第一层fun函数(2,1)所以o为1
  • 第四次调用.fun(3)时m为3,此时函数闭包了第三次调用函数时候的n值,即m=3,n=2,调用第一层fun函数为fun(3,2)所以o为2

即最终答案:undefined 0 1 2

分析: var c = fun(0).fun(1);c.fun(2);c.fun(3);

  • 第一次调用一层fun(0),o为undefined
  • 第二次调用.fun(1)时m=1,此时fun闭包了外层函数的n值,也就是第一次调用的n,即m=1,n=0,所以o为0
  • 第三次.fun(2)此时fun闭包的是第二次调用的,即m=2,n=1,并在内部调用第一层fun函数fun(2,1),所以o为1
  • 第四次.fun(3)此时函数闭包的是第二次调用的,即m=3,n=1,
  • 并在内部调用第一层函数fun(3,1),所以o为1

即最终答案:undefined 0 1 1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值