【面试常见题】一文带你理解透彻JavaScript中的词法作用域

115 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第23天,点击查看活动详情

前言

😀 面试中,作用域的问题是最常见的,不论是初级、中级、还是高级,只要你是做前端的,基本都会问这个问题。

😀 而作用域是前端编程语言中最基本的功能,它能够存储变量当中的值,并且可以在之后的逻辑中对这个值进行访问和修改。

😀 但是将这个作用域中的变量引入到代码中,就会发生一些神奇的事情,这也是这篇文章中会带你理解的部分。

词法作用域

😉 词法作用域就是定义在词法阶段的作用域。你也可以理解为你在写代码的时候,将变量和块作用域写在哪里就觉得了词法作用域在哪里。

function foo(a){
    var b = a * 2;
    
    function bar(c){
        console.log(a, b, c)
    }
    
    bar(b * 3);
}

foo(2);

🤪 上面这段代码,我们不需要理解输出的结果是什么,我们只需要理解其中的作用域关系即可。

  • foo函数包含着整个全局作用域。
  • a、 bar、 b包含在foo函数所创建的作用域中。
  • c包含在bar函数所创建的作用域中。

为了便于理解,可以将上面这段代码想象成几个逐级包含的气泡。

也就是这些作用域气泡由其对应的作用域块代码写在哪里决定的。

  • 当代码在支持 console.log() 时,它需要查找 a、b、c 三个变量的引用
  • 查找的过程就是逐层往外去寻找,找到了就使用这个引用关系

作用域查找会在找到第一个匹配的标识符时停止。

无论函数在哪里被调用、如何调用,它的词法作用域都只由函数被声明时所处的位置决定。

eval函数

在JavaScript词法作用域中,由于它是在写代码期间被函数的声明位置定义的,那么怎么才能在运行的过程中去修改词法作用域呢?

😷 先看代码,再来分析

function foo(str, a){
    eval(str);
    console.log('输出:', a, b);
}

var b = 1;

foo('var b = 2', 0);

image.png

  • 这里输出的是 0 和 2
  • 但是我们定义的全局变量 b 的值是 1 ,为什么会变成 2 呢?

😌 这是因为 eval 函数虽然接收的是一个字符串,但是它并不会将它视为一段字符串,而是视为书写时就存在于程序中的一段代码,然后放置在 eval 函数执行的位置。

😌 也正是因为 eval 函数通过欺骗的方式来实现修改词法作用域

总结

在JavaScript的词法作用域中的作用域是由书写代码时函数声明的位置来决定的,编译过程中,可以使用 eval 函数来修改其中声明的某个作用域。

在使用 eval 函数时,也会导致代码运行变慢,所以最好是不要使用它