Javascript中的作用域和上下文深入理解_javascript技巧
概述
Javascript中的作用域和上下文的實作是Javascript語言獨有的特性,從某種程度上來說,Javascript語言是十分靈活的。 Javascript中的函數可以採用各種各樣的上下文,作用域也可以被封裝和保存。正是由於這些特性,Javascript也提供了許多很有用的設計模式。然而,作用域和脈絡也是Javascript程式設計師在開發中經常迷惑的地方。
以下會向大家介紹Javascript中作用域和上下文的概念,以及它們的不同。
作用域 VS 上下文
首先要說明的很重要的一點是作用域和上下文並不是同一個概念,它們指涉的並不是同一個東西。作為一個前端的菜逼,常常會看到一些文章把這兩個概念弄混,結果有些東西越看越不明白。這兩個概念貌似被混淆了很長一段時間了。因此,查了很多資料,簡單說明下這兩個概念。 :stuck_out_tongue_closed_eyes:
在Javascript中,當函數被呼叫時都會有一個作用域和上下文和這個函數綁定在一起。從根本上來說,作用域是基於函數的而上下文是基於物件的。換句話說,作用域適用於函數被呼叫時函數中變數的存取權限。上下文通常是指「this」關鍵字的值,「this」是擁有目前執行程式碼的物件的參考。
變數作用域
變數可以定義在局域或全域作用域中,分別稱為局部變數和全域變數。全域變數是指在函數體外宣告的變量,在程式的任何地方都可以存取全域變數。局部變數是指在函數體內定義的變量,它僅可以在函數體內或嵌套的函數內被訪問,並且不能在函數外部被訪問。
Javascript目前並不支援區塊級作用域(在if、switch、for等語句中定義的變數)。這意味著在區塊內定義的變量,在區塊外也可以存取。但是,在ES6中,我們可以使用「let」關鍵字來定義區塊級作用域。
關於作用域的內容,大家可以查到別的資料,這部分內容相對簡單。
「this」上下文
上下文(context)通常取決於函數被呼叫的方式。當函數作為物件的方法被呼叫時,「this」指涉的是呼叫該函數的物件。
var obj={
foo:function (){
console.log(this === obj);
}
};
obj.foo(); //輸出true
同樣,當我們使用「new」關鍵字建立新物件時,this引用的是新建立的物件。
function foo(){
console.log(this);
}
foo(); //輸出window
var obj=new foo(); //輸出 foo {}
有一點要注意的是,當全域作用域中的函數被呼叫時,this引用的是全域對象,在瀏覽器環境中指的就是window。但是,如果在嚴格模式下運行程式碼時,「this」被設定為「undefined」
執行上下文(Execution Context)
Javascript是單執行緒的語言,這也就是說Javascript在瀏覽器中執行時,一次只能做一件事情,其他的事情將被方法佇列中,等待被處理。
1.當Javascript程式碼檔案被瀏覽器載入後,預設最新進入的是一個全域的執行上下文。當在全域上下文中呼叫函數時,程式留就進入該被呼叫函數內,此時Javascript引擎就會為該函數建立一個新的執行上下文,並且將其壓入到執行上下文堆疊的頂部。瀏覽器總是執行當前在堆疊頂部的上下文,一旦執行完畢,該上下文就會從堆疊頂部被彈出,然後,進入其下的上下文執行程式碼。這樣,堆疊中的上下文就會被依序執行並且彈出堆疊,直到回到全域的上下文。
2.一個執行上下文可以被分成兩個階段:建立階段和執行階段。在創建階段,javascript解釋器首先會創建一個變數物件(也成為“活動物件”,activation object)。活動物件由變量,函數宣告和參數組成。在這個階段,函數的作用域鏈被初始化,this引用的物件也被確定。接下來就是執行階段,在這個階段,程式碼被解釋並執行。
在Javascript程式碼中,可以有任意多個函數上下文,我們已經知道,當函數被呼叫時,Javascript解釋器就會建立一個新的上下文,同時會建立一個私有的作用域,函數內部宣告的任何變數都不能在目前函數作用域外部直接存取。
3.透過上面的解釋,我們對函數的「執行上下文」有了一個基本的概念,但是這裡也是大家最容易迷惑的一個地方。 Javascript中的「執行上下文」主要是指作用域,而不是上面第四小節所指的「this上下文」。類似的容易混淆的概念在Javascript中還有很多,但是我們只要弄清楚了每個概念所指代的具體對象,就不會再迷惑,因此,這裡也希望大家能夠真正的區分開“執行上下文”和“this上下文”。
簡單的一句話概括來說,執行上下文是與作用域相關的概念,雖然這樣說可能不太嚴謹。
作用域鏈
對每一個執行上下文來說,都有一個作用域連跟它綁在一起。作用域鏈包含了執行上下文堆疊中的執行上下文活動物件(activation object,聽起來有點繞口)。作用域鏈決定了變數的存取和識別碼的解析。
程式碼範例:
function first(){
second();
function second(){
third();
function third(){
fourth();
function fourth(){
//代碼
}
}
}
}
first();
執行上面的程式碼,巢狀的函數都會被執行。就上面的程式碼來說,也會形成一個作用域鏈,作用域鏈從頂部到底部的順序為:fourth, third, second, first, global。函數fourth可以存取全域作用域中的變量,並且可以存取函數third, second, first中定義的任何變數。
有一點要注意的是,在函數體內,局部變數的優先權高於同名的全域變數。如果在函數內宣告的局部變數或函數參數中帶有的變數和全域變數重名,那麼全域變數就被局部變數所覆蓋。
簡單來說,每次我們嘗試訪問一個變量時,程式都會在當前函數作用域內查找變量,如果找不到就沿著作用域鏈到該函數的上層去查找,直到找到該變量為止,如果找不到則回傳undefined。
總結
這篇文章介紹了javascript中上下文和作用域的相關概念,javascript中還有幾個比較重要的概念,如閉包等,這些在以後自己弄明白了會寫成文章~~

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題











