• 闭包


    定义:当函数可以记住并访问所在的词法作用域时,就产生了闭包 (你不知道的JavaScript)
    闭包是指有权访问另一个函数作用域中的变量的函数(JavaScript高级程序设计)

    特点

      让外部访问函数内部变量成为可能;

      局部变量会常驻在内存中;

      可以避免使用全局变量,防止全局变量污染;

      会造成内存泄漏(有一块内存空间被长期占用,而不被释放)

    DEMO:

    function foo() {
        var a = 2;
        return function fun1() {
            console.log(a)
        }
    }
    var fun2 = foo()
    fun2()  // 2

     在上面的例子中,fun1能够访问foo的内部作用域,我们把fun1作为一个值返回。在foo()执行后,把foo()的返回值 fun1 赋值给fun2并调用fun2。打印出了结果2.

    此时,我们可以说fun1记住并访问了所在的词法作用域 或者说 fun2访问了另一个函数作用域中的变量(fun2在全局作用域中声明,访问了foo的内部作用域)
    由于引擎有自动的垃圾回收机制,在foo()执行后(不再使用),通常foo的整个内部作用域会被销毁,对内存进行回收。闭包的神奇之处正是可以阻止这件事情的发生,因为fun1依然持有对该作用域的引用,这个引用就叫做闭包。
    无论使用何种方式对函数类型的值进行传递,当函数在别处调用时,都可以看到闭包。

    DEMO2

    function add(x){
        return function(y){
          console.log(x,y)
          return x + y;
        };
      }
      var addFun1 = add(2);
    
      var addFun2 = add(9);
      console.log(addFun1)
      //  ƒ (y){
      //    console.log(x,y)
      //    return x + y;
      //  }
      console.log(addFun2)
    //  ƒ (y){
    //    console.log(x,y)
    //    return x + y;
    //  }
      console.log(addFun1(2))//2、2、4
    
      console.log(addFun2(2))//9、2、11

    直接传递

    function foo() {
        var a = 2;
        function baz() {
            console.log(a);
        }
        bar(baz); //对函数类型进行值得传递
    }
    function bar(fn) {
        fn(); // 闭包产生了
    }

    或者间接的传递

    var fn;
    function foo(){
        var a = 2;
        function baz() {
            console.log(a);
        }
        fn = baz; //对函数类型进行值得传递
    }
    function bar(fn) {
        fn(); //闭包产生了
    }
    foo()
    bar()

    无论通过什么手段将内部函数传递到所在的词法作用域之外,它都会保持对定义时作用域的引用,这个函数无论在何处执行,都产生了闭包。

    现在你理解闭包了吗?思考一下你的代码中产生了哪些闭包?

    无处不在的闭包

    定时器

    function wait(message) {
        setTimeout(function timer() {
            console.log(message)
        }, 1000)
    }
    wait('hello 闭包')

    循环和闭包

    思考下面的代码

    for (var i=1;i<=5;i++){
        (function(j){
            setTimeout(function timer() {
                console.log(j)
            },j*1000)
        })(i)
    }

    正常情况下,我们对这段代码的预期是每隔一秒输出一个数字,1-5。但是实际上并不会这样,这段代码会每间隔一秒输出一个数字6。这是为什么?

    首先6从哪里来,当timer执行时,for循环早已结束,终止条件是i=6。
    有些小伙伴可能会不明白了,timer不是创建了闭包,保持了对i的引用吗?
    没错。for循环了5次,创建了5个闭包,但是都是保持了对同一个i的引用(i是全局变量)。所以当timer执行时,i = 6,输出6,没毛病。
    那么如何达到我们想要的效果,我们需要更多的闭包。循环过程中每个迭代都需要一个闭包

    for (var i=1;i<=5;i++){
        (function(j){
            setTimeout(function timer() {
                console.log(j)
            },j*1000)
        })(i)
    }

    立即执行函数创建了一个新的作用域,使得延迟函数的的回调可以将新的作用域封闭在每个迭代内部。问题解决。

     

  • 相关阅读:
    MVC之路由规则 (自定义,约束,debug)
    MCV之行为
    mvc之页面强类型
    Jquery异步上传图片
    三层VS控制器
    Oracle 表分区
    C#编写的通过汉字得到拼音和五笔码
    MYSQL存储过程学习
    Sina App Engine(SAE)入门教程(9)- SaeMail(邮件)使用
    状态CSS
  • 原文地址:https://www.cnblogs.com/hy96/p/12973471.html
Copyright © 2020-2023  润新知