作用域和作用域鏈的相關知識

来源:https://www.cnblogs.com/feixianxing/archive/2023/09/19/scope-chain.html
-Advertisement-
Play Games

作用域(scope)規定了變數能夠被訪問的“範圍”,離開了這個“範圍”變數便不能被訪問。作用域分為:局部作用域和全局作用域。 ...


作用域

作用域(scope)規定了變數能夠被訪問的“範圍”,離開了這個“範圍”變數便不能被訪問。
作用域分為:

  • 局部作用域
  • 全局作用域

局部作用域

局部作用域分為函數作用域和塊作用域。

函數作用域

在函數內部聲明的變數只能在函數內被訪問,外部無法直接訪問。

function foo(){
    const bar = 1;
}

console.log(bar); // ReferenceError: bar is not defined

總結

  • 函數內部聲明的變數,在函數外部無法被訪問;
  • 函數的參數也是函數內部的局部變數;
  • 不同函數內部聲明的變數無法互相訪問;
  • 函數執行完畢後,函數內部的變數實際被清空了。

塊作用域

在JavaScript中使用{}包裹的代碼稱為代碼塊,代碼塊內部聲明的變數外部將有可能無法被訪問。

有可能:取決於使用let還是var

for (let i=1; i<=5; i++){
    // i 只能在該代碼塊中被訪問
    console.log(i); // 正常
}

// 超出了 i 的作用域
console.log(i); // 報錯
for (var i=1; i<=5; i++){
    // i 能在該代碼塊中被訪問
    console.log(i); // 正常
}

// 超出了 i 的作用域
console.log(i); // 不會報錯,因為 i 是使用var聲明的

總結

  • let聲明的變數會產生塊作用域,var不會產生塊作用域;
  • const聲明的常量也會產生塊作用域;
  • 不同代碼塊之間的變數無法互相訪問;
  • 推薦使用let或const。

全局作用域

<script>標簽和.js文件的最外層就是所謂的全局作用域,在此聲明的變數在函數內部也可以被訪問。
全局作用域中聲明的變數,任何其它作用域都可以被訪問。

註意

  • 為window對象動態添加的屬性預設也是全局的,不推薦!
  • 函數中未使用任何關鍵字聲明的變數為全局變數,不推薦!!
  • 儘可能少的聲明全局變數,防止全局變數被污染。

作用域鏈

作用域鏈本質上是底層的變數查找機制

  • 在函數被執行時,會優先查找當前函數作用域中查找變數;
  • 如果當前作用域查找不到則會依次逐級查找父級作用域直到全局作用域。

總結

  • 嵌套關係的作用域串聯起來形成了作用域鏈
  • 相同作用域鏈中按著從小到大的規則查找變數
  • 子作用域能夠訪問父作用域,父級作用域無法訪問子級作用域

垃圾回收機制

記憶體的生命周期

JS環境中分配的記憶體,一般有如下生命周期

  1. 記憶體分配:當我們聲明變數、函數、對象的時候,系統會自動為他們分配記憶體
  2. 記憶體使用:即讀寫記憶體,也就是使用變數、函數等
  3. 記憶體回收:使用完畢,由垃圾回收器自動回收不再使用的記憶體
// 為變數分配記憶體
const num = 10;

// 為對象分配記憶體
const obj = {
    num: 10
}

// 為函數分配記憶體
function fn(){
    const num = 10;
    console.log(num);
}

說明

  • 全局變數一般不會回收(關閉頁面回收)
  • 一般情況下局部變數的值,不用了,會被自動回收

記憶體泄漏:程式中分配的記憶體由於某種原因程式未釋放無法釋放叫做記憶體泄漏

演算法說明

這一部分介紹JS引擎是如何回收記憶體的。

複習

堆棧空間分配區別:

  1. 棧(操作系統):由操作系統自動分配釋放函數的參數值、局部變數等,基本數據類型放到棧裡面。
  2. 堆(操作系統):一般由程式員分配釋放,若程式員不釋放,由垃圾回收機制回收。複雜數據類型放到堆裡面。

下麵介紹兩種常見的瀏覽器垃圾回收演算法引用計數法標記清除法

引用計數法

IE採用的引用計數演算法,定義“記憶體不再使用”,就是看一個對象是否有指向它的引用,沒有引用了就回收對象
演算法

  1. 跟蹤記錄被引用的次數
  2. 如果被引用了一次,那麼就記錄次數1,多次引用會累加
  3. 如果減少一個引用就減1
  4. 如果引用次數是0,則釋放記憶體。

引用計數演算法是個簡單有效的演算法,但是現在已經很少使用,因為它存在一個致命的問題:嵌套引用(迴圈引用)

如果兩個對象相互引用,儘管他們已不再使用,垃圾回收器不會進行回收,導致記憶體泄漏。

