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屬性的訪問!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/43666.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/43666.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/43666.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C語言 printf函數緩沖機制

printf不立即打印到stdout的原因 printf函數使用了緩沖機制。當我們調用printf時,輸出通常不會立即顯示在屏幕上,而是先存儲在一個緩沖區中。這是為了提高I/O操作的效率。 緩存數據輸出的原理 stdio庫維護了一個緩沖區。當緩沖區滿了,或者在特定條件下,緩沖區的內容會被刷新…

前端如何讓網頁頁面完美適配不同大小和分辨率屏幕

推薦使用postcss插件,它會自動將項目所有的px單位統一轉換為vw等單位(包括npm安裝的第三方組件),從而實現適配,具體配置規則可參考官網或npm網站介紹。 另外對于大屏的適配,需要縮放網頁,可使用…

歐姆龍安全PLC及周邊產品要點指南

電氣安全、自動化設備作業安全,向來是非常非常之重要的!越來越多的客戶在規劃新產線、改造既有產線的過程中,明確要求設計方和施工方將安全考慮進整體方案中進行考慮和報價!作為一名自動化電氣工程師,尤其是高級工程師…

養寵經驗分享貓咪經常掉毛怎么辦?最值得買的寵物空氣凈化器分享

身為資深鏟屎官,深知若偷懶不打掃,家中便成貓毛紛飛、異味繚繞的戰場,尤其換季時,更是雪上加霜。長期處于這樣的環境,不僅我們頭疼眼澀、咳嗽氣喘,對老人、小孩、孕婦等敏感群體更是健康大敵。 幸運的是&a…

Vagrant配合VirtualBox搭建虛擬機

目錄 前言一、軟件下載及安裝1.下載2.安裝擴展: 二、創建一個虛擬機1.Vagrant官方鏡像倉庫 三、使用遠程工具連接虛擬機1.修改相關配置文件 四、虛擬機克隆及使用1.通用配置2.簡單搭建一個java環境3.克隆虛擬機1.重命名虛擬機(可選)2.打包指定…

靶場練習 手把手教你通關DC系列 DC1

DC1靶場通關教程 文章目錄 DC1靶場通關教程前言一、信息收集1.主機存活2.端口收集3.網頁信息收集4.目錄收集4.1 Nikto4.2 Dirb 信息收集總結 二、漏洞發現與利用1. 發現2. 利用 三、FlagFlag1Flag2Flag3Flag4Flag5(提權) 前言 本次使用的kali機的IP地址為192.168.243.131 DC1的…

機器學習 - 比較檢驗

