【js作用域】JavaScript中作用域的是什么?:从编译时其承担什么角色和查询作用域中的变量的角度解析作用域

在这里插入图片描述

😁 作者简介:一名大四的学生,致力学习前端开发技术
⭐️个人主页:夜宵饽饽的主页
❔ 系列专栏:JavaScript进阶指南
👐学习格言:成功不是终点,失败也并非末日,最重要的是继续前进的勇气

​🔥​前言:

这里是关于作用域真正的面目,涉及到编译时,作用域的作用和承担的角色,还有我们在查找变量时运用的LHS和RHS查询的方法,希望可以帮助到大家,欢迎大家的补充和纠正

文章目录

    • 第1章 作用域是什么
      • 1.1 编译原理
      • 1.2 理解作用域
        • 1.2.1 先介绍一下图中的关键人物
        • 1.2.1 再介绍一下关键的过程
      • 1.3 作用域的嵌套
      • 1.4 异常

第1章 作用域是什么

几乎所有编程语言最基本的功能之一,就是能够储存变量当中的值,并且能在之后对这个值进行访问或修改。事实上,正是这种储存和访问变量的值的能力将状态带给了程序。

若没有了状态这个概念,程序虽然也能够执行一些简单的任务,但它会受到高度限制,做不到非常有趣。

但是将变量引入程序会引起几个很有意思的问题,也正是我们将要讨论的:这些变量住在哪里?换句话说,它们储存在哪里?最重要的是,程序需要时如何找到它们?

这些问题说明需要一套设计良好的规则来存储变量,并且之后可以方便地找到这些变量。这套规则被称为作用域。

但是,究竟在哪里而且怎样设置这些作用域的规则呢?

1.1 编译原理

程序中的源代码在执行之前会经历三个步骤,统称为“编译”

  • 分词/词法分析

    这个过程会将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代码块被称为词法单元(token)。例如,考虑程序 var a = 2;。这段程序通常会被分解成为下面这些词法单元:var、a、=、2 、;。空格是否会被当作词法单元,取决于空格在这门语言中是否具有意义。

    分词(tokenizing)和词法分析(Lexing)之间的区别是非常微妙、晦涩的,主要差异在于词法单元的识别是通过有状态还是无状态的方式进行的

    ❓ 什么是有状态和无状态:

    • 有状态:在有状态的识别中,解析器会维护一个状态,它会跟踪源代码的位置并根据当前状态识别词法单元。这意味着解析器会考虑上下文以确定如何解释源代码
    • 无状态:在无状态的识别中,解析器不维护状态,它单纯地从源代码的开头开始逐个识别词法单元,而不考虑上下文
  • 解析/语法分析

    这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)

  • 代码生成

    将 AST 转换为可执行代码的过程称被称为代码生成。这个过程与语言、目标平台等息息相关。抛开具体细节,简单来说就是有某种方法可以将 var a = 2; 的 AST 转化为一组机器指令,用来创建一个叫作 a 的变量(包括分配内存等),并将一个值储存在 a 中。

在这里插入图片描述

⭐️ 总结:

对于 JavaScript 来说,大部分情况下编译发生在代码执行前的几微秒(甚至更短!)的时间内。在我们所要讨论的作用域背后,JavaScript 引擎用尽了各种办法(比如 JIT,可以延迟编译甚至实施重编译)来保证性能最佳。

1.2 理解作用域

在这里插入图片描述

1.2.1 先介绍一下图中的关键人物
  • 引擎:从头到尾负责整个JavaScript程序的编译及执行过程
  • 编译器:引擎的好朋友之一,负责语法分析及代码生成的脏话累活
  • 作用域:引擎的另一位好朋友,负责收集并维护所有声明的标识符,组成的一系列查询,并实施一套非常严格的规则,确定当前执行的代码对这些表示符的访问权限
1.2.1 再介绍一下关键的过程
  1. LHS查询:引擎对变量查询的一种方法,LHS 查询是赋值操作的左侧查询。它发生在试图将一个值赋给一个变量时,例如:当你执行 var a = 42;,JavaScript引擎需要进行LHS查询来找到变量 a,以便将值 42 存储在变量 a 中。
  2. RHS查询:RHS查询是赋值操作的右侧查询。它发生在试图获取变量的值时,而不是赋值,例如:如果你执行 console.log(a);,JavaScript引擎会执行RHS查询来获取变量 a 的值,并将其传递给 console.log 函数进行打印。

