JS学习——作用域解析

前言

作用域大家都不陌生,正所谓面试造火箭,现实拧螺丝,面试时我们会碰到各种类型的题,这些题跟作用域的特性息息相关,这种体现理论的题最容易犯错,往往看了解析会恍然大悟。本节我们就来学习一下作用域的特征。

作用域特征

作用域包含几个特征,掌握了它们,相关面试题也就很简单了:

  • 块级作用域。块级作用域简单来说就是{}里面的区域,es6中的let与const声明的变量就是块级作用域,块级作用域的变量只会在块级里面生效。
js 复制代码
   function a(){
      let b = 1
    }
    console.log(b) //b is not defined

b是块级作用域,在块级外就是未定义。除了let、const声明出来的变量,在js中只有函数才有块级作用域。上述例子中我们用let定义了b变量,如果换成var呢?

js 复制代码
    function a(){
      var b = 1
    }
    console.log(b) //b is not defined

依旧是未定义的,因为b是在a函数里面的定义的,它就是块级作用域。除此之外在其他地方如果用var它就是全局的。

js 复制代码
   if(true){
      var a = 0
    }
    console.log(a) //0

这时候把var换成let或const,那么a肯定就是未定义的,因为这两个声明出来的变量是块级的。三者之间的区别我们要牢牢掌握的。

  • 作用域链。函数中的变量值如果当前函数未定义,会去上一层函数中去寻找,直至找到或者到script全局,这就是作用域链,作用链讲究就近原则 ,并且只有从内往外找,外部不能访问内部的变量
js 复制代码
    function a(){
      var c = 1
      function b(){
        var d = 2
        console.log(c) // 1
      }
      b()
      console.log(d) // d is not defined
    }
    a()
  • 全局变量。如果一个变量直接赋值,没有声明那么它就是全局变量,有着全局作用域,全局变量就是在window上面的。
js 复制代码
   (function () {
        var a = b = 1;
      })();
      // console.lg(a); // a is not defined
      console.log(b); // 1

b是直接赋值的,所以b是全局变量,相当于window.b,而a是在函数中定义的,所以a是块级作用域,在函数外打印不出,b却可以。

  • 变量提升。用var声明的变量,会把声明放到最顶层,这种机制被称为变量提升,注意let、const是没有变量提升的。
js 复制代码
      console.log(a) // undefined
      var a = 1
      console.log(b) //Cannot access 'b' before initialization
      let b = 2

a用var声明的,var就会放到前面,所以打印undefined而没有报错,但是let就会直接报错。

实例

熟悉以上特性,碰到相关面试题做出来就不在话下,下面我们实战解析,看下具体的,面试题:

  • 第一道:
js 复制代码
   function a() {
        var b = 1;
        function c() {
          console.log(b);
          var b = 2;
          console.log(b);
        }
        c();
        console.log(b);
      }
      a();

打印出来的依次是undefined、2、1。这道题考察了变量提升、作用域链,在c函数中变量b进行了声明提升,所以第一次打印b为undefined,后面赋值为2,而a函数中,有声明的b,根据作用域链打印出来为1。

  • 第二道:
js 复制代码
   function a(b) {
        console.log(b);
        function b() {}
        console.log(b);
      }
    a(2);

这道题打印出来的是两个b函数,该题考查了变量的优先级,我们在函数中声明了b函数,声明函数提升,所以函数中打印的都是b函数。下面我们将b赋值为函数又会打印什么呢?

js 复制代码
   function a(b) {
        console.log(b);
        var b = function () {};
        console.log(b);
      }
      a(2);

答案是2,赋值的函数,注意这次我们是将b进行赋值,而不是直接声明b函数,那为什么第一个不是undefined呢?因为第一个b表示了形参 ,当我们直接a(2),此时b就是实参的值。通过这两个例子我们知道变量是有优先级,具体的优先级为:
变量声明>函数声明>参数>变量提升

在函数中碰到相同的变量,我们要根据优先级知道它表示的是哪个值。

总结

作用域考查的就是我们的基础,面试中碰到了作用域相关的题,我们可以按照以下步骤去分析:

  1. 变量是否为全局变量、是否在函数内声明。

  2. 找到变量作用域链。

  3. 考虑变量表示的优先级。

    通过以上分析,大概率就能得到正确的答案啦。

相关推荐
泯泷9 小时前
手搓JSVMM第 8 篇:函数调用:参数、返回值与调用帧
前端·javascript·前端框架
Sherotree9 小时前
Google Antigravity——Agent 被提到主界面
前端·ide·后端·edge浏览器
Mr数据杨9 小时前
【Codex】用学生入学评估模块建立新生能力画像
android·java·javascript·django·codex·项目开发
里欧跑得慢10 小时前
Flutter 像素级还原:设计稿到代码的视觉无损转换技巧
前端·css·flutter·web
里欧跑得慢10 小时前
Flutter 三方库 function_tree — 鸿蒙应用开发中的动态数学公式解析与计算神器,实现鸿蒙深度适配下的复杂函数逻辑运行时求值实战(适配鸿蒙 HarmonyOS Next ohos)
android·前端·安全·flutter·华为·harmonyos
苏灿烤鱼11 小时前
没有机密文件,也没有付费数据,在浏览器里造一颗"间谍卫星"
前端·javascript·github
leonkay11 小时前
C# 特性(Attribute)——【1】基础讲解
开发语言·青少年编程·面试·c#·.net·个人开发
泯泷11 小时前
手搓JSVM第 5 篇:写第一个编译器:从 AST 生成 IR
前端·javascript·算法
泯泷11 小时前
手搓JSVM第 7 篇:控制流:if、while 与 jump
前端·javascript·算法
泯泷11 小时前
手搓JSVM第 6 篇:把 IR 编成字节码:emit 与 label fixup
前端·javascript·算法