列聯表 列聯表(Contingency Table)是一種用于顯示兩個或多個分類變量之間關系的表格。它廣泛應用于統計學中的分類數據分析,尤其在獨立性檢驗和關聯性分析時。列聯表的每個單元格展示了相應分類變量組合的頻數(或比例&#xff09…

【2024_CUMCM】LINGO入門+動態規劃

目錄 什么是動態規劃 怎么使用動態規劃? 例題:最短路線問題 2020b-問題一 穩定性分析 靈敏度分析 什么是動態規劃 基本想法:將原問題轉換為一系列相互聯系的子問題,然后通過逐層遞推求得最后的解 基本思想:解決…

X12端口配置指南:ISA ID、測試指示符與997

通過知行之橋EDI系統實現X12 & 標準XML之間的格式轉換時,需要完善交換頭ISA ID及其限定符、測試標識符以及997的相關配置。 在X12文件中有兩組EDI ID對,分別是發送方 ID 限定符 及發送方ID ,接收方 ID 限定符及接收方ID。 比如&#xf…

STM32Cubemx配置生成 Keil AC6支持代碼

文章目錄 一、前言二、AC 6配置2.1 ARM ComPiler 選擇AC62.2 AC6 UTF-8的編譯命令會報錯 三、STM32Cubemx 配置3.1 找到stm32cubemx的模板位置3.2 替換文件內核文件3.3 修改 cmsis_os.c文件3.4 修改本地 四、編譯對比 一、前言 使用keil ARM compiler V5的時候,編譯…

RK3568 buildroot 使用dropbear實現ssh遠程的方法

RK3568 buildroot 使用dropbear實現ssh遠程的方法 文章目錄 RK3568 buildroot 使用dropbear實現ssh遠程的方法前言一、創建S99dropbear.sh腳本二、創建sshd_config三、添加root賬戶密碼到系統驗證登錄前言 rk3568 linux 的sdk中,buildroot已經集成了dropbear的所需的lib庫環境…

交替打印-GO

1 兩個channel 版本 package mainimport ("fmt""sync")var wg sync.WaitGroup var c1 chan int var c2 chan intfunc A(){defer wg.Done()for i:0;i<10;i {<-c1fmt.Println(2*i)c2<-1 //牽引協程} } func B(){defer wg.Done()for i:0…

Java內存區域與內存溢出異常(自動內存管理)

序言&#xff1a;Java與C之間有一堵由內存動態分配和垃圾收集技術所圍成的高墻&#xff0c;墻外面的人想進去&#xff0c;墻里面的人卻想出來。 1.1概述 對于從事C、C程序開發的開發人員來說&#xff0c;在內存管理領域&#xff0c;他們既是擁有最高權力的“皇帝”&#xff0c…

使用OpenCV在按下Enter鍵時截圖并保存到指定文件夾

使用OpenCV在按下Enter鍵時截圖并保存到指定文件夾 在這篇博客中&#xff0c;我們將介紹如何使用OpenCV庫來實現一個簡單的功能&#xff1a;在按下Enter鍵時從攝像頭截圖并保存到指定的文件夾中。這個功能可以用于各種應用&#xff0c;例如監控系統、視頻捕捉等。 前置條件 …

在FPGA程序中Handshake(握手)和Register(寄存器)區別

在FPGA程序中&#xff0c;Handshake&#xff08;握手&#xff09;和Register&#xff08;寄存器&#xff09;是兩種不同的通信和數據傳輸機制。它們各有特點和適用場景。以下是它們的區別和應用場景的詳細解釋&#xff1a; Register&#xff08;寄存器&#xff09; 特點&#…

SQLServer用戶們,你們攤上大事了!

最近一段時間&#xff0c;我們經常會收到了許多用戶的咨詢&#xff0c;問我們何時能納管SQLServer&#xff1f;耐不住小伙伴們的猛烈催促及熱切期待&#xff0c;本不想納管SQLServer的研發團隊也抓緊將這項需求提上日程。并在DBdoctor v3.2.2版本中成功實現了對SQLServer的納管…

班級錄取查詢系統如何制作

在教育的長河中&#xff0c;我們每位老師都曾面臨過這樣一個問題&#xff1a;如何高效、準確地完成班級錄取查詢的任務&#xff1f;記得在以往&#xff0c;每當新學期伊始&#xff0c;我們不得不手忙腳亂地整理學生名單&#xff0c;然后逐一通知他們所在的班級。這個過程不僅耗…

谷歌Google Ads新賬號推廣方案

第一階段重點 推廣地區優化&#xff1a;分析投放國家的數據&#xff0c;剔除高花費低轉化的國家&#xff0c;將預算重新分配給高性價比的國家&#xff0c;從而降低詢盤成本并增加詢盤數量。關鍵詞優化&#xff1a;識別并暫停或降價高成本低回報的關鍵詞&#xff0c;減少詢盤成本…

《mysql篇》--索引事務

索引 索引的介紹 索引是幫助MySQL高效獲取數據的數據結構&#xff0c;是一種特殊的文件&#xff0c;包含著對數據表里所有記錄的引用指針&#xff0c;因為索引本身也比較大&#xff0c;所以索引一般是存儲在磁盤上的&#xff0c;索引的種類有很多&#xff0c;不過如果沒有特殊…

[ios-h5]在ios系統瀏覽器中輸入框得到焦點時頁面自動放大

問題&#xff1a; 在ios系統瀏覽器中輸入框得到焦點時頁面自動放大。 解決&#xff1a; 添加meta標簽。 <meta name"apple-mobile-web-app-capable" content"yes" /> <meta name"viewport" content"widthdevice-width, initial-…