• 攻略前端面试官(二):作用域和闭包


    本文在个人主页同步更新~

    背就完事了

    介绍:一些知识点相关的面试题和答案
    使用姿势:看答案前先尝试回答,看完后把答案收起来检验成果~

    面试官:如何理解JS的作用域和作用域链

    答:在ES5中,只有全局作用域和局部作用域。ES6因为let,const的引入而有了块作用域。js在浏览器中的顶级作用域是window。作用域链的话,是指子作用域会一级一级向上寻找所有父作用域的变量。

    解析:1. 局部作用域所对应的是函数环境,块作用域对应的是大括号({ ... })内的环境,如if,for,switch等;2. 作用域链简单理解为函数嵌套时,内层函数可以访到上层函数的作用域,而上层又可以往上上层找,从而形成链式结构。

    面试官:什么是变量提升

    答:在js中,所有变量和函数的声明都会被提升到所在作用域的最顶部,所以变量先使用,后声明是不会报错的。

    解析:注意是所在作用域而不是最外层。

    面试官:var和let, const有什么区别

    答:var声明的变量没有块作用域,在最外层声明的话会挂在到window上,并且存在变量提升;let和const声明的是块级变量,不会挂载到window上,也不存在变量提升,而且不能重复声明同一个变量;const声明的变量不能被重新赋值,一般用来声明常量。

    解析:const声明的变量虽然不能被重新赋值,但并不意味着他的值不能被改变。例如当const声明的变量是引用类型的时候。

    面试官:什么是闭包

    答:闭包是一个可以访问到其他函数内部变量的函数

    解析:常见于函数嵌套,子函数可以访问到父级函数作用域下的变量,则子函数称为闭包。

    面试官:闭包有什么作用和缺点

    答:闭包的作用有两个,正常来说,函数作用域外部无法访问到内部的变量,而闭包可以作为桥梁让外部能成功访问到函数的变量;第二个是能使函数变量的值一直保存在内存中。缺点的话就是可能引起内存泄漏,在函数执行完毕后,可能因为闭包导致外层函数的变量一直存在内存中。

    解析:闭包的第一个作用可以理解为,java中的getter方法,外部只能通过getter(闭包)来访问对象(函数)中的私有变量(变量)。

    理解小帮手

    介绍:总结性的图表或笔试题目和解析,让知识点更容易懂

    关于作用域,作用域链,以及变量提升和var,let,const和闭包的相关知识

    下面通过代码分析来加深认识
    注: 建议将复制到浏览器控制台,或者node环境下执行,手机的话建议横屏~

    var count = 0                               // 用来标识打印的序号
    console.log(++count, song)                  // 输出:1 undefined  解析:变量提升
    
    // console.log(++count, artist)             // 报错:'artist'不能在变量声明前使用  解析:let没有变量提升
    
    var song = '说好不哭'
    let artist = '奶茶伦'
    const favorite = '奶茶'
    const girlfriend = {
        name : '蔡依林'
    }
    
    console.log(++count, window.song)           // 输出:2 说好不哭  解析:最外层用var声明会挂载到window上
    
    console.log(++count, window.artist)         // 输出:3 undefined  解析:let声明不会挂载到window
    
    for(var i = 0; i < 10; i++ ) {}             // 在for中用var声明变量i
    for(let j = 0; j < 10; j++ ) {}             // 在for中用let声明变量j
    
    console.log(++count, i)                     // 输出:4 10  解析:var没有块作用域
    
    // console.log(++count, j)                  // 报错:j没有被定义  解析:let有块作用域
    
    // favorite = '咖啡'                        // 报错:常量被赋值  解析:const声明的变量不能被重新赋值
    girlfriend.name = '昆凌'
    console.log(++count, girlfriend)            // 输出:5 {name:'昆凌'}  解析:const声明的引用类型可以被改变
    
    function cooperate () {
        console.log(++count, song)              // 输出:6 说好不哭  解析:作用域链,往上层作用域找song
        var feat = undefined
        function getFeat (artistName) {         // 闭包 - 函数嵌套
            feat = artistName
            return feat
        }
        return getFeat                          // 将闭包暴露出来
    }
    
    // console.log(++count, feat)               // 报错:feat没有被定义  解析:feat在函数cooperate的作用域中,属于局部变量
    
    var getFeat = cooperate()                   // 创建一个闭包
    console.log(++count, getFeat('阿信'))       // 输出:7 阿信  解析:通过闭包成功打印cooperate函数中的feat属性
    
    getFeat = null                              // 闭包使用后需要手动释放,不然会造成内存泄漏
    

    相关笔试题思考

    前端面试的笔试题或多或少会有一至两道考核作用域的题目,如以下经典例子

    function p1() { 
        console.log(1);
    }
    function p2() { 
        console.log(2);
    }
    (function () { 
        if (false) {
            function p1() {
                console.log(3);
            }
        }else{
            function p2(){
                console.log(4)
            }
        }
        p2();
        p1()
    })();
    

    解决这类问题的关键在于

    1. 将所有变量,在纸上手动提升了再说,脑补容易有疏漏
    2. 通过function 声明的函数,也别忘了进行变量提升(function p1(){} 等同于 var p1 = function () {})
    3. 分析作用域,一般会拿var没有块作用域来整事
    var p1;                        // p1 = undefined  解析:变量提升
    var p2;                        // p2 = undefined - 变量提升
    function p1() {                // p1 = function () { 1 }
        console.log(1);
    }
    function p2() {                // p2 = function () { 2 }
        console.log(2);
    }
    (function () {                 // 匿名函数作用域
        var p1;                    // p1 = undefined  解析:变量提升,function p1(){} 等同于 var p1 = function () {},var没有块作用域
        var p2;                    // p2 = undefined  解析:变量提升
        if (false) {
            function p1() {        // p1 = undefined  解析:因为if(false),这块代码不会执行
                console.log(3);
            }
        }else{
            function p2(){         // p2 = funtcion() { 4 }
                console.log(4)
            }
        }
        p2();                      // 在当前作用域找到了p2,打印4
        p1()                       // 在当前作用域找到了p1,报错:p1 is not a function
            })();       
    

    以上!


    Kane -- 一切都是命运石之门的选择

  • 相关阅读:
    中国区 Azure 服务和定价模式概述
    云计算的那些「What」
    【虚拟机-可用性集】ARM 中可用性集使用的注意事项
    【虚拟机-可用性集】将虚拟机添加到可用性集中
    【虚拟机-远程连接】Azure Linux 虚拟机常见导致无法远程的操作
    【虚拟机-远程链接】Azure Windows 虚拟机常见导致无法远程的操作
    【虚拟机-网络IP】使用 Powershell 设置 VNET 中的静态 IP
    matlab的diff()函数
    matlab的拟合函数polyfit()函数
    解决Python各种no module named "XX"的问题
  • 原文地址:https://www.cnblogs.com/rainykane/p/12009177.html
Copyright © 2020-2023  润新知