1.3 作用域的嵌套

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

当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,或抵达最外层的作用域(也就是全局作用域)为止

遍历嵌套作用域链的规则很简单:引擎从当前的执行作用域开始查找变量,如果找不到,就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都会停止。

我们这里可以引用《不知道的javaScript》的书籍中嵌套图:
在这里插入图片描述

这个建筑代表程序中的嵌套作用域链。第一层楼代表当前的执行作用域,也就是你所处的

位置。建筑的顶层代表全局作用域。

LHS 和 RHS 引用都会在当前楼层进行查找,如果没有找到,就会坐电梯前往上一层楼,

如果还是没有找到就继续向上,以此类推。一旦抵达顶层(全局作用域),可能找到了你

所需的变量,也可能没找到,但无论如何查找过程都将停止。

1.4 异常

为什么区分LHS和RHS是一件重要的事情?

考虑以下代码:

function foo(a) {
	console.log( a + b );
	b = a;
}
foo( 2 );

第一次对 b 进行 RHS 查询时是无法找到该变量的。也就是说,这是一个“未声明”的变量,因为在任何相关的作用域中都无法找到它。

  • 如果 RHS 查询在所有嵌套的作用域中遍寻不到所需的变量,引擎就会抛出 ReferenceError异常。值得注意的是,ReferenceError 是非常重要的异常类型。

  • 相较之下,当引擎执行 LHS 查询时,如果在顶层(全局作用域)中也无法找到目标变量,全局作用域中就会创建一个具有该名称的变量,并将其返还给引擎,前提是程序运行在非“严格模式”下。

ES5 中引入了“严格模式”。同正常模式,或者说宽松 / 懒惰模式相比,严格模式在行为上有很多不同。其中一个不同的行为是严格模式禁止自动或隐式地创建全局变量。因此,在严格模式中 LHS 查询失败时,并不会创建并返回一个全局变量,引擎会抛出同 RHS 查询失败时类似的ReferenceError异常

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/161178.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python语言这么火热,其实具有以下特点

Python语言具有以下特点: 简单易学:Python语言是一种解释型语言,语法简单明了,代码简洁,易于理解,可以一边编码一边运行,非常合适编程初学者。门槛较低:Python不需要复杂的环境配置…

null 不好,我真的推荐你使用 Optional

"Null 很糟糕." - Doug Lea。 Doug Lea 是一位美国的计算机科学家,他是 Java 平台的并发和集合框架的主要设计者之一。他在 2014 年的一篇文章中说过:“Null sucks.”1,意思是 null 很糟糕。他认为 null 是一种不明确的表示&#x…

持续集成交付CICD:Jenkins Sharedlibrary 共享库

目录 一、理论 1.共享库 2.共享库配置 3.使用共享库 4.共享库扩展 二、实验 1.连接共享库 2.使用共享库 三、问题 1.路径报错 2.readJSON 报错 一、理论 1.共享库 (1)概念 1)共享库这并不是一个全新的概念,其实在编…

electron使用better-sqlite3打包失败(electron打包有进程没有界面)

remove *\chrome_100_percent.pak: Access is denied. 解决: 管理员权限执行:taskkill /IM 你的进程名.exe /F,再次执行build electron使用better-sqlite3打包后有进程没有界面 原因是代码及依赖包安装有误,模块丢失。主要分享的…

企业月结快递管理教程

什么是月结快递?员工可能不清楚,但是企业行政人员应该很熟悉。各大快递公司为了留住商企这些大客户,推出了月结协议寄件,企业可以和快递公司签订月结协议,员工寄件不需要当场结算快递费,而是将快递费挂在企…

Git详解及 github使用

1.1 关于版本控制 开始之前先看一个没有版本控制的例子 1.1.1 本地版本控制 本地版本控制系统 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的 好处就是简单,但是特别容易犯错。有时候会混淆所在…

Android 解决CameraView叠加2个以上滤镜拍照黑屏的BUG (二)

