当前位置: 首页 > news >正文

JavaScript中的LHS和RHS

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属性的访问!

相关文章:

JavaScript中的LHS和RHS

LHS和RHS之前我们先来回忆一下最简单的赋值操作! var test100; console.log(test); 以上代码的意思简单我们理解为把右边的值赋值给左边的test变量,然后输出打印结果。 可是我们要是深入理解你就会发现在这个过程当中,还发生了一些其他的事情 而这些事情就是今天…...

appium 实战问题 播放视频时无法定位到元素

背景 在做UI自动化时,有播放详情页的用例,但是发现视频在播放的时候无法定位到元素或者很慢,了解到appium在动态的页面实时获取布局元素导致定位变慢。所以只能将视频暂停在操作元素,点击到暂停按钮又是个问题,通过ad…...

鸿蒙‘ohpm‘ 不是内部或外部命令,也不是可运行的程序-解决方案

🔥 博客主页: 小韩本韩! ❤️ 感谢大家点赞👍收藏⭐评论✍️ 在鸿蒙的DevEco Studio的终端下输入 ohpm -v 或者 你需要下载第三方ohpm包的时候提示‘ohpm‘ 不是内部或外部命令,也不是可运行的程序- 主要是因为我们…...

方法引用 异常 file

目录 一.方法引用 1.方法引用概述 2.引用静态方法 3.引用成员方法 i.引用其他成员方法 ii.引用本类成员方法 iii.引用父类成员方法 4.引用构造方法 5.其他调用方式 i.使用类名引用成员方法 ii.引用数组的构造方法 二、异常 1.异常的作用 2.异常的处理方式 i.JVM…...

比较(六)利用python绘制径向柱图

比较(六)利用python绘制径向柱图 径向柱图(Circular Barplot)简介 径向柱图基于同心圆网格来绘制条形图,虽然不如普通条形图表达准确,但却有抓人眼球的效果。其衍生的南丁格尔玫瑰图则广为人知。 快速绘制…...

为什么需要重写equals和如何重写equals

首先先看Java中的 ,比较的两个对象的地址值。 如果是基本数据类型,那么就是比较的是值。 如果是引用数据类型,比较的就是地址. object类中的equals方法也是用的; 所以要比较两个对象的大小,去调用默认的equals方法…...

C#字符串操作:判断一个字符串是否存在于另一个字符串按特定字符分割后的子字符串中的几种方法

要判断一个字符串是否存在于另一个字符串按特定字符分割后的子字符串中,可以使用以下几种方法: 方法一:使用Split和Array.Exists 你可以使用 Split 方法将字符串分割成子字符串数组,然后使用 Exists方法检查目标字符串是否在数组…...

Hi3861 OpenHarmony嵌入式应用入门--MQTT

MQTT 是机器对机器(M2M)/物联网(IoT)连接协议。它被设计为一个极其轻量级的发布/订阅消息传输 协议。对于需要较小代码占用空间和/或网络带宽非常宝贵的远程连接非常有用,是专为受限设备和低带宽、 高延迟或不可靠的网络而设计。这些原则也使该协议成为新兴的“机器…...

[22] Opencv_CUDA应用之 使用背景相减法进行对象跟踪

Opencv_CUDA应用之 使用背景相减法进行对象跟踪 背景相减法是在一系列视频帧中将前景对象从背景中分离出来的过程,它广泛应用于对象检测和跟踪应用中去除背景 背景相减法分四步进行:图像预处理 -> 背景建模 -> 检测前景 -> 数据验证 预处理去除噪声背景建模,以便与…...

Maven在Windows中的配置方法

本文介绍在Windows电脑中,下载、配置Maven工具的详细方法。 Maven是一个广泛使用的项目管理工具,主要针对Java项目,但也可以用于其他类型的项目;其由Apache软件基金会维护,旨在简化和标准化项目构建过程,依…...

一、redis-万字长文读懂redis

高性能分布式缓存Redis `第一篇章`1.1缓存发展史&缓存分类1.1.1 大型网站中缓存的使用带来的问题1.1.2 常见缓存的分类及对比与memcache对比1.2 数据类型选择&应用场景1.2.1 string1.2.2 hash1.2.3 链表1.2.4 set1.2.5 sortedset有序集合类型1.2.6 总结1.3 Redis高级应…...

搞清楚[继承],易如反掌

