JavaScript閉包的深入理解

来源:https://www.cnblogs.com/gulei/archive/2018/01/19/8317746.html
-Advertisement-
Play Games

閉包算是javascript中一個比較難理解的概念,想要深入理解閉包的原理,首先需要搞清楚其他幾個概念: 一、棧記憶體和堆記憶體 學過C/C++的同學可能知道,電腦系統將記憶體分為棧和堆兩部分(大學的基礎課,忘掉的趕緊重新撿起來)。 棧記憶體(連續的存儲空間,類似數據結構中的棧):主要用來存放數值、字元、 ...


閉包算是javascript中一個比較難理解的概念,想要深入理解閉包的原理,首先需要搞清楚其他幾個概念:

一、棧記憶體和堆記憶體

學過C/C++的同學可能知道,電腦系統將記憶體分為棧和堆兩部分(大學的基礎課,忘掉的趕緊重新撿起來)。

棧記憶體(連續的存儲空間,類似數據結構中的棧):主要用來存放數值、字元、記憶體地址等小數據

堆記憶體(散列的存儲空間,類似數據結構中的鏈表):存放可以動態變化的大數據

 

二、基本類型和引用類型

JavaScript將變數分為兩種類型:

基本類型:Number、String、Boolean 、undefined、null(值被保存在棧記憶體中)

引用類型:Object、Array、function(具體內容被保存在堆記憶體中,在棧記憶體中僅保存堆記憶體的地址)

如上圖,當在程式中在執行中有如下情況:

1、聲明變數a為基本類型時,直接在棧記憶體中保存它的值為100;

2、當將a賦值給b時,b在棧記憶體中新建空間,將a的值複製過來

(註:之後a和b就沒有關係了,再改變a或b的值,不影響另外一個,它們是獨立的)

3、聲明變數p1為引用類型時,將p1的內容保存在堆記憶體中,並將堆記憶體的物理地址保存在棧記憶體中

4、當將p1賦值給p2時,p2在棧記憶體中新建空間,僅複製堆記憶體的物理地址

(註:p1和p2中都保存的是指向堆記憶體的地址,即指的是同一個對象,當修改p1對象的屬性後,p2對象的屬性同時被修改)

 

另外,在電腦語言中還有一些很重要的特性:

1、修改基本類型的值,實際上是新建空間存一個新值,然後將變數名指向新的空間(舊值依然存在棧記憶體中,只是缺少變數名指向它)

2、刪除引用類型,其實並不刪除堆記憶體中的內容,僅刪除了棧記憶體中的物理地址(對象的內容依然存在堆記憶體中,只是缺少了地址的指向)

(註:電腦關於記憶體的管理,跟我們正常想到的不一樣,例如硬碟恢復就是利用這個原理,為刪除的內容重新建立一個指向即可訪問)

 

二、變數作用域

javascript中變數又分為全局變數和局部變數

全局變數:在全局環境中聲明的變數

局部變數:在函數中聲明的變數

當函數在執行時,會創建一個封閉的執行期上下文環境,函數內部聲明的變數僅可在函數內部使用,外部無法訪問,而全局變數則在任何地方都可以使用

 

三、預編譯

JavaScript的運行為三步:語法分析》預編譯》解釋執行

1、語法分析:通篇掃描js文件,檢查是否有低級語法錯誤

2、預編譯四部曲:(發生在解釋執行的前一刻)
  a、創建AO對象(執行期上下文對象,全局為GO)
  b、將形參和變數聲明作為AO對象的屬性名,值為undefined
  c、將實參值傳遞給形參,即賦值給AO對象對應屬性名
  d、將函數聲明為AO對象的方法名,值為函數體

3、解釋執行:解釋一行,執行一行。

function test(a){
  var b=1;
 function c(){}  
}
test(2);
/* 函數預編譯四部曲(函數執行前一刻,不執行不會預編譯),全局預編譯同理
 * 1---testAO{}
 * 2---testAO{a:undefined,b:undefined}
 * 3---testAO{a:2,b:undefined}
 * 4---testAO{a:2,b:1,c:function(){}}
 */

 

四、作用域鏈

每個JavaScript函數都是一個對象,對象中有些屬性可以訪問(比如name),有些屬性不可以訪問(比如[[scope]]僅供js引擎使用)