typedef struct 在 C 語言中用於建立結構體類型別名,簡化結構體使用。它透過指定結構體別名將一個新的資料類型作為現有結構體的別名。優點包括增強可讀性、程式碼重複使用和類型檢查。注意:在使用別名前必須定義結構體,別名在程式中必須唯一且僅在其宣告的作用域內有效。

Java 中的變數期望值異常可以透過以下方法解決:初始化變數;使用預設值;使用 null 值;使用檢查和賦值;了解局部變數的作用域。

JavaScript 閉包的優點包括維持變數作用域、實作模組化程式碼、延遲執行和事件處理;缺點包括記憶體洩漏、增加了複雜性、效能開銷和作用域鏈影響。

C++ 中的 #include 預處理器指令將外部來源檔案的內容插入到目前原始檔案中,以複製其內容到目前原始檔案的相應位置。主要用於包含頭文件,這些頭文件包含程式碼中所需的聲明,例如 #include <iostream> 是包含標準輸入/輸出函數。

C++智慧指標的生命週期:建立:分配記憶體時建立智慧指標。所有權轉移:透過移動操作轉移所有權。釋放:智慧指標離開作用域或被明確釋放時釋放記憶體。物件銷毀:所指向物件被銷毀時,智慧型指標成為無效指標。

可以。 C++ 允許函數巢狀定義和呼叫。外部函數可定義內建函數,內部函數可在作用域內直接呼叫。巢狀函數增強了封裝性、可重複用性和作用域控制。但內部函數無法直接存取外部函數的局部變量,且傳回值類型需與外部函數宣告一致,內部函數不能自遞歸。

在 Vue 中,let 和 var 宣告變數時在作用域上存在差異:作用域:var 具有全域作用域,let 具有區塊級作用域。區塊級作用域:var 不會建立區塊級作用域,let 建立區塊級作用域。重新宣告:var 允許在同一作用域內重新宣告變數,let 不允許。

JavaScript 中,this 的指向類型有:1. 全域物件;2. 函數呼叫;3. 建構函式呼叫;4. 事件處理程序;5. 箭頭函數(繼承外層 this)。此外,可以使用 bind()、call() 和 apply() 方法明確設定 this 的指向。