穷不失义,达不离道。——孔丘《论语》 继承 1、简单理解2、继承2、1、继承的概念2、2、继承定义2、3、基类和派生类对象赋值转换2、4、继承中的作用域2、5、派生类默认成员函数2、6、继承中的特点2、6、1、友元2、6、2、静态成员2、6、3、菱形继承及菱形虚拟继承 3、…...

Perl 语言入门学习指南:探索高效脚本编程的奥秘

引言 Perl,全称Practical Extraction and Report Language,是一种功能强大的编程语言,特别擅长于文本处理、报告生成以及系统自动化管理任务。自1987年诞生以来,Perl凭借其灵活性、强大的内置功能库和广泛的社区支持,…...

【HTML】-解决页面内容无法选择、复制问题

目录 1、网页内容无法选中 1.1、问题原因 1.2、解决脚本 1.2.1、开启控制台窗口 1.2.2、执行脚本命令 2、内容复制弹出阻止框 2.2、解决脚本 1、网页内容无法选中 1.1、问题原因 今天在访问某一网站平台,需要将内容进行选择、复制时发现不可使用。 在使用…...

C#中委托与事件

一、委托 在面向对象中,我们可以将任何数据类型作为参数传递给方法,能否将一个方法作为参数传递给另一个方法?C#中通过委托可以实现将方法作为参数进行传递。 1.1概念 委托是一种引用类型,它可以用于封装并传递方法作为参数。委…...

通用后台管理(二)——项目搭建

目录 前言 一、安装vue-cli依赖 1、使用yarn下载vue-cli 2、使用npm下载 3、检查一下是否下载成功 二、创建项目 1、创建项目,my-app是项目名称 2、 这里选择vue 2,蓝色表示选中的。 3、启动项目 三、下载项目依赖 四、配置项目 1、修改esli…...

多模态大模型之达摩院通义MPLUG

引言 随着人工智能技术的飞速发展,多模态技术逐渐成为研究的热点。它结合了文本、图像、声音等多种数据类型,为机器理解世界提供了更丰富的视角。本文根据严明老师的达摩院通义MPLUG多模态预训练技术分享,及其在电商等行业的应用实践&#x…...

文章翻译记录

以 PINN 为基础,我们开发了一个框架,用于在不同震源位置和速度模型下进行地震建模。本研究的显著贡献包括: 1. 为了提高网络对不同速度模型的泛化能力,必须将速度变量 vp 作为系统的输入参数。本研究从监督学习中汲取灵感&#xf…...

C++ 语法习题(2)

第三讲 循环语句 1.偶数 编写一个程序&#xff0c;输出 1 到 100之间&#xff08;包括 1 和 100&#xff09;的全部偶数。 输入格式 无输入。 输出格式 输出全部偶数&#xff0c;每个偶数占一行。 输入样例 No input输出样例 2 4 6 ... 100 参考代码: #include <i…...

使用Gstreamer时遇到WARNING: erroneous pipeline: no element “x264enc“(亲测有效)

WARNING: erroneous pipeline: no element “x264enc” 解决&#xff1a; 我下了gstreamer1.0-plugins-ugly包就解决了 sudo apt install -y gstreamer1.0-plugins-ugly...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

postgresql|数据库|只读用户的创建和删除(备忘)

CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)

【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

脑机新手指南(七):OpenBCI_GUI:从环境搭建到数据可视化(上)

一、OpenBCI_GUI 项目概述 &#xff08;一&#xff09;项目背景与目标 OpenBCI 是一个开源的脑电信号采集硬件平台&#xff0c;其配套的 OpenBCI_GUI 则是专为该硬件设计的图形化界面工具。对于研究人员、开发者和学生而言&#xff0c;首次接触 OpenBCI 设备时&#xff0c;往…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

ubuntu22.04 安装docker 和docker-compose

首先你要确保没有docker环境或者使用命令删掉docker sudo apt-get remove docker docker-engine docker.io containerd runc安装docker 更新软件环境 sudo apt update sudo apt upgrade下载docker依赖和GPG 密钥 # 依赖 apt-get install ca-certificates curl gnupg lsb-rel…...

归并排序:分治思想的高效排序

目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法&#xff0c;由约翰冯诺伊曼在1945年提出。其核心思想包括&#xff1a; 分割(Divide)&#xff1a;将待排序数组递归地分成两个子…...