摘要:一旦到達(dá)頂層全局作用域,可能找到,也可能沒(méi)有找到,查找過(guò)程都必須停止。當(dāng)引擎執(zhí)行查詢(xún)時(shí),如果查詢(xún)?cè)谒星短椎淖饔糜蛑斜閷げ坏剿璧淖兞?,引擎就?huì)拋出異常。代表作用域判別失敗相關(guān),而則代表作用域判別成功了,但是對(duì)結(jié)果的操作是非法或不合理的。
什么是作用域
對(duì)于幾乎所有編程語(yǔ)言,最基本的功能之一就是能夠存儲(chǔ)變量的值,并且能在之后對(duì)這個(gè)值進(jìn)行訪(fǎng)問(wèn)和修改。這樣就會(huì)帶來(lái)幾個(gè)問(wèn)題,這些變量存儲(chǔ)在哪里?程序在需要的時(shí)候又是如何找到它們的?要解決這些問(wèn)題,就需要引入一套規(guī)則來(lái)存儲(chǔ)變量和訪(fǎng)問(wèn)變量,這套規(guī)則就是作用域。
理解作用域在剛開(kāi)始接觸 JavaScript 這門(mén)語(yǔ)言時(shí),肯定會(huì)經(jīng)常接觸到?JavaScript 是動(dòng)態(tài)語(yǔ)言,?是解釋執(zhí)行的,但事實(shí)上?JavaScript 是一門(mén)編譯語(yǔ)言。只不過(guò)和 Java、C# 這些傳統(tǒng)意義上的編譯語(yǔ)言不同,JavaScript 的編譯過(guò)程不是發(fā)生在構(gòu)建之前的。大部分情況下,JavaScript 的編譯過(guò)程發(fā)生在代碼執(zhí)行前的很短時(shí)間內(nèi)。也就是說(shuō)?JavaScript 代碼在執(zhí)行前都要進(jìn)行編譯。
為了更好地理解作用域,我們需要明確下面幾個(gè)概念
引擎
從頭到尾負(fù)責(zé)整個(gè) JavaScript 程序的編譯及執(zhí)行過(guò)程
編譯器
負(fù)責(zé)語(yǔ)法分析及代碼生成等臟活累活
作用域
負(fù)責(zé)收集并維護(hù)由所有聲明的標(biāo)識(shí)符(變量) 組成的一系列查詢(xún), 并實(shí)施一套非常嚴(yán)格的規(guī)則, 確定當(dāng)前執(zhí)行的代碼對(duì)這些標(biāo)識(shí)符的訪(fǎng)問(wèn)權(quán)限。
下面我們從引擎、編譯器和作用域的角度,分析 var a = 2?這條聲明語(yǔ)句,看看它們是如何協(xié)同完成工作的
遇到 var a, 編譯器會(huì)詢(xún)問(wèn)作用域是否已經(jīng)有一個(gè)該名稱(chēng)的變量存在于同一個(gè)作用域的集合中。 如果是, 編譯器會(huì)忽略該聲明, 繼續(xù)進(jìn)行編譯; 否則它會(huì)要求作用域在當(dāng)前作用域的集合中聲明一個(gè)新的變量, 并命名為a。
接下來(lái)編譯器會(huì)為引擎生成運(yùn)行時(shí)所需的代碼, 這些代碼被用來(lái)處理 a = 2 這個(gè)賦值操作。 引擎運(yùn)行時(shí)會(huì)首先詢(xún)問(wèn)作用域, 在當(dāng)前的作用域集合中是否存在一個(gè)叫作 a 的變量。 如果是, 引擎就會(huì)使用這個(gè)變量; 如果否, 引擎會(huì)繼續(xù)查找該變量。如果引擎最終找到了 a 變量, 就會(huì)將 2 賦值給它。 否則引擎就會(huì)舉手示意并拋出一個(gè)異常!
簡(jiǎn)單來(lái)說(shuō),變量的賦值操作會(huì)執(zhí)行兩個(gè)動(dòng)作, 首先編譯器會(huì)在當(dāng)前作用域中聲明一個(gè)變量(如果之前沒(méi)有聲明過(guò)), 然后在運(yùn)行時(shí)引擎會(huì)在作用域中查找該變量, 如果能夠找到就會(huì)對(duì)它賦值,否則就會(huì)并拋出一個(gè)異常。
作用域嵌套我們知道引擎查找變量的過(guò)程在作用域中進(jìn)行的,而這個(gè)過(guò)程通常會(huì)涉及多個(gè)作用域。
當(dāng)一個(gè)塊或函數(shù)嵌套在另一個(gè)塊或函數(shù)中時(shí), 就發(fā)生了作用域的嵌套。 因此, 在當(dāng)前作用域中無(wú)法找到某個(gè)變量時(shí), 引擎就會(huì)在外層嵌套的作用域中繼續(xù)查找, 直到找到該變量,或抵達(dá)最外層的作用域(也就是全局作用域) 為止。
為了便于理解,可以將作用域嵌套比喻成一棟高樓,我們從一樓(當(dāng)前作用域)開(kāi)始查找,如果沒(méi)有找到,就會(huì)前往上一個(gè)樓層繼續(xù)查找,以此類(lèi)推。一旦到達(dá)頂層(全局作用域),可能找到,也可能沒(méi)有找到,查找過(guò)程都必須停止。
繼續(xù)上文的示例,引擎在執(zhí)行編譯器生成的代碼時(shí),會(huì)通過(guò)查找變量 a 來(lái)判斷它是否已經(jīng)聲明過(guò)。查找的過(guò)程由作用域進(jìn)行協(xié)助, 但是引擎執(zhí)行怎樣的查找, 會(huì)影響最終的查找結(jié)果。查找過(guò)程分為兩類(lèi):LHS查詢(xún)和RHS查詢(xún)。其實(shí)很簡(jiǎn)單,當(dāng)變量出現(xiàn)在賦值操作的左側(cè)時(shí)進(jìn)行 LHS 查詢(xún), 出現(xiàn)在右側(cè)時(shí)進(jìn)行 RHS 查詢(xún)。
更準(zhǔn)確一點(diǎn)的講,?RHS?查詢(xún)是查找某個(gè)變量的值,而?LHS?查詢(xún)是查找變量的容器本身,從而可以對(duì)其賦值。如下面的示例:
var a = 2; // a: LHS查詢(xún) var b = 3; // b: LHS查詢(xún) a = b; //a: LHS查詢(xún) b:RHS查詢(xún)
為什么區(qū)分 LHS 和 RHS 是一件重要的事情?
因?yàn)樵谧兞窟€沒(méi)有聲明(在任何作用域中都無(wú)法找到該變量) 的情況下, ?LHS?和?RHS兩種查詢(xún)的行為是不一樣的。
1.當(dāng)引擎執(zhí)行?RHS?查詢(xún)時(shí),如果 RHS 查詢(xún)?cè)谒星短椎淖饔糜蛑斜閷げ坏剿璧淖兞浚?引擎就會(huì)拋出 ReferenceError異常。?
console.log(a); //ReferenceError: a is not defined
2.當(dāng)引擎執(zhí)行 LHS 查詢(xún)時(shí), 如果在頂層(全局作用域) 中也無(wú)法找到目標(biāo)變量,全局作用域中就會(huì)創(chuàng)建一個(gè)具有該名稱(chēng)的變量, 并將其返還給引擎, 前提是程序運(yùn)行在非“嚴(yán)格模式” 下。如果在“嚴(yán)格模式”下,引擎也會(huì)拋出?ReferenceError異常。
//非嚴(yán)格模式 var a =2; b = a; console.log(b); //2
//嚴(yán)格模式 "use strict"; var a =2; b = a; console.log(b); //ReferenceError: b is not defined
另外,如果 RHS 查詢(xún)找到了一個(gè)變量, 但是你嘗試對(duì)這個(gè)變量的值進(jìn)行不合理的操作,比如試圖對(duì)一個(gè)非函數(shù)類(lèi)型的值進(jìn)行函數(shù)調(diào)用, 或著引用 null 或 undefined 類(lèi)型的值中的屬性, 那么引擎會(huì)拋出另外一種類(lèi)型的異常, 叫作TypeError。
ReferenceError 代表作用域判別失敗相關(guān), 而 TypeError 則代表作用域判別成功了, 但是對(duì)結(jié)果的操作是非法或不合理的。
《你不知道的JavaScript》
微信公眾號(hào):
聲明:本文為博主學(xué)習(xí)感悟總結(jié),水平有限,如果不當(dāng),歡迎指正。轉(zhuǎn)載與引用請(qǐng)注明作者及出處。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/104559.html
摘要:前言官方文檔地址中文文檔地址是一個(gè)的第三方庫(kù),是的優(yōu)秀實(shí)踐。初次了解是在讀林昊翻譯的設(shè)計(jì)模式與最佳實(shí)踐一書(shū)時(shí)。能力所限,已翻譯部分可能仍有字詞錯(cuò)誤或語(yǔ)句不通順的地方,歡迎有能力的同學(xué)幫助糾正。就是其中的佼佼者。 前言 官方文檔地址: https://www.styled-components.com/ 中文文檔地址:https://github.com/hengg/styled-com...
摘要:前言官方文檔地址中文文檔地址是一個(gè)的第三方庫(kù),是的優(yōu)秀實(shí)踐。初次了解是在讀林昊翻譯的設(shè)計(jì)模式與最佳實(shí)踐一書(shū)時(shí)。能力所限,已翻譯部分可能仍有字詞錯(cuò)誤或語(yǔ)句不通順的地方,歡迎有能力的同學(xué)幫助糾正。就是其中的佼佼者。 前言 官方文檔地址: https://www.styled-components.com/ 中文文檔地址:https://github.com/hengg/styled-com...
摘要:我們先來(lái)看一看的官方定義展開(kāi)語(yǔ)法可以在函數(shù)調(diào)用數(shù)組構(gòu)造時(shí)將數(shù)組表達(dá)式或者在語(yǔ)法層面展開(kāi)還可以在構(gòu)造字面量對(duì)象時(shí)將對(duì)象表達(dá)式按的方式展開(kāi)。 我們先來(lái)看一看MDN的官方定義 展開(kāi)語(yǔ)法(Spread syntax), 可以在函數(shù)調(diào)用/數(shù)組構(gòu)造時(shí), 將數(shù)組表達(dá)式或者string在語(yǔ)法層面展開(kāi);還可以在構(gòu)造字面量對(duì)象時(shí), 將對(duì)象表達(dá)式按key-value的方式展開(kāi)。(譯者注: 字面量一般指 [1...
摘要:指北詳談解構(gòu)賦值附贈(zèng)練習(xí)題一何謂解構(gòu)賦值基本概念首先我們看一下給的定義解構(gòu)賦值語(yǔ)法是一個(gè)表達(dá)式,這使得可以將值從數(shù)組或?qū)傩詮膶?duì)象提取到不同的變量中從定義中,我們可以發(fā)現(xiàn)解構(gòu)賦值的作用是對(duì)變量進(jìn)行賦值主要通過(guò)兩個(gè)方面實(shí)現(xiàn)這個(gè)作用數(shù)組將數(shù)組中的 ES6指北【6】——詳談解構(gòu)賦值【附贈(zèng)練習(xí)題】 一、何謂解構(gòu)賦值? 1. 基本概念 首先我們看一下MDN給的定義 解構(gòu)賦值語(yǔ)法是一個(gè) Javasc...
摘要:箭頭函數(shù)基本語(yǔ)法函數(shù)語(yǔ)法具名函數(shù)匿名函數(shù)三句話(huà)第一句話(huà)聲明第二句話(huà)聲明匿名函數(shù)第三句話(huà)把匿名函數(shù)賦值給箭頭函數(shù)語(yǔ)法特點(diǎn)只能做賦值,不能做聲明第一種寫(xiě)法完全寫(xiě)法不省略參數(shù)個(gè)數(shù),不省略函數(shù)體花括號(hào)參數(shù)個(gè)數(shù)函數(shù)體內(nèi)語(yǔ)句個(gè)數(shù)第二種寫(xiě)法省略參數(shù)括號(hào)參 1.箭頭函數(shù)基本語(yǔ)法 1.1 ES3 函數(shù)語(yǔ)法 // 具名函數(shù) function xxx(arg1, arg2) { console.lo...
閱讀 2816·2021-11-22 13:52
閱讀 1289·2021-10-14 09:43
閱讀 3727·2019-08-30 15:56
閱讀 3026·2019-08-30 13:22
閱讀 3357·2019-08-30 13:10
閱讀 1636·2019-08-26 13:45
閱讀 1169·2019-08-26 11:47
閱讀 2876·2019-08-23 18:13