JavaScript中的LHS和RHS

news2025/1/9 21:54:12

LHS和RHS之前我们先来回忆一下最简单的赋值操作!

var test=100;
console.log(test);

以上代码的意思简单我们理解为把右边赋值给左边test变量,然后输出打印结果。

可是我们要是深入理解你就会发现在这个过程当中,还发生了一些其他的事情

而这些事情就是今天我们要说的LHS和RHS

就比如之前那段代码:

var test=100;

JS会将其看成两句声明:var testtest=100 弄成两个部分: 编译代码执行

var test 这个部分也就是定义声明的时候就开始进行编译(编译器)

test=100 而后面这一段赋值声明会留在原地等待代码执行(引擎)

那么JS中的变量赋值操作会被拆分执行为两个动作:

  1. JS编译器会在当前作用域中声明这个变量,当然是这个变量不存在的情况下!

  2. 然后在运行代码时,JS引擎会在作用域查找这个变量,如果能找到就会对它进行一些操作,例如:赋值操作

而以上这两部的操作又间接引出LHS和RHS的概念!

所以这里的JS编译又与传统的编译语言是不同的!

LHS与RHS基本概念

LHS 全称为: Left-hand Side(左侧引用)

LHS其实就是赋值操作左侧查询,LHS查询试图找到变量的容器本身,从而对其赋值!

注意: =操作符调用函数时传入参数的操作都会导致赋值操作!

小结

通常情况下,如果查找的目的是对变量进行赋值,那么就会使用LHS查询 也就是当变量出现在赋值操作左侧

RHS 全称为: Right-hand Side(右侧引用)

RHS其实就是赋值操作右侧查询,可以理解为需要获取到某值

小结

通常情况下,如果查找的目的是获取变量或函数的值,就会使用RHS查询, 也就是变量出现在赋值操作右侧

总的来说LHS和RHS通常是指等号赋值运算的时候,左右边的引用!

可能这样说对于理解LHS和RHS还是比较抽象,我们要用一个案例来解释一下!

举个梨子

console.log(test);

按照LHS与RHS的查找规范, 这段代码就是一个所谓的RHS右侧引用

因为这里test变量,我们并没有对其进行赋值操作,而只是想在作用域当中查找这个变量并取得值,然后输出打印,所以执行的就是RHS

test = 100;

而这段代码当中 按照LHS与RHS的查找规范, 就是一个LHS左侧引用

因为此时JS并不关心当前的值是什么, 只是想要给当前这个赋值操作找到一个目标容器!

我们再看一个案例, 大家可以猜猜看以下代码当中有多少个LHS查询 又有多少RHS查询?

代码如下

function foo(num) {
console.log(num);
}

foo(100);

分析

  1. 首先function foo(num)这里就存在一个LHS查询 因为100赋值给了num形参对吧,也就相当于num=100,那么就会在这个函数作用域当中先找出num这个变量容器!

  2. foo(100) 本身就是在求返回值的操作, 在作用域当中就会进行RHS查找这个foo(100)函数是否存在, 并没有对其进行赋值操作, 所以这里很明显就是一个RHS查找

  3. 最后console.log(num)这里其实又是一个RHS查询, 因为在这行代码中,我们只有一个变量 num 被使用,因此在 console.log(num) 中只有一个RHS查询,在作用域中查找, 用于获取num的值!

所以正确答案是以上代码当中存在1个LHS查询, 2个RHS查询

所以通过上面的案例最后我们可以把LHS与RHS简单的理解为以下概念:

赋值操作的目标是谁,那么就是LHS(左侧引用查找),

谁是赋值操作的源头,则就是RHS(右侧引用查找)

面试题: 请找出以下代码当中,所有的LHS和所有的RHS

function test(num) {
var num2 = num;
return num + num2;
}
var num = test(100);

代码分析

包含LHS的代码

  1. var num = test(100) 这一段代码很显然是一个LHS,因为num在赋值运算的左边,也就是赋值操作的目标,所以要对num变量进行LHS查询, 那么这里的查询过程就是由作用域(词法作用域)进行配合查找的!

  2. function test(num)的形参num在调用test(100)时,将实参100赋值给了形参num,也就是num=100,因为形参num在赋值运算的左边,也就是赋值操作的目标,所以要对形参num进行LHS查询

  3. var num2 = num 这一段代码也是一个LHS,因为num2在赋值运算的左边,也就是赋值操作的目标,所以要对num2进行LHS查询