[[scope]]用來存儲了運行期上下文對象的集合(即作用域鏈),作用域鏈中除了自身創建的AO對象外,還包括了所有父級運行期上下文對象(AO)

function a(){
  function b(){
    var b = 234;   
  }
  var a = 123;
  b();
}
var glob = 100;
a();

 

當b執行完成後,b的AO要被銷毀,即b的[[scope]]第0位將被置空,如果再次執行b,將新建一個新的AO將其地址存到第0位,

當a也執行完成後,a的AO要被銷毀,即a的[[scope]]第0位將被置空,同時a的AO中存著b,b也將被一同銷毀

 

在瞭解如上這些概念後,我們再來看下麵這個經典的閉包,你會有一個全新的認識

function a(){
  var b=123;
  function c(){
    console.log(b+=1);
  }
  return c;
}
var d=a();
d();

當這段代碼在執行時的順序如下:

1、預編譯全局,生成執行上下文對象GO{d:undefined,a:function(){}}

2、定義a函數,將a函數的[[scope]]屬性設置為{0:GO}

3、預編譯a函數,生成a的執行上下文對象aAO{b:undefined,c:function(){}},修改a函數的[[scope]]屬性為{0:aAO,1:GO}

4、執行a函數,給aAO的屬性賦值{b:123,c:function(){}}

5、定義c函數,將c函數的[[scope]]屬性設置為{0:aAO,1:GO},並將c返回給d

6、a函數執行完畢,銷毀[[scope]]屬性第0位對aAO對象的引用

7、執行d函數(等於執行c函數)之前,先預編譯生成c的執行上下文對象cAO{},修改c函數的[[scope]]屬性為{0:cAO,1:aAO,2:GO}

8、執行c函數,b變數在cAO中沒有,到[[scope]]屬性中的下一位aAO中獲取

 

 

 

我的博客即將搬運同步至騰訊雲+社區,邀請大家一同入駐:https://cloud.tencent.com/developer/support-plan


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

-Advertisement-
Play Games
更多相關文章
  • 1, 轉義字元 轉義字元:用於表示網頁中的特殊字元 XHTML不直接輸入符號,建議使用轉義字元。 &nbsp 空格; &copy 版權; &reg 註冊商標 如果輸入連續的空格要使用&的轉義字元&(&amp;)nbsp,即&amp;nbsp; 2, 水平線 <Hr> 水平線 <hr width="5 ...
  • xss攻擊(跨站腳本) 是網站應用程式的安全泄露攻擊,是代碼註入的一種。它允許惡意用戶將代碼註入到網頁上,其他用戶在觀看網頁時就會受到影響。 攻擊原理 其特點是不對伺服器端造成任何傷害,而是通過一些正常的站內交互途徑,例如發佈評論,提交含有 JavaScript 的內容文本。這時伺服器端如果沒有過濾 ...
  • 前端框架層出不窮,不過萬變不離其宗,就是從MVC過渡到MVVM。從數據映射到DOM,angular中用的是watcher對象,vue是觀察者模式,react就是state了。 React通過管理狀態實現對組件的管理,通過this.state()方法更新state。當this.setState()被調 ...
  • 在網上找了好久針對react-native的測試方法,但是沒有找到靠譜的方式。要麼很淺只是跑了一下官方的例子,要麼就是版本有點老舊,照著無法進行。jest提供的react-native例子很少,而enzyme提供的react-native-mock庫也是各種報錯,讓人很是絕望。於是乎在搜索到的信息指 ...
  • 屬性 屬性名 屬性類型 描述說明 預設值 language String 多語言設置,使用時需提前引入\locales文件夾下對應的語言文件,中文zh,引入語言文件必須放在fileinput.js之後 'en' showCaption Boolean 是否顯示被選文件的簡介 true showBro ...
  • Skip to content This repository Search Pull requests Issues Marketplace Explore @VIVI863628 Sign out Unwatch 1 Star 0 Fork 0 VIVI863628/PouchDB Code I... ...
  • JavaScript數組去重 1、原型去重法。通過prototype找到數組的源性對象Array,在數組的原型上添加unique()方法。需要使用的時候使用 點 “ . ” 進行連接。 優點:擴展性比較高,復用性比較高。 缺點:通過給數組對象擴展,新增方法,導致數組結構中有新增了一個方法。此時如果用 ...
  • 個人JavaScript小工具,每天熟記一點點。多練習,達到熟練的效果。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...