摘要:如果是,編譯器會忽略該聲明,繼續進行編譯否則它會要求作用域在當前作用域的集合中聲明一個新的變量接下來編譯器會為引擎生成運行時所需的代碼,這些代碼被用來處理這個賦值操作。引擎運行時會首先詢問作用域,在當前的作用域集合中是否存在一個叫做的變量。
引言
幾乎所有的編程語言都有作用域的概念,那作用域到底指的是什么呢?作用域就是編程語言在定義變量時,變量如何存儲、變量如何訪問的一套規則,不同的編程語言的規則大同小異,接下來就來看看這套規則是怎么設定的
編譯原理在傳統編譯語言中,在代碼執行之前都會有一個編譯過程:
分詞/詞法分析:將代碼語句分解成有意義的代碼塊,又叫詞法單元。
解析/語法分析:將詞法單元轉換一個逐級嵌套的具有語法規則的樹狀結構,又叫抽象語法樹(AST)
代碼生成:解析AST并轉化成機器指令
和傳統編譯語言不太一樣,js的編譯和執行并不是分開執行,大多數情況都是編譯過程結束就會立刻執行,為了在短時間的編譯過程內達到較優性能,js引擎較一般編譯器更復雜,現在就讓來看js的編譯過程,簡單的以編譯var a = 2為例:
遇到var a,編譯器會詢問作用域是否已經有一個該名稱的變量存在于同一個作用域的集合中。如果是,編譯器會忽略該聲明,繼續進行編譯;否則它會要求作用域在當前作用域的集合中聲明一個新的變量a
接下來編譯器會為引擎生成運行時所需的代碼,這些代碼被用來處理a=2這個賦值操作。引擎運行時會首先詢問作用域,在當前的作用域集合中是否存在一個叫做a的變量。如果否,引擎就會使用這個變量;如果引擎最終找到了a,就會將2賦值給它。否則引擎就會拋出一個異常
詞法作用域作用域一般有兩種工作模型,第一種是被大多數編程語言所采用的詞法作用域,另外一種叫作動態作用域,如Bash腳本采用的就是動態作用域。詞法作用域就是定義在詞法階段的作用域,詞法作用域是由你在寫代碼時將變量和塊作用域寫在哪里來決定的,由變量定義位置決定,而動態作用域則是由變量使用的位置來決定的。下面來看個例子:
function foo(a) { var b = a * 2; function bar(c) { console.log(a, b, c) } bar(b * 3) } foo(2)
首先來分析一下這里一共存在幾個作用域?
全局作用域,里面存在foo變量
foo函數創建的作用域,里面有a,b,bar變量
bar函數創建的作用域,里面有c變量
接下來再來分析一下變量的查找過程,引擎執行console.log()需要查找a、b、c三個變量的引用,首先從最里面的bar()作用域開始找,引擎無法找到a,因此會再往上到foo()作用域中找,在這里找到了a,停止查找,對于b、c來說查找過程一樣。作用域查找始終從運行時最內層開始查找,逐級向外查找,直到遇見第一個匹配的變量為止。
函數作用域函數作用域指的是屬于這個函數的全部變量都可以在整個函數的范圍內使用及復用,這是大家都知道的定義,但是函數作用域的存在到底有什么用呢?接下來就一起看看函數作用域的秒用。
隱藏內部實現隱藏內部實現就是將變量和函數包裹在一個函數的作用域中,達到隱藏的目的,為什么要這么做呢?軟件設計中有一個非常有名的原則叫最小暴露原則,指最小限度暴露必要內容,而將其他內容都隱藏起來,比如模塊或對象的API設計。用函數作用域來包裹變量和函數來達到最小暴露原則,阻止外部直接訪問,來看下面的例子:
function?doSomething(a)?{ ??b?=?a?+?doSomethingElse(?a?*?2?); ??console.log(?b?*?3?); } function?doSomethingElse(a)?{ ? return?a?-?1; } var?b; doSomething(?2?);?//?15”
在這段代碼中doSomethingElse和b應該是doSomething內部私有的,但是卻被暴露出來,這樣會導致以預期之外的形式被使用,產生意料之外的結果,更合理的設計應該是將這些私有的內容隱藏在doSomething內部,例如:
function?doSomething(a)?{ function?doSomethingElse(a)?{ ?? return?a?-?1; } ??var?b; ??b?=?a?+?doSomethingElse(?a?*?2?); ??console.log(?b?*?3?); } doSomething(?2?);
這樣b和doSomethingElse都無法從doSomething外部訪問,但是這樣也會存在一些問題,首先在全局作用域中聲明了doSomething函數,污染了全局作用,其次,必須通過顯示調用才能執行,那么有沒有什么辦法既不會污染作用域也不需要調用就可以自執行呢?答案就是函數表達式,看下面的例子:
(function?doSomething(a)?{ function?doSomethingElse(a)?{ ?? return?a?-?1; } ??var?b; ??b?=?a?+?doSomethingElse(?a?*?2?); ??console.log(?b?*?3?); })(2)
首先來看(function doSomething(){})這是一個函數表達式,和函數聲明不同的是用括號包起來的,然后再(function doSomething(a){})()調用傳值,這樣既能自執行也不會污染作用域,社區給這種用法定義了一個術語:IIFE,代表立即執行函數表達式
塊作用域除JavaScript外很多編程語言都支持塊作用域,盡管你可能寫過很偽塊作用域形式的代碼,最常見的就是for循環:
for(var i=0; i<10; i++) { console.log(i) }
寫這段代碼通常是希望變量i在循環內部使用,但是實際上i會被綁定到外部作用域中,要確保沒有在作用域的其他地方意外使用i,就只能依靠自覺,這時候塊級作用域就顯得尤為有用,ES6改變了現狀,引入了新的let、const關鍵字,let關鍵字可以將變量綁定到所在的任意作用域中,也就是let為其聲明的變量隱式地創建了作用域:
for(let i=0; i<10; i++) { console.log(i) } console.log(i) // ReferenceError
這時候i就只會在for循環的內部有效
總結這篇文章主要介紹了JS作用域相關的內容。如果有錯誤或不嚴謹的地方,歡迎批評指正,如果喜歡,歡迎點贊。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/109081.html
摘要:一作用域域表示的就是范圍,即作用域,就是一個名字在什么地方可以使用,什么時候不能使用。概括的說作用域就是一套設計良好的規則來存儲變量,并且之后可以方便地找到這些變量。 一、作用域 域表示的就是范圍,即作用域,就是一個名字在什么地方可以使用,什么時候不能使用。想了解更多關于作用域的問題推薦閱讀《你不知道的JavaScript上卷》第一章(或第一部分),從編譯原理的角度說明什么是作用域。概...
摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進擊之詞法作用域與作用域鏈什么是閉包閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。在中函數構成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個具有封閉功能與包裹功能的結構。所謂的閉包就是...
摘要:每一個由構造函數創建的對象都會默認的連接到該神秘對象上。在構造方法中也具有類似的功能,因此也稱其為類實例與對象實例一般是指某一個構造函數創建出來的對象,我們稱為構造函數的實例實例就是對象。表示該原型是與什么構造函數聯系起來的。 本文您將看到以下內容: 傳統構造函數的問題 一些相關概念 認識原型 構造、原型、實例三角結構圖 對象的原型鏈 函數的構造函數Function 一句話說明什么...
摘要:中沒有可執行的函數了,執行完出棧。當某個函數被調用時,會創建一個執行環境及相應的作用域鏈。檢查當前環境中的函數聲明使用聲明的。確定指向所以說的指向,是在函數執行時確定的。 理解js 的執行過程是很重要的,比如,作用域,作用域鏈,變量提升,閉包啊,要想明白這些,你就得搞懂函數執行時到底發生了什么! 一、執行環境(Execution Context)又稱執行上下文 當代碼執行時都會產生一個...
閱讀 2096·2021-11-23 09:51
閱讀 2839·2021-11-22 15:35
閱讀 2937·2019-08-30 15:53
閱讀 1037·2019-08-30 14:04
閱讀 3276·2019-08-29 12:39
閱讀 1802·2019-08-28 17:57
閱讀 1086·2019-08-26 13:39
閱讀 551·2019-08-26 13:34