zoukankan      html  css  js  c++  java
  • 一篇常做错的经典JS闭包面试题

    在这里插入图片描述

    作者 | Jeskson

    来源 | 达达前端小酒馆

    1

    究竟是怎么样的一道面试题,能让我拿出来说说呢?下面请看代码:

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(c) {
                return fun(c,a);
            }
        };
    }
    
    var d = fun(0); d.fun(1); d.fun(2);
    d.fun(3);
    var d1 = fun(0).fun(1).fun(2).fun(3);
    var d2 = fun(0).fun(1);
    d2.fun(2);
    d2.fun(3);
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(c) {
                return fun(c,a);
            }
        };
    }
    var d = fun(0); d.fun(1); d.fun(2);
    d.fun(3);
    var d1 = fun(0).fun(1).fun(2).fun(3);
    var d2 = fun(0).fun(1); d2.fun(2);
    d2.fun(3);
    
    undefined
    VM1036:2 0
    VM1036:2 0
    VM1036:2 0
    
    VM1036:2 undefined
    VM1036:2 0
    VM1036:2 1
    VM1036:2 2
    
    VM1036:2 undefined
    VM1036:2 0
    VM1036:2 1
    VM1036:2 1
    {fun: ƒ}
    
    //答案:
    //undefined,0,0,0
    //undefined,0,1,2
    //undefined,0,1,1
    

    JS函数分两种:具名函数(命名函数)和匿名函数。

    如何判断两种函数的方法呢?

    可以用fn.name来判断,如果有name就是具名函数,如果没有name就是匿名函数。

    需要注意的是在IE浏览器上无法获取具名函数的name,会返回undefined的结果,而在谷歌浏览器上就可以获取。

    // 获取名称
    function getFunctionName(fun){
     if(fun.name !== undefined)
      return fun.name;
      var funName = fun.toString();
      funName = funName.substr('function '.length);
      funName = funName.substr(0, funName.indexOf('('));
      return funName;
    }
    

    2

    函数创建的方法有哪些?

    第一种是:声明函数,声明函数方法,包括函数名和函数体。

    function funDa() {}
    

    第二种是:创建匿名函数的表达式。

    创建一个变量,这个变量的内容是一个函数,为匿名函数

    var funDa = function() {}
    

    这样这个函数就没有了name

    var funDa = function(){}
    getFunctionName(funDa).length;
    // 0
    

    第三种是:创建具名函数表达式。

    var funDa = function dada(){};
    

    创建一个变量,变量赋值的内容为一个带有名称的函数。具名函数表达式的函数名只能在创建函数内部使用,函数的外层只能使用funData,dada函数名只能在创建函数内部使用。

    在这里插入图片描述

    在这里插入图片描述
    在对象内定义函数,也是属于函数表达式。

    第四种是:Function构造函数

    在这里插入图片描述

    Function("alert(1)");
    ƒ anonymous() {
    alert(1)
    }
    
    Function("dada");
    ƒ anonymous() {
    dada
    }
    
    new Function("alert(1)");
    ƒ anonymous() {
    alert(1)
    }
    
    new Function("dada");
    ƒ anonymous() {
    dada
    }
    

    Function构造函数传一个函数字符串,返回包含这个字符串命令的函数。

    第五种是:自执行函数

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    ( function(){
        alert(1);
    })();
    undefined
    
    ( function(){
        alert(1);
    })
    ƒ (){
        alert(1);
    }
    

    在这里插入图片描述

    (function da1(){
        alert(1);
    })();
    

    自执行函数也是“函数表达式”。

    第六种是:其他

    运用eval,setTimeout,setInterval等方法。

    3

    在这里插入图片描述

    第一个fun函数是属于标准的具名函数声明,是新创建的函数,返回的是一个对象字面量表达式,属性一个新的Object。

    这返回,对象内部包含一个fun的属性,属于匿名函数表达式,这个fun属性存放的是一个新创建匿名函数表达式,所有声明的匿名函数都是一个新函数。则第一个fun函数和第二个fun函数不同,都是新创建的函数。

    4

    函数作用域链的问题

    在这里插入图片描述
    对象内部的函数表达式:

    var d = {
        fn: function(){
            console.log(fn);
        }
    };
    d.fn();
    
    VM1879:3 Uncaught ReferenceError: fn is not defined
        at Object.fn (<anonymous>:3:21)
        at <anonymous>:6:3
    fn @ VM1879:3
    (anonymous) @ VM1879:6
    
    var d1 = {
        fn: function(){
            console.log("dada");
        }
    };
    d1.fn();
    
    VM1973:3 dada
    undefined
    

    非对象内部的函数表达式:

    在这里插入图片描述

    var da = function () {
        console.log(da);
    };
    da();
    VM2270:2 ƒ () {
        console.log(da);
    }
    undefined
    

    使用var可以访问到存放当前函数的变量,var da,da()访问函数的变量,在对象内部不能访问到。

    5

    函数作用域链:

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(c) {
                return fun(c,a);
            }
        };
    }
    var d = fun(0); d.fun(1); d.fun(2);
    d.fun(3);
    var d1 = fun(0).fun(1).fun(2).fun(3);
    var d2 = fun(0).fun(1); d2.fun(2);
    d2.fun(3);
    
    var d = fun(0); 
    d.fun(1); 
    d.fun(2);
    d.fun(3);
    
    undefined
    VM2273:2 0
    VM2273:2 0
    VM2273:2 0
    

    第一个fun(0)在调用第一层fun函数,第二个fun(1)是在调用前一个fun的返回值的fun函数。即就是fun(1),fun(2),fun(3)函数独使在调用第二层fun函数,第一次调用fun(0)时,b为undefined,第二次调用fun(1),c为1,a为0。

    var d = fun(0);调用的是第一层

    而d.fun->fun(0).fun调用第二层

    fun:function(1),return fun(1,a),fun(1,0),此时fun闭包了外层函数的a,也就是第一次调用的a=0。这样子第一层fun函数为fun(1,0),所以为0。

    第一次:

    function fun(0,undefined) {
        console.log(undefined)
        return {
            fun: function(c) {
                return fun(c,0);
            }
        };
    }
    

    fun(0),b为undefined,fun(0).fun(1),c=1,此时fun闭包外层函数的a,也就是第一次调用的a=0,即c=1,a=0,并在内部调用第一层fun函数fun(1,0),所以b=0。

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(1) {
                return fun(1,0);
            }
        };
    }
    

    第三次调用fun(2)时,c为2,还是调用d.fun,还是闭包了第一次调用时的a,fun(2,0)所以输出b为0。

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(c) {
                return fun(c,a);
            }
        };
    }
    var d = fun(0); d.fun(1); d.fun(2); d.fun(3);
    

    6

    var d1 = fun(0).fun(1).fun(2).fun(3);
    

    在这里插入图片描述

    从fun(0)调用第一层fun函数,返回值为一个对象,第二个fun(1)调用的是第二层fun函数,后面的也是第二层fun函数。

    第一层fun(0),b为undefined,第二层.fun(1)时c为1,c=1,a=0,内部调用第一层fun函数fun(1,0),所以b为0。

    调用你.fun(2)时,c为2,此时当前的fun函数不是第一次执行的返回对象,而是第二次执行的返回对象,第二次执行第一层fun函数是:

    fun(1,0),a=1,b=0。第三次执行fun函数,c=2,a=1

    function fun(a,b) {
        console.log(b)
        return {
            fun: function(1) {
                return fun(1,0);
            }
        };
    }
    
    fun(1,0),a=1,b=0。第三次执行fun函数,c=2,a=1
    
    function fun(a,b) {
        console.log(b)
        return {
            fun: function(2) {
                return fun(2,1);
            }
        };
    }
    
    // 1
    
    function fun(2,1) a=2, b=1
    

    第四次调用.fun(3)为c为3

    // a=2
    function fun(a,b) {
        console.log(b)
        return {
            fun: function(3) {
                return fun(3,2);
            }
        };
    }
    

    7

    var d2 = fun(0).fun(1);
    d2.fun(2);
    d2.fun(3);
    

    在这里插入图片描述
    var d2=fun(0).fun(1);
    // undefined, 0
    此时的return(c=1,a=0),return fun(1,0),所以b为0

    d2.fun(2);
    第三次调用.fun(2),c为2

    
    // c为2,a=1,b=0
    function fun(a,b) {
        console.log(b)
        return {
            fun: function(2) {
                return fun(2,a);
            }
        };
    }
    
    所以return fun(2,1)
    function fun(a=2,b=1),所以为 1
    

    d2.fun(3),c为3,还是调用的第二次的返回值,最终调用第一层的
    fun(a,b)

    // c为3,a=1,b=0
    function fun(a,b) {
        console.log(b)
        return {
            fun: function(3) {
                return fun(3,a);
            }
        };
    }
    
    所以return fun(3,1)
    function fun(a=3,b=1),所以为 1
    

    注意这里的:

    // c为3,a=1,b=0
    这是调用这个代码的结果
    
    a=1,b=0
    
    var d2 = fun(0).fun(1);
    

    好了,这样就知道大概的答案和讲解了:

    8

    总的来说,你明白了!讲的好辛苦,给个赞哦!求奖励,我来了

    在这里插入图片描述

    
    function fun(a,b) {
        console.log(b)
        return {
            fun: function(c) {
                return fun(c,a);
            }
        };
    }
    var d = fun(0); d.fun(1); d.fun(2);
    d.fun(3);
    var d1 = fun(0).fun(1).fun(2).fun(3);
    var d2 = fun(0).fun(1); d2.fun(2);
    d2.fun(3);
    
    
    undefined
    VM1036:2 0
    VM1036:2 0
    VM1036:2 0
    
    VM1036:2 undefined
    VM1036:2 0
    VM1036:2 1
    VM1036:2 2
    
    VM1036:2 undefined
    VM1036:2 0
    VM1036:2 1
    VM1036:2 1
    {fun: ƒ}
    

    一名喜爱编程技术与专注于前端的程序员,将web前端领域、数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。分享web前端相关的技术文章、工具资源,精选课程、热点资讯。

    推荐阅读

    1、你知道多少this,new,bind,call,apply?那我告诉你

    2、为什么学习JavaScript设计模式,因为它是核心

    3、一篇文章把你带入到JavaScript中的闭包与高级函数

    4、大厂HR面试ES6中的深入浅出面试题知识点

    ❤️ 不要忘记留下你学习的脚印 [点赞 + 收藏 + 评论]

    作者Info:

    【作者】:Jeskson
    【原创公众号】:达达前端小酒馆。
    【福利】:公众号回复 “资料” 送自学资料大礼包(进群分享,想要啥就说哈,看我有没有)!
    【转载说明】:转载请说明出处,谢谢合作!~

    大前端开发,定位前端开发技术栈博客,PHP后台知识点,web全栈技术领域,数据结构与算法、网络原理等通俗易懂的呈现给小伙伴。谢谢支持,承蒙厚爱!!!


    若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。


    请点赞!因为你们的赞同/鼓励是我写作的最大动力!

    欢迎关注达达的CSDN!

    这是一个有质量,有态度的博客

    前端技术栈

  • 相关阅读:
    Mysql8.0中caching_sha2_password报错解决
    Centos7 安装mysql-8.0.18(rpm)
    如何有效的清理yum缓存
    虚拟机安装后配置IP地址
    正确计算linux系统内存使用率
    Linux 怎样更改locale语言设置
    linux把EDT时间修改为CST格式
    开放接口的安全验证方案(AES+RSA)
    Linux下JDK中文字体乱码
    服务器http请求https服务时报错解决方案
  • 原文地址:https://www.cnblogs.com/dashucoding/p/12081027.html
Copyright © 2011-2022 走看看