包含RHS的代码

  1. var num = test(100)这段代码虽然有LHS查询但同时也有RHS 原因之前其实我们也说过,可以把这段代码分成两个部分来看,其中就有test(100)调用这部分,而这里恰恰是要求得一个结果,需要知道test(100)的值是多少 那么根据谁是赋值操作的源头是谁则就是RHS,从而获取test(100)的返回值

  2. var num2 = num也跟上面同理虽然有LHS查询但同时也有RHS,也就是其中也有num这个变量在赋值运算符右边, 此时需要知道num的值 那么根据谁是赋值操作的源头是谁则就是RHS

  3. return num + num2 这里我们按照(词法作用域查找)思维来说需要知道 numnum2的值, 也就是说只是想查找这两个变量,并取得它们的值,然后才是进行求和操作, 所以这里就是RHS查找, 也就是需要分别对num 和num2都进行RHS查询

    那么根据上面的案例当中,要看出左侧右侧并不一定意味这就是=号的左侧和右侧,赋值操作还有其他几种形式

小结

如果查找的目的是对变量进行赋值,那么就会使用LHS查询
如果目的是获取变量的值,就会使用RHS查询

LHS与RHS查找规则

从之前的案例当中,不管是LHS还是RHS 都会在当前执行的作用域中开始查找变量

LHS在查找的时候,是把右边赋值给左边变量那么就会对左边变量进行当前作用域中的LHS查询,来判断是否声明过!

RHS在查找的时候,是先看谁是赋值操作的源头, 然后在这个基础之上进行当前作用域中的RHS查询,简单点说也就是在当前作用域中查找右边变量或者函数表达式来判断是否已经声明过!

那么LHS和RHS在当前作用域当中如果没有找到所需的标识符 就会根据作用域链向上一级作用域继续查找该标识符,以此类推这样每次上升一层作用域去查找, 最后到达全局作用域就会停止,这也是我们之前讲过的作用域链!

我们可以回顾一下之前的作用域链

如图

LHSRHS都会在当前执行代码的所在环境进行查找, 如果没有找到,才往上一层查找 以此类推, 一旦抵达顶层全局作用域之后,可能找到了你所需的变量,也可能没找到,但无论如何查找过程都将停止!

结合(作用域+编译器+JS引擎)来理解LHS和RHS

我们用一段代码来说明:

var test = 100;

分析

JS引擎 会认为这里有两个完全不同的声明

一个由编译器在编译时处理也就是var test

另一个则由JS引擎在运行时处理,也就是test = 100

编译器遇到var test 会向当前作用域询问是否已经存在这个变量, 如果有就交给编译器继续进行编译赋值, 否则它会要求作用域当前作用域的中声明一个新的变量test

然后JS引擎运行代码的时候,会首先询问作用域,在当前的作用域中是否存在一个叫作test变量, 如果有,JS引擎就会使用这个变量, 如果没有JS引擎会继续根据作用域链查找该变量

如果JS引擎最终找到了test变量,就会将100赋值给它, 否则JS引擎就会抛出一个异常!

LHS与RHS异常问题

RHS查询当前作用域中如果找不到变量,引擎会抛出ReferenceError错误

例如: 直接在整个作用域当中打印输出一个没有定义声明变量或函数就会报ReferenceError错误

如图

例如:

代码如下

function foo(num) {    console.log(num + data);     data = num;}foo( 100);

以上的代码当中进行了RHS但无法查找到, 因为作用域是往上查找的,这里也很明显是找不到的!

如图

函数也是一样, 在调用一个完全没有声明函数时也会抛出ReferenceError错误

就比如说如下代码:

test();

如图

所以在作用域中直接调用一个没有定义的test()函数 直接在整个作用域进行了RHS查找也没有查到

自然会报ReferenceError错误

但是如果RHS作用域中查找到变量,但是进行了不规范的操作, 比如: 在末尾加了个()简单点说就是试图对一个非函数类型值/变量进行函数调用 那么JS引擎会抛出另一种异常叫做 TypeError(类型异常)

如图

还有就是如果RHS作用域中查找到变量, 但是引用了nullundefined 类型的值中的属性,也会报一个TypeError(类型异常)的错误!

知识点复习

在 JS 中,null 和 undefined 是特殊的值,用于表示变量或属性不存在或没有值。如果你引用 null 或 undefined 类型的值中的属性,意味着你尝试访问一个不存在的属性或者一个没有被赋值的变量。具体而言,如果你尝试在一个 null 或 undefined 类型的值中访问一个属性,JS引擎会抛出一个类型错误(TypeError),提示你不能在 null 或 undefined 上访问属性。

例如,以下代码会抛出TypeError(类型异常)!

var obj=null;obj.username;

如图

所以这里总结以下:

ReferenceError作用域查找失败,也就是说找不到这个变量或者函数才抛出来的

TypeError则代表作用域查找成功了, 但是对结果的操作是非法不合理的!