1. 前言 这段时间,在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位,在项目前期,的确为我们节省了不少时间。 但随着项目持续深入,对于CameraView的使用进入深水区,逐…

windows快捷方式图标变成空白

今天突然有客户说应用程序快捷方式图标变成了空白,就研究了一下,网上找了一下很多都说是什么图标缓存有问题,试过之后发现并不能解决问题。 然后发现用户的文件上都一把黄色的小锁的标志,查了一下说是文件属性里面设置加密之后就会…

【全网首发】2023年NOIP真题

目录 前言 真题 结尾 前言 NOIP题目了解一下,后续有可能会出讲解,题目全部来自于洛谷 真题 第一题:词典 第二题:三值逻辑 第三题:双序列扩展 第四题: 天天爱打卡 结尾 大家可以把你的预期分数打在评论…

Java(一)(引用类型的参数在传递,方法重载,面向对象编程基础)

基本类型和引用类型的参数在传递的时候有什么不同? 基本类型的值传递:参数传输存储的数据值 引用类型的值传递:参数传输存储的地址值 传递数组名字的时候,传递的是数组的地址,change方法可以通过地址直接访问我们在堆内存中开辟的数组,然后改变数组,数组中的元素发生变化 方…

Google Chrome 任意文件读取 (CVE-2023-4357)漏洞复现

Google Chrome 任意文件读取 (CVE-2023-4357)漏洞复现 1.漏洞描述 该漏洞的存在是由于 Google Chrome 中用户提供的 XML 输入验证不足。远程攻击者可以创建特制网页,诱骗受害者访问该网页并获取用户系统上的敏感信息。远程攻击者可利用该漏洞通过构建的 HTML 页面…

Linux进程控制

目录 写实拷贝 为什么要写实拷贝? fork函数 返回值 常规用法 调用失败的原因 进程终止 情况分类 a.代码正常执行完了 b.崩溃了(进程异常) 进程的退出码 c语言提供的系统的退出码 如何理解进程退出 操作都有哪些方式&#xff…

「Verilog学习笔记」使用3-8译码器①实现逻辑函数

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点,刷题网站用的是牛客网 timescale 1ns/1nsmodule decoder_38(input E1_n ,input E2_n ,input E3 ,input A0 ,input A1…

ExoPlayer架构详解与源码分析(9)——TsExtractor

系列文章目录 ExoPlayer架构详解与源码分析(1)——前言 ExoPlayer架构详解与源码分析(2)——Player ExoPlayer架构详解与源码分析(3)——Timeline ExoPlayer架构详解与源码分析(4)—…

Linux 阻塞机制及等待队列

原文地址: http://www.cnblogs.com/gdk-0078/p/5172941.html 阻塞与非阻塞是设备访问的两种方式。驱动程序需要提供阻塞(等待队列,中断)和非阻塞方式(轮询,异步通知)访问设备。在写阻塞与非阻塞的驱动程序时…

DB9串口引脚介绍

一、公头和母头 图片示意源于网络: 二、 每个引脚的功能定义 公头:所有排针式的接头(5针朝上,从左到右序号依次是1~9) 母头:所有插槽式的接孔(5孔朝上,从右到左序号依次是1~9) 针…

Go 之 captcha 生成图像验证码

目前 chptcha 好像只可以生成纯数字的图像验证码,不过对于普通简单应用来说也足够了。captcha默认将store封装到内部,未提供对外操作的接口,因此使用自己显式生成的store,可以通过store自定义要生成的验证码。 package mainimpor…

“升级图片管理,优化工作流程——轻松将JPG转为PNG“

在图片时代,无论是工作还是生活,图片管理都显得尤为重要。批量处理图片,将JPG格式轻松转换为PNG格式,能够使您的图片管理更优化,提高工作效率。 首先,我们进入首助编辑高手主页面,会看到有多种…

Springboot更新用户密码

UserController PatchMapping("/updatePwd")//RequestBody注解&#xff0c;mvc框架才能自动的去读取请求体里的json数据&#xff0c;转换成map集合对象public Result updatePwd(RequestBody Map<String,String> params){//1.校验数据String oldPwd params.get…

Leetcode——最长递增子序列

1. 题目链接&#xff1a;300. 最长递增子序列 2. 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&a…