文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

什么是JavaScript的作用域

2024-12-03 09:38

关注

现代编程语言的最基本功能之一就是能够存储变量当中的值,以便于之后的使用于修改。也正是这个功能将状态带给了程序。

在JavaScript中,作用域就是一套设计良好的规则来存储变量。

简述编译原理

通常我们会将JavaScript归类为“动态”或“解释执行“语言,但它实际上是一门编译语言。与传统的编译语言不同,它不是提前编译的,编译结果也不能在分布式系统中进行移植。

例如V8引擎,为了提高JavaScript代码的运行性能,在运行之前会先将其编译为本地的机器码,然后再去执行机器码,达到提升速度的目的。

           这个过程将由字符组成的代码分解成对程序有意义的代码块,这些代码块被称为词法单元。

    例如 var foo = 'bar' 通常会被分解为这些词法单元:var 、 foo 、 = 、 'bar'

           这个过程将词法单元转换成一个“由元素逐级嵌套组成的代表程序语法的树“,这个树被称为“抽象语法树”(AST)。    

    image

          将上边的抽象语法树转换为机器可执行代码

JavaScript引擎比只有三个步骤的语言的编译器要复杂的多。例如在语法分析和代码生成阶段有特定的步骤来对运行性能进行优化,包括对冗余元素进行优化等。

对于JavaScript来说,大部分情况下编译发生在代码执行的前几微秒,任何代码片段在执行前都要进行编译。因此JavaScript编译器首先对 var foo = 'bar' 进行编译,然后做好执行它的准备,并且通常马上就会执行它。

引擎、编译器、作用域在赋值操作中的配合

对于 var foo = 'bar' 这段代码,大家很有可能认为是一句简单的声明。而事实上JavaScript执行时会将它分成两个完全不同的声明。

  1.  编译器首先将这段代码分解成词法单元,然后解析为树结构。(在下一步代码生成时,处理这段代码的方式会跟预期有所不同)
  2.  遇到 var foo ,编译器会检查作用域是否已有同名变量存在。如果有的话编译器会忽略声明,继续编译。否则它会生成代码在当前作用域的变量集合中声明一个新的变量,命名为 foo
  3.  接下来编译器会为引擎生成运行时所需代码,用来处理 foo = 'bar' 这个赋值操作。
  4.  引擎运行时会首先查询当前作用域是否存在叫做 foo 的变量。如果有引擎则会使用这个变量,否则会一直向上层作用域查找。
  5.  如果最终找到了 foo 这个变量,就会将 'bar' 赋给它,否则抛出异常。

总结:变量的赋值会执行两个动作:首先是编译器在当前作用域中声明变量(如果变量未被声明过);接着运行时引擎在作用域查找该变量,能找到就会对它赋值。

LHS查询 vs RHS查询

引擎执行编译器生成的代码时,会通过查找 foo 来判断是否已经声明过。查找的过程由作用域来协助。在我们的例子中,引擎为变量 foo 进行的时LHS查询,还有另一个查找类型叫RHS查询。顾名思义,它们的意思是Left hand side 和 Right hand side

  1. // 考虑下边的代码  
  2. console.log(foo) 

此例中 foo 的引用就是RHS查询,这里没有赋予 foo 任何值,相反的,我们需要查找 foo 的值,才能传递给log方法。 

  1. // 相比之下  
  2. foo = 'bar' 

这里对 foo 的查询则是LHS查询,我们并不关心 foo 当前的值是什么, 只是想为这个赋值操作找到目标。 

  1. // 再分析下边的代码  
  2. function foo(a) {  
  3.   console.log(a)  
  4.  
  5. foo('bar') 

这段代码里既有LHS查询又有RHS查询

  1.  最后一行 foo(...) 函数的调用需要对 foo 进行RHS查询 → 找到 foo 的值
  2.  入参时存在隐式的 a = 'bar' ,需要对 a 进行LHS查询
  3.  console.log(a) 对 a 进行RHS查询
  4.  console.log(...) 本身也需要对 console 对象进行RHS查询

作用域的嵌套

我们在文章开始时说过,作用域是根据名称查找变量的一套规则。实际情况中需要同时顾及几个作用域。

当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此在当前作用域中没有查找到目标变量时,会逐层向上查找直到全局作用域。 

  1. // 考虑以下代码  
  2. function foo(a) { 
  3.  console.log(a + b)  
  4.  
  5. var b = 258 
  6. foo(369) 

对 b 进行的RHS查询无法在 foo 内部完成,但可以在上一级的作用域中完成(在此例中是全局作用域)。

LHS,RHS查询都会在作用域内逐层查找,直到找到为止(或到达全局作用域)。

ReferenceError

上一节提到了LHS,RHS都会在作用域内逐层查找变量,但如果到达全局作用域仍然没有找到变量怎么办呢?

这时区分LHS和RHS查询的意义就体现出来了。

如果RHS查询在所有嵌套的作用域中都没有找到所需变量,引擎就会抛出 ReferenceError。

如果LHS查询在所有嵌套的作用域中都没有找到所需变量,引擎就会在全局作用域中创建一个具有该名称的变量,并将其返回给引擎。

注意:ES5中引入了严格模式,与普通模式相比,严格模式其中一个不同就是进制自动或隐式的创建全局变量。因此在严格模式下LHS查询失败时不会创建并返回全局变量,引擎同样会抛出 ReferenceError。

总结

            1.  var foo 在其作用域中声明新的变量。此操作在代码执行前进行。

            2.  接下来 foo = 'bar' 会查询(LHS)变量 foo 并对其赋值。

 

来源:前端大全内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