首頁 web前端 js教程 JavaScript中的閉包(Closure)詳細介紹_javascript技巧

JavaScript中的閉包(Closure)詳細介紹_javascript技巧

May 16, 2016 pm 04:23 PM
javascript 閉包

閉包是JavaScript中重要的特性,最大的功能在於保存函數運作過程中的資訊。在JavaScript中,閉包的諸多特性源自於函數呼叫過程中的作用域鏈上。

 

函數呼叫物件與變數的作用域鏈

 

對於JavaScript中的每一次函數調用,JavaScript都會創建一個局部物件以儲存在該函數中定義的局部變數;如果在該函數內部還有一個嵌套定義的函數(nested function),那麼JavaScript會在已經定義的局部物件之上再定義一個嵌套局部物件。對於一個函數,其內部有多少層的巢狀函數定義,也就有多少層的巢狀局部物件。這個局部物件稱為“函數呼叫物件”(ECMAScript 3中的“call object”,ECMAScript 5中改名為“declarative environment record”,但個人認為還是ECMAScript 3中的名稱更容易理解一些)。以下面的函數呼叫為例:


複製程式碼 程式碼如下:

function f(x){
  var a = 10;
  return a*x;
}
console.log(f(6));//60

在這個簡單的例子中,當呼叫f()函數時,JavaScript會建立一個f()函數的呼叫物件(姑且稱為f_invokeObj),在f_invokeObj物件內部有兩個屬性:a和x;運行f()時,a值為10而x值為6,因此最後的回傳結果為60。圖示如下:

當存在函數巢狀時,JavaScript將建立多個函數呼叫物件:


複製程式碼 程式碼如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    return b*b;
  }
}
console.log(f(6));//360


在這個範例中,當呼叫f()函數時,JavaScript會建立一個f()函數的呼叫物件(f_invokeObj),其內部有兩個屬性a和x,a值為10而x值為6;執行f ()時,JavaScript會對f()函數中的g()函數進行解析定義,並建立g()的呼叫物件(g_invokeObj),其內部有一個屬性b,b值與傳入參數x相同為6 ,因此最後的回傳結果為360。圖示如下:

可以看到,函數呼叫物件形成了一條鏈。當內嵌函數g()運行,需要取得變數值的時候,會從最近的函數呼叫物件開始進行搜索,如果無法搜尋到,則沿函數呼叫物件鏈在更遠的呼叫物件中進行搜尋,此即所謂的「變數的作用域鏈」。如果兩個函數呼叫物件中出現相同的變量,則函數會取離自己最近的那個呼叫物件中的變數值:


複製程式碼 程式碼如下:

function f(x){
  var a = 10;
  return a*g(x);
  function g(b){
    var a = 1;
    return b*b*a;
  }
}
console.log(f(6));//360, not 3600


在上面的例子中,g()函數的呼叫物件(g_invokeObj)和f()函數的呼叫物件(f_invokeObj)中均存在變數a且a的值不同,當執行g()函數時,在g()函數內部所使用的a值為1,而在g()函數外部所使用的a值則為10。圖示此時的函數呼叫物件鏈如下:

什麼是閉包?

在JavaScript中所有的函數(function)都是對象,而定義函數時都會產生對應的函數呼叫對象鏈,一次函數定義對應一個函數呼叫對象鏈。只要函數物件存在,對應的函數呼叫物件就存在;一旦某函數不再被使用,對應的函數呼叫物件就會被垃圾回收掉;而這種函數物件和函數呼叫物件鏈之間的一一組合,就稱為「閉包」。在上面f()函數和g()函數的例子中,就存在兩個閉包:f()函數物件和f_invokeObj物件組成了一個閉包,而g()函數物件和g_invokeObj-f_invokeObj物件鏈一起組成了第二個閉包。當g()函數執行完畢後,由於g()函數不再被使用,因此g()閉包被垃圾回收了;之後,當f()函數執行完畢後,由於同樣的原因,f()閉包包也被垃圾回收了。

從閉包的定義可以得出結論:所有的JavaScript函數在定義後都是閉包 – 因為所有的函數都是對象,所有的函數在執行後也都有其對應的呼叫對象鏈。

不過,令閉包真正發揮作用的是巢狀函數的情況。由於內嵌函數是在外部函數運行的時候才開始定義的,因此內嵌函數的閉包中所保存的變數值(尤其是外部函數的局部變數值)是這次運行過程中的值。只要內嵌函數物件依然存在,那麼其閉包就依然存在(閉包中的變數值不會發生任何改變),從而也就實現了保存函數運行過程的資訊這個目的。考慮以下這個例子:


複製程式碼 程式碼如下:

var a = "outside";
function f(){
  var a = "inside";
  function g(){return a;}
  return g;
}
var result = f();
console.log(result());//inside


在這個例子中,當運行f()函數時,g()函數被定義,同時建立了g()函數的閉包,g()閉包包含了g_invokeObj-f_invokeObj物件鏈,因此保存了f()函數執行過程中的變數a的值。當執行console.log()語句時,由於g函數物件仍然存在,因此g()閉包也依然存在;當執行這個仍然存在的g函數物件時,JavaScript會使用依然存在的g()閉包並從中取得變數a的值(“inside”)。

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

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

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

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

熱門話題

Java教學
1664
14
CakePHP 教程
1423
52
Laravel 教程
1317
25
PHP教程
1268
29
C# 教程
1242
24
C++ lambda 表達式中閉包的意思是什麼? C++ lambda 表達式中閉包的意思是什麼? Apr 17, 2024 pm 06:15 PM

在C++中,閉包是能夠存取外部變數的lambda表達式。若要建立閉包,請擷取lambda表達式中的外部變數。閉包提供可重複使用性、資訊隱藏和延遲求值等優點。它們在事件處理程序等實際情況中很有用,其中即使外部變數被銷毀,閉包仍然可以存取它們。

C++ 函式中閉包的優點和缺點是什麼? C++ 函式中閉包的優點和缺點是什麼? Apr 25, 2024 pm 01:33 PM

閉包是一種巢狀函數,它能存取外層函數作用域的變量,優點包括資料封裝、狀態保持和靈活性。缺點包括記憶體消耗、效能影響和調試複雜性。此外,閉包還可以建立匿名函數,並將其作為回調或參數傳遞給其他函數。

C++ Lambda 表達式如何實作閉包? C++ Lambda 表達式如何實作閉包? Jun 01, 2024 pm 05:50 PM

C++Lambda表達式支援閉包,即保存函數作用域變數並供函數存取。語法為[capture-list](parameters)->return-type{function-body}。 capture-list定義要捕獲的變量,可以使用[=]按值捕獲所有局部變量,[&]按引用捕獲所有局部變量,或[variable1,variable2,...]捕獲特定變量。 Lambda表達式只能存取捕獲的變量,但無法修改原始值。

解決閉包導致的記憶體洩漏問題 解決閉包導致的記憶體洩漏問題 Feb 18, 2024 pm 03:20 PM

標題:閉包造成的記憶體洩漏及解決方法引言:閉包是JavaScript中一個非常常見的概念,它可以讓內部函數存取外部函數的變數。然而,閉包在使用不當的情況下可能導致記憶體洩漏。本文將探討閉包所造成的記憶體洩漏問題,並提供解決方法及具體程式碼範例。一、閉包引起的記憶體洩漏問題閉包的特性是內部函數可以存取外部函數的變量,這意味著在閉包中引用的變數不會被垃圾回收。如果使用不當,

函數指標和閉包對Golang效能的影響 函數指標和閉包對Golang效能的影響 Apr 15, 2024 am 10:36 AM

函數指針和閉包對Go性能的影響如下:函數指針:稍慢於直接調用,但可提高可讀性和可復用性。閉包:通常更慢,但可封裝資料和行為。實戰案例:函數指標可最佳化排序演算法,閉包可建立事件處理程序,但會帶來效能損失。

PHP 函數的鍊式呼叫與閉包 PHP 函數的鍊式呼叫與閉包 Apr 13, 2024 am 11:18 AM

是的,可以透過鍊式呼叫和閉包優化程式碼簡潔性和可讀性:鍊式呼叫可將函數呼叫連結為一個流暢介面。閉包可建立可重複使用程式碼區塊,並在函數外部存取變數。

golang函數閉包在測試中的作用 golang函數閉包在測試中的作用 Apr 24, 2024 am 08:54 AM

Go語言函數閉包在單元測試中發揮著至關重要的作用:捕獲值:閉包可以存取外部作用域的變量,允許在巢狀函數中捕獲和重複使用測試參數。簡化測試程式碼:透過擷取值,閉包消除了對每個循環重複設定參數的需求,從而簡化了測試程式碼。提高可讀性:使用閉包可以組織測試邏輯,使測試程式碼更清晰、更易於閱讀。

golang匿名函數和閉包的優缺點總結 golang匿名函數和閉包的優缺點總結 May 05, 2024 am 09:54 AM

匿名函數簡潔、匿名,但可讀性差、調試困難;閉包能封裝資料、管理狀態,但可能導致記憶體消耗和循環引用。實戰案例:匿名函數可用於簡單數值處理,閉包可實現狀態管理。

See all articles