function fn(){
    let o1 = {}
    let o2 = {}
    o1.a = o2
    o2.a = o1
    return '引用計數無法回收'
}
fn()
image-20230919210142437

如上圖,函數執行結束後,局部變數都被取消,但是由於對象相互引用,記憶體無法被回收。

並且,每執行一次函數,就會導致一次記憶體泄漏。

標記清除法

現代的瀏覽器已經不再使用引用計數演算法了。
現代瀏覽器通用的大多是基於標記清除演算法的某些改進演算法,總體思想都是一致的。
核心

  1. 標記清除演算法將“不再使用的對象”定義為“無法達到的對象”。
  2. 就是從根部(在S中就是全局對象)出發定時掃描記憶體中的對象。凡是能從根部到達的對象,都是還需要使用的。
  3. 那些無法由根部出發觸及到的對象被標記為不再使用,稍後進行回收

image-20230919210704609

如圖,標記清除法可以解決引用計數法無法解決的相互引用的問題。

閉包

概念:一個函數對周圍狀態的引用捆綁在一起,內層函數中訪問到其外層函數的作用域
簡單理解:閉包 = 內層函數 + 外層函數的變數

function outer(){
    const num = 10;
    function fn(){
        console.log(num);
    }
    fn();
}
outer(); // 10

內函數fn使用了外函數的變數num,形成閉包。

另一個例子:統計函數調用次數,函數調用一次,就加1。

function counter(){
    let num = 0;
    return function(){
        console.log(num++);
    }
}

const add = counter();
add(); // 0
add(); // 1
add(); // 2

閉包作用:封閉數據,提供操作,外部也可以訪問函數內部的變數。

閉包應用:實現數據的私有。

變數提升

變數提升是JavaScript中比較“奇怪”的現象,它允許在變數聲明之前即被訪問(僅存在於var聲明變數)

console.log(num); // undefined
var num = 10;

在這個案例中,使用var聲明的num會存在變數提升的現象。提前輸出不會報錯,是因為這個變數已經聲明瞭。雖然聲明會被提升,但是賦值操作不會被提升,所以num還是undefined

註意

  1. 變數在未聲明即被訪問時會報語法錯誤;
  2. 變數在var聲明之前即被訪問,變數的值為undefined;
  3. let/const聲明的變數不存在變數提升;
  4. 變數提升出現在相同作用域當中;
  5. 實際開發中推薦先聲明再訪問變數

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 可擴展性對於物聯網管理系統的設計和開發非常重要,它直接影響著系統的性能、可靠性和能耗等方面,是評估一個系統優劣的重要因素之一。可擴展性對物聯網管理系統的影響主要體現在以下幾個方面: ...
  • 引言 作為一名後端工程師,使用終端是一種常見的做法,也是你應該學習的技能。許多命令和實用程式可以幫助你在使用 Linux 時更有效地完成任務。 基本 Linux 命令 如果你想使用 Linux 操作系統,學習常用的命令將會大有幫助。本篇將為後端工程師回顧一些基本到高級的 Linux 操作命令。 基礎 ...
  • 1. MySQL的客戶端/伺服器通信協議 1.1. MySQL的客戶端和伺服器之間的通信協議是“半雙工”的 1.2. 在任何時刻,要麼是由伺服器向客戶端發送數據,要麼是由客戶端向伺服器發送數據,這兩個動作不能同時發生 1.3. 當查詢的語句很長的時候,參數max_allowed_packet就特別重 ...
  • 引入 本文在兩台2核2g的雲伺服器上搭建了Hadoop集群,兩台雲伺服器分別是阿裡雲(hjm)和騰訊雲(gyt),集群部署規劃如下: hjm gyt HDFS NameNode\SecondaryNameNode\DataNode DataNode YARN ResourceManager\Node ...
  • 一、邏輯存儲結構 表空間(Tablespace):一個mysql實例,及一個資料庫實例,可以對應多個表空間(ibd文件),用於存儲記錄,索引等數據。 段(Segment):分為數據段(Leaf node segment)、索引段(Non-leaf node segment)、回滾段(Rollback ...
  • 之前寫的一個 Flutter 收音機,支持桌面端和手機端,在https://www.cnblogs.com/imlgc/p/17536481.html ,寫完之後就不怎麼管了。後面陸陸續續有人郵件索要驗證碼,不是經常使用的郵箱,所有也不經常打開,也導致很多人沒有收到回覆。 所以,乾脆將這個東西開源了 ...
  • 在DAYU200開發板上燒寫OpenHarmony系統,利用huaweicloud-iot-device-sdk完成華為雲IoT平臺對接,完成物聯網數據通信。 ...
  • 複製 init.gradle.kts 文件到 Windows 的 %USERPROFILE%/.gradle 或者 Linux 的 ~/.gradle 目錄下。也可以直接複製文末的代碼為 init.gradle.kts。 Gradle 不支持鏡像源的直接設置,只能通過 maven() 方法設置一個新 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...