• 面试题:对闭包的理解


    在 JS 忍者秘籍(P90)中对闭包的定义:闭包允许函数访问并操作函数外部的变量。红宝书上对于闭包的定义:闭包是指有权访问另外一个函数作用域中的变量的函数。 MDN 对闭包的定义为:闭包是指那些能够访问自由变量的函数。这里的自由变量是外部函数作用域中的变量。
     

    形成闭包的原因

    内部的函数存在外部作用域的引用就会导致闭包。从上面介绍的上级作用域的概念中其实就有闭包的例子 return f就是一个表现形式。

    闭包的作用

    • 保护函数的私有变量不受外部的干扰。形成不销毁的栈内存。
    • 保存,把一些函数内的值保存下来。闭包可以实现方法和属性的私有化

    闭包经典使用场景

    1、return 一个函数

    var n = 10
    function fn(){
        var n =20
        function f() {
           n++;
           console.log(n)
         }
        return f
    }
    
    var x = fn()
    x() // 21
    //这里的 return ff()就是一个闭包,存在上级作用域的引用。

    2、函数作为参数

    var a = '林一一'
    function foo(){
        var a = 'foo'
        function fo(){
            console.log(a)
        }
        return fo
    }
    
    function f(p){
        var a = 'f'
        p()
    }
    f(foo())
    /* 输出
    *   foo
    / 

    //使用 return fo 返回回来,fo() 就是闭包,f(foo()) 执行的参数就是函数 fo,因为 fo() 中的 a 的上级作用域就是函数foo(),所以输出就是foo

    3、自执行函数

    var n = '林一一';
    (function p(){
        console.log(n)
    })()
    /* 输出
    *   林一一
    / 
    
    //同样也是产生了闭包p(),存在 window下的引用 n。

    4、循环赋值

    for(var i = 0; i<10; i++){
      (function(j){
           setTimeout(function(){
            console.log(j)
        }, 1000) 
      })(i)
    }
    
    //因为存在闭包的原因上面能依次输出1~10,闭包形成了10个互不干扰的私有作用域。
    //将外层的自执行函数去掉后就不存在外部作用域的引用了,输出的结果就是连续的 10。
    //为什么会连续输出10,因为 JS 是单线程的遇到异步的代码不会先执行(会入栈),
    //等到同步的代码执行完 i++ 到 10时,异步代码才开始执行此时的 i=10 输出的都是 10。

    5、使用回调函数就是在使用闭包

    window.name = '林一一'
    setTimeout(function timeHandler(){
      console.log(window.name);
    }, 100)

    使用闭包需要注意什么

    容易导致内存泄漏。闭包会携带包含其它的函数作用域,因此会比其他函数占用更多的内存。过度使用闭包会导致内存占用过多,所以要谨慎使用闭包。

    经典面试题

    for 循环和闭包(号称必刷题)

    var data = [];
    
    for (var i = 0; i < 3; i++) {
      data[i] = function () {
        console.log(i);
      };
    }
    
    data[0]();
    data[1]();
    data[2]()
    /* 输出
        3
        3
        3
    /

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

    使用闭包改善上面的写法达到预期效果,写法1:自执行函数和闭包

    //写法1:自执行函数和闭包
    var
    data = []; for (var i = 0; i < 3; i++) { (function(j){ setTimeout( data[j] = function () { console.log(j); }, 0) })(i) } data[0](); data[1](); data[2]()

    //写法2:使用 let,let 具有块级作用域,形成的3个私有作用域都是互不干扰的。

    var data = [];

    for (let i = 0; i < 3; i++) {
    data[i] = function () {
    console.log(i);
    };
    }

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

     
     
     
     
     
  • 相关阅读:
    使用Session防止表单重复提交
    Session
    使用Cookie进行会话管理
    Java邻接表表示加权有向图,附dijkstra最短路径算法
    web工程中URL地址的推荐写法
    Web工程中各类地址的写法
    HTTP中的重定向和请求转发的区别
    squid代理
    代理服务 squid 隐藏真实ip,也就是透明代理
    docker添加sshd 服务
  • 原文地址:https://www.cnblogs.com/yixiancheng/p/15169110.html
Copyright © 2020-2023  润新知