JS引擎执行LHS查询时,如果在所有作用域中找不到目标变量,就会在全局作用域中创建一个与该变量名相同的全局变量,并将其返回给JS引擎,前提是在非严格模式下 这也正好呼应了我们前面所讲解的隐式全局变量的真正含义!

代码分析以下函数当中的a = 100其实是一个LHS,但是变量a并没有在函数块当中进行声明就直接赋值了,那么没声明的变量正常情况下,作用域中是找不到的那么LHS则会在"全局作用域"中创建一个与该"变量"名相同的"全局变量a"

如图

我们再看一段代码

function foo(a){  num = a;  //num = 100}foo(100);

分析

上面的代码执行的LHS查询,在非严格模式下,JS引擎直到在全局作用域中都没有找到num这个变量,所以它就在全局作用域中声明了一个变量num 当然也对变量a进行一次RHS查询以获得变量a的值, 所以此时结果不会报错, 并且num也被赋值为100

我们也可以使用Chrome调试工具当中的Sources断点来查看全局作用域当中是否真的存在这个num变量,果不其然,我们在global中找到了这个num变量

如图

但是如果是严格模式下会ReferenceError的错误

代码如下:

"use strict";function test(){    a=100;}test();console.log(a);

如图

也就是说在严格模式下,LHS查询是无法帮助我们建立隐式全局变量

LHS与RHS的区别

其实我们在熟悉了LHS和RHS抛出的异常问题之后,就会明白它们彼此的区别在什么地方了!

RHS查询在所有嵌套作用域中找不到所需的变量或函数,引擎就会抛出ReferenceError异常

但是要注意的是,如RHS查询找到了一个变量,但是对这个变量的值进行不合理的操作, 例如: 使用null或者undefnied类型的属性,这种违规操作, JS引擎会抛出TypeError异常

LHS查询 相比之下,非严格模式的情况下 执行LHS查询时,如果在顶层作用域也无法找到目标变量,那么全局作用域会创建一个具有该名称的隐式全局变量,并将其返回给JS引擎, 当然如果是在严格模式下,LHS查询找不到目标变量时, 依旧会抛出ReferenceError异常

总结

所以区分LHS和RHS很重要的依据就是最终 查询在作用域链中找不到需要的变量函数 会抛出什么!

LHS和RHS都会在当前执行作用域中开始查询,当前没找到,就会根据作用域链上级作用域继续查找目标标识符

不成功的RHS会导致抛出ReferenceError异常

不成功的LHS自动隐式全局作用域中创建一个同名的全局变量 严格模式下也会抛出ReferenceError异常

作用域与LHS和RHS之间的关系

之前我们学过作用域JS引擎用来管理如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找的一套规则

如果查找的目的是对变量进行赋值,那么就会使用LHS查询

如果目的是获取变量的值,就会使用RHS查询

小提示:

要注意一点: 如果代码中引用了类似于foo.bar.baz,那么词法作用域查找只会试图查找foo标识符,找到这个变量后,再根据对象属性访问规则会分别接管, 并对barbaz属性的访问!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++入门基础(1)

因为6月中旬学校事情多,许久未更新,让我们继续学习吧! 目录 前言: 一、命名空间: 1、定义: 2、使用: 3、访问命名空间域: 二、C输入、输出函数: 1、输入函数: 2、输出…

vue3源码(六)渲染原理-runtime-core

1.依赖关系 runtime-dom 依赖于runtime-core,runtime-core 依赖于reactivity和sharedruntime-core提供跨平台的渲染方法createRenderer,用户可以自己传递节点渲染的渲染方法renderOptions,本身不关心用户使用什么APIruntime-dom提供了为浏览器而生的渲染…

关闭这八个电脑设置,保护个人隐私

你知道吗?电脑可能一直在偷窥你的小秘密。朋友们,一定要记得关闭这8个电脑设置哦,这样可以有效地保护我们的个人隐私。 按住键盘Windows键加i键,快速打开Windows设置。然后点击隐私选项。 我们来看基本的常规设置。里面有四个设置…

关于put提交不了参数的解决办法

html中form表单只支持GET与POST请求,而DELETE、PUT等method并不支持, 如图所示 参数请求改成RequestBody,用json格式传参即可解决问题

资料分析笔记整理

提升技巧多做题、少动笔、多分析 资料分析认识 国考一般20题(24~28分钟) 统计材料的类型包括单纯的文字、表格、图形以及由这些元素组成的复合类型材料 文字性材料:(30~60秒) 多段落型文字材料(时间、关键词、结构) 孤立段落文字材料(时间、关键词、标点[。;]) 表…

数据挖掘——matplotlib

matplotlib概述 Mat指的是Matlab,plot指的是画图,lib即library,顾名思义,matplotlib是python专门用于开发2D图表的第三方库,使用之前需要下载该库,使用pip命令即可下载。 pip install matplotlib1、matpl…

Idea使用EasyApi插件自动生成接口文档到Yapi

1.安装EasyApi插件 2.配置Yapi 设置-》EasyApi Yapi的Server 配置为Yari项目的地址 tokens:项目名Yapi项目里面的token:例如:test-project0e6cfb3c22c884a0fce108fffe554a20ca12341e421d7201233143ee440af36b mytest-portal0e6cfb3c22c884a…

关于Qt模型插入最后一行数据中存在未填满的项,点击导致崩溃的解决办法

在使用Qt模型视图框架的时候,你可能会遇见这种情况:给QTableView设置设置模型的时候,网模型里面插入数据,因为数据是一行一行插入的,即要使用model的appandRow函数,但有时候最后一行数据没有填满一行&#…

Temu是什么?Temu自养号测评有什么优势?

一、 Temu是什么?Temu是拼多多于海外线的跨境电商平台,“Temu”这个名字的含义也和拼多多的意思相近。Temu跨境电商自上线以来,下载量不断攀升,发展势头一片大好,击穿地板价的商品在欧美市场掀起了一阵狂潮&#xff0c…

html H5 dialog弹窗学习,实现弹窗显示内容 替代confirm、alert

html H5 dialog弹窗学习,实现弹窗内容 替代confirm 框架使用的mui,使用mui.confirm() 弹窗内容过多时,弹窗被撑的到屏幕外去了,使用H5 dialog 标签自定义一个固定大小的弹窗,内容过多时可下拉显示 效果展示 隐私政策内容很多,可以下拉显示 代码 myDialog.css dialog{p…

Java虚拟机面试题汇总

目录 1. JVM的主要组成部分及其作用? 1.1 运行时数据区划分? 1.2 哪些区域可能会发生OOM? 1.3 堆和栈的区别? 1.4 内存模型中的happen-before是什么? 2. HotSpot虚拟机对象创建流程? 2.1 类加载过程…

Xilinx zc706 USB电路解析

作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 USB OTG检测原理 USB3320 USB_ID为低电平时候,为host模式,USB_ID为悬空(高…

如何快速实现一个无缝轮播效果

🧑‍💻 写在开头 点赞 收藏 学会🤣🤣🤣 需求简介 轮播图是我们前端开发中的一个常见需求,在项目开发中,我们可以使用element、ant等UI库实现。某些场景,为了一个简单的功能安装一…

数据结构作业/2024/7/9

2>实现双向循环链表的创建、判空、尾插、遍历、尾删、销毁 fun.c #include "head.h" //1.双向循环链表的创建 doubleloop_ptr create_list() …

面经-计算机网络-数据结构-堆

1.什么是堆 堆是一种满足以下条件的树: 堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。或者说,任意一个节点的值都大于等于(或小于等于)所有子节点的值。 2.堆的用途 当我们只关心所有数…

Raylib 实现超大地图放大缩小与两种模式瓦片地图刷新

原理: 一种刷新模式: 在宫格内整体刷新,类似九宫格移动到边缘,则九宫格整体平移一个宫格,不过这里是移动一个瓦片像素,实际上就是全屏刷新,这个上限是 笔记本 3060 70帧 100*100个瓦片每帧都…

16:9横屏短视频素材库有哪些?横屏短视频素材网站分享

在这个视觉内容至关重要的时代,16:9横屏视频因其宽广的画面和优越的观赏体验,已经成为无数创作者和营销专家的首选格式。但要创造出吸引人的横屏视频,高质量的视频素材库是不可或缺的。不管你是资深视频制作人还是刚入行的新手,下…

香橙派编译linux内核支持ebpf和虚拟WIFI

前言 上一篇文章香橙派5plus上跑云手机方案一 redroid(带硬件加速)中说了怎么运行redroid,这篇补一下怎么修改参数编译内核。 补充 上篇文章有个内容需要补充一下:更新完内核需要用下面的命令防止内核被apt更新,不然后面使用apt update又回…

空间计量模型及 Stata 具体操作步骤

目录 一、引言 二、空间计量模型理论原理 空间自回归模型(SAR): 空间误差模型(SEM):, 空间杜宾模型(SDM): 三、实证模型构建 四、数据准备 五、Stata …

怎么将几首音乐合并在一起?这四种合并方法大家都在用!

怎么将几首音乐合并在一起?在音乐的海洋中遨游时,我们是否曾被音乐的海洋所淹没?在享受旋律的流转中,我们是否频繁地在不同的曲目间穿梭,仿佛迷失在无尽的音符之中?但音乐数量的繁多,不仅带来了…