前端JS必备知识点:闭包及其作用

闭包及其作用

闭包是JavaScript中的一大难点,在学习闭包之前,首先我们必须清楚高阶函数、变量的作用域、作用域、作用域链和执行上下文,如果对高阶函数、变量的作用域、作用域、作用域链和执行上下文不是很清楚的同学,我建议先去学习相关的理论基础再过来学习闭包,这样理解的会更加透彻。

高阶函数

高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出(函数作为返回值,函数作为参数);JavaScript的回调函数是以实参形式传入其他函数中,这也是高阶函数(在函数式编程中回调函数被称为 lambda表达式)

    //第一种情况,将函数作为参数(回调函数)
    function fn(callback) {
        callback && callback();
    }
    fn(function () {
        alert('hello world');
    });
    //第二种情况,将函数作为返回值输出
    function fn() {
        return function () {
            alert('hello world');
        }
    }
    fn()();

变量作用域

  1. 函数内部可以使用全局变量
  2. 函数外部不可以使用局部变量
  3. 当函数执行完毕,本作用域的局部变量会销毁

闭包(closure)

在很多时候我们要获取函数的局部变量,在正常情况下是不允许的(上述我们已经说过),这个时候就要用到我们的闭包了,闭包:指有权访问另一个函数作用域中变量的函数;闭包是允许函数访问局部作用域之外的数据。即使外部函数已经退出,外部函数的变量仍可以被内部函数访问到;闭包的主要作用:延伸了变量的作用范围

闭包实现的三个条件:

  1. 内部函数访问了外部函数的变量
  2. 外部函数已经退出
  3. 内部函数仍可以访问
    function fun() {
        var x = 0;
        return function (y) {
            x = x + y;
            console.log(x);
        }
    }
    var a = fun();
    a(1); //1
    a(1); //2

上述代码在执行的时候,a得到的是闭包对象的引用,虽然fun函数执行完毕,但是fun的活动对象由于闭包的存在并没有被销毁,在执行a(1)的时候,仍然访问到了x变量,并将其加1再赋值给x变量,若再执行a(1),则x变量值为2,因为闭包的引用a并没有消除。(闭包返回了函数,函数可以创建独立的作用域)

闭包的核心内容:有些情况下(函数调用返回一个函数),函数调用完成之后,其执行上下文环境不会接着被销毁

    //全局执行上下文
    function fun() {
        var max = 10;
        // fun函数执行上下文
        return function bar(x) {
            if (x > max) {
                console.log(x);
            }
        }; //bar函数执行上下文
    }
    var f = fun();
    f(15);

上面代码在执行到调用fun()函数后,按道理应该会销毁掉fun()函数执行上下文和其中的数据,但是这里没有销毁,因为在fun()函数执行完毕之后返回的是一个函数,函数能够创建独立的作用域,而正巧合的是,返回的这个函数体中,还有一个自由变量max要引用fun函数作用域下的fun()函数执行上下文环境中的max。因此,这个max不能被销毁,销毁了之后bar函数中的max就找不到值了。因此,这里的fun()函数上下文环境不能被销毁,还存在执行上下文栈中,没有出栈,所以使用闭包会增加内容开销,在IE中可能导致内存泄露。解决方法:在退出函数之前,将不使用的局部变量全部清除(变量赋值为null)。 

闭包案例

循环注册点击事例(点击li输出当前的li的索引号)

1、利用动态添加属性的方式

    for (var i = 0; i < lis.length; i++) {
        lis[i].index = i;//动态添加index属性
        lis[i].onclick = function () {
            console.log(this.index);
        }
    }

2、利用闭包的方式得到当前li的索引号

    //闭包不符合预期
    var lis = document.querySelectorAll('li');
    for (var i = 0; i < 5; i++) {
            lis[i].onclick = function () {//函数执行是在循环之后才运行,变量i是全局作用域
                console.log(i);
            }
        }

上面代码输出结果都是 5,这不符合预期,由于作用域链的一个副作用,闭包只能取得包含函数中任何变量的最后一个值。闭包保存的是整个变量对象,而不是某个特殊的变量。

解决办法:

    //方法一:利用立即执行函数强制让闭包符合预期
    for (var i = 0; i < 5; i++) {
    //利用for循环创建了4个立即执行函数
        (function (i) {
            lis[i].onclick = function () {
                console.log(i);
            }
        })(i)
    }
    //立即函数也称为小闭包,因为在立即函数里面的任何一个函数都可以访问i这个变量
    //方法二:使用let声明变量
    var lis = document.querySelectorAll('li');
    for (let i = 0; i < 5; i++) {
    //每次循环会产生一个块级作用域 
            lis[i].onclick = function () {
                console.log(i);
            }
        }

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

湖大啊哈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值