• 闭包


    对闭包的理解:

    闭包是就是函数中的函数,里面的函数可以访问外面函数的变量,外面的变量的是这个内部函数的一部分。

    闭包是指有权访问另一函数作用域中的变量的函数。创建闭包的方式是在一个函数内部创建另一个函数。

    
    

    闭包的作用

    1、封装细节

    2.使用闭包可以访问函数中的变量。减少代码量

    3.可以使变量长期保存在内存中,生命周期比较长。

    如何从外部读取局部变量?

    我们有时候需要得到函数内的局部变量,但是在正常情况下,这是不能读取到的,只有通过变通方法才能读取到。

    例:
    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push(function () {
                return i * i;
            });
        }
        return arr;
    }
    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    f1();
    f2();
    f3();

    需要注意的是:返回的函数并没有立刻执行,直到调用f()才执行。
    结果全部都是16,原因在于返回的函数引用了变量i,但它并非立刻执行,而是调用完f()才执行,等到三个函数都返回,他们所引用的的变量i已经变成了4,因此最终结果为16.

    返回闭包时牢记的一点就是:返回函数不要引用任何循环变量,或者后续会发生变化的变量。

    
    

    如果一定要引用循环变量怎么办?方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

    function count() {
        var arr = [];
        for (var i=1; i<=3; i++) {
            arr.push((function (n) {
                return function () {
                    return n * n;
                }
            })(i));
        }
        return arr;
    }
    var results = count();
    var f1 = results[0];
    var f2 = results[1];
    var f3 = results[2];
    
    f1(); // 1
    f2(); // 4
    f3(); // 9

    闭包的应用场景

      1.使用闭包代替全局变量

      2.函数外或在其他函数中访问某一函数内部的参数

      3.在函数执行之前为要执行的函数提供具体参数(setTimeOut)

           4.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点(循环赋值)

           5.封装相关功能

    1.使用闭包代替全局变量

    全局变量有变量污染和变量安全等问题。

    2.函数外或在其他函数中访问某一函数内部的参数

    为了解决在Ajax callback回调函数中经常需要继续使用主调函数的某一些参数。

    3.在函数执行之前为要执行的函数提供具体参数(setTimeOut)

    某些情况下,是无法为要执行的函数提供参数,只能在函数执行之前,提前提供参数。(如f1())

    4.为节点循环绑定click事件,在事件函数中使用当次循环的值或节点,而不是最后一次循环的值或节点(循环赋值)

    使用闭包注意点:

    (1)由于闭包会使得函数中的变量都保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成页面的性能问题,在IE中可能会导致内存泄漏。解决方法是,在退出函数之前,将不使用的局部变量全部删除。将该变量置为null。

    经典闭包面试题

    function fun(n,o){
        console.log(o);
        return {
            fun:function(m){
                return fun(m,n);
            }
        }
    }
        
    var f1=fun(0);//undefined
    f1.fun(1);//0
    f1.fun(2);//0
    f1.fun(3);//0
    
    var f2=fun(0).fun(1).fun(2).fun(3);//undefined,0,1,2
    
    var f3=fun(0).fun(1);//undefined,0
    f3.fun(2);//1
    f3.fun(3);//1

    解析:

    (1)f1=fun(0) 执行外层的fun()函数,因为有两个参数,所以n=0,o=undefined; f1此时的返回值为一个对象,对象中包含fun属性,而这个fun属性是一个匿名函数,这个匿名函数会返回一个fun函数

             f1.fun(1)执行f1返回对象的fun函数,此时m=1,返回的内层的fun(m,n),此时执行函数体,n的值向外层寻找为0;

        f1.fun(2)执行f1返回对象的fun函数,此时m=2,返回的内层的fun(m,n),此时执行函数体,n的值向外层寻找为0;

    (2)f2=fun(0)执行外层的fun()函数,因为有两个参数,所以n=0,o=undefined; f1此时的返回值为一个对象,对象中包含fun属性

        fun(0).fun(1)会调用前者返回的对象里的fun属性,并传入1作为第一个参数,执行返回的fun函数fun(m,n)=fun(1,0)所以console.log(o)=0

        fun(0).fun(1).fun(2) 这里使用的闭包是.fun(1)返回的闭包,因为每次执行fun()都会返回一个新的对象,而.fun(2)引用的是fun(1),所以n的值被保留为1

    问题:实现一个暴露内部变量,而且外部可以访问修改的函数(get和set,闭包实现)

    var person=(function(){
        var name="xiaoan";
        return {
            getName:function(){
                return name;
            },
            setName:function(newValue){
                name=newValue;
                return name;
            }
        }
    })();
    console.log(person.name);//直接访问访问不到
    console.log(person.getName());//访问变量
    console.log(person.setName('liming'));//修改变量

    匿名函数的调用方式:使用()将匿名函数括起来,然后在后面加一对小括号。表示立即执行该匿名函数。

    问题:setTimeout使用闭包,实现定时输出5,4,3,2,1

    for(let i = 5; i >0; i-- ){
        setTimeout(function(){
            console.log(i)
        },1000)
    }
    //闭包
    for(var i = 5; i >0; i-- ){ setTimeout((function(num){ return function(){ console.log(num) } }(i)),1000) }

    这种方式虽然是依次打印了5,4,3,2,1,但是却是1s同时输出的,执行时间没有起作用,为什么??

    从闭包中接收传过来的参数i,然后setTimeout异步调用,进入异步队列,循环代码很快就执行完了,在1s后,从异步队里中返回执行后的结果,依次输出5,4,3,2,1

    即首先执行同步任务将for循环结束,然后执行异步中的setTimeout(),由于是同时计时的,所以全部都是1s输出

    如何每隔1s依次打印一个结果?(设置延迟时间依次增加1s即可)

    for(var i = 5; i >0; i-- ){
        setTimeout((function(num){
            return function(){
                console.log(num)
            }
        }(i)),1000*(5-i))
    }
  • 相关阅读:
    设计模式学习总结
    WCF一个Host实现多契约服务
    通过服务端监控结果,说说WCF的并发处理
    分词中常用算法比较与设想
    SQL Server 2008建立分区表(Table Partition)
    .NET 4中Entity Framework 新增查询与优化
    Web Service 接口大量数据传输解决方案
    面向对象的设计原则与目标[总结篇]
    说说ADO.NET EF 中的实体修改方法
    数据库大型应用解决方案总结
  • 原文地址:https://www.cnblogs.com/xiaoan0705/p/9081637.html
Copyright © 2020-2023  润新知