摘要:什么是閉包紅寶書上給出的定義是閉包是指有權訪問另一個函數作用域中的變量的函數,看到另外一個理解是函數和函數內部能訪問到的變量或者環境的總合,就是一個閉包。閉包用于創建單例所謂單例,就是只有一個實例的對象。它使用立即執行函數和閉包來達到目的。
面試必問題目,但總覺得理解得不深入,索性寫一篇文章慢慢梳理吧。
什么是閉包紅寶書上給出的定義是:閉包是指有權訪問另一個函數作用域中的變量的函數,看到另外一個理解是:函數和函數內部能訪問到的變量(或者環境)的總合,就是一個閉包。創建一個閉包最常見的方式就是在一個函數內部創建另一個函數。下面寫一個例子:
function f1() { var a = 1; function closure() { console.log(++a); } return closure; }
上面例子中,f1 內部的匿名函數以及它能夠訪問到的外部函數的變量 a 合在一起,就形成了一個閉包。使用 return 將閉包返回的目的是讓它可以被外部訪問。下面看看它怎么使用:
var f2 = f1(); // 執行外部函數,返回閉包 f2(); // 2 f2(); // 3 f2(); // 4
第一句執行函數 f1() 后,閉包被返回并賦值給了一個全局變量 f2,以后每次調用 f2(),變量 a 的值就會加 1。通常函數執行完畢后,其作用域鏈和活動對象都會被銷毀,為什么這里 a 并沒有被銷毀并且每次執行 f2() 還會被遞增?原因是閉包有權訪問外部函數的變量,進一步說,閉包的作用域鏈會引用外部函數的活動對象,所以 f2() 在執行時,其作用域鏈實際上是:
自身的活動對象;
f1() 的活動對象;
全局變量對象。
所以 f1() 執行完后,其執行環境的作用域鏈會被銷毀,但活動對象仍然會留在內存中,因為閉包作用域鏈在引用這個活動對象(說白了就是閉包還需要使用外層函數的變量,不允許它們被銷毀),直到閉包被銷毀后,f1() 的活動對象才會被銷毀。
上面例子中,是將返回的閉包賦值給了一個全局變量 f2,var f2 = f1();,f2 是不會被銷毀的,每次執行完 f2(),閉包的作用域鏈不會被銷毀,所以就會出現每次執行 f2(),a 遞增。
但是換一種閉包的調用方式,情況會不同:
f1()(); // 2 f1()(); // 2
因為沒有把閉包賦值給一個全局變量,閉包執行完后,其執行域鏈與活動對象都銷毀了。
閉包的作用 創建用于訪問私有變量的公有方法其實構造函數中定義的實例方法,就是閉包:
function Person(){ var name = "Leon"; function sayHi() { alert("Hi!"); } this.publicMethod = function() { alert(name); return sayHi(); } }
構造函數 Person 中定義實例方法 publicMethod() 就是一個閉包,它可以訪問外部函數的變量 name 和 函數 sayHi(),為什么要這么做呢?因為我們想在構造函數中定義一些私有變量,讓外部不能直接訪問,只能通過定義好的公有方法訪問,從而達到保護變量,收斂外部權限的目的。
而在普通函數中,把閉包 return 出去供外部使用,其實目的也就是:讓函數內部的變量始終保持在內存中,同時保護這些變量,讓它們不能被直接訪問。
function person(){ var name = "Leon"; function sayHi() { alert("Hi!"); } function publicMethod() { alert(name); return sayHi(); } return publicMethod; }閉包用于創建單例
所謂單例,就是只有一個實例的對象。單例模式的好處在于:
保證一個類只有一個實例,避免了一個在全局范圍內使用的實例頻繁創建與銷毀。
比如網頁中的彈窗,點擊 a 按鈕彈出,點擊 b 按鈕隱藏,如果彈窗每一次彈出都需要新建一個對象,將會造成性能的浪費,更好的辦法就是只實例化一個對象,一直使用。
劃分了命名空間,避免了與全局命名空間的沖突。
比如在一個單例中可以定義很多方法,通過單例.方法來使用,避免了在全局環境中定義函數,造成函數名沖突。
下面逐步介紹下單例的創建方式,后兩種方式將用到閉包。
1. 對象字面量創建單例var singleton = { attr1: 1, attr2: 2, method: function () { return this.attr1 + this.attr2; } } var s1 = singleton; var s2 = singleton; console.log(s1 == s2) // true
上面用字面量形式創建了一個單例,可以看到 s1 和 s2 是等同的。這種方式的問題在于外部可以直接訪問單例的內部變量并加以修改,如果想讓單例擁有私有變量,就需要使用模塊模式,模塊模式就是用了閉包。
2. 模塊模式JS 中的模塊模式的作用是:為單例添加私有變量和公有方法。它使用立即執行函數和閉包來達到目的。
var singleton = (function(){ // 創建私有變量 var privateNum = 1; // 創建私有函數 function privateFunc(){ console.log(++privateNum); } // 返回一個對象包含公有方法 return { publicMethod: function(){ console.log(privateNum) return privateFunc() } }; })(); singleton.publicMethod(); // 1 // 2
這里首先定義了一個立即執行函數,它返回一個對象,該對象中有一個閉包 publicMethod(), 它可以訪問外部函數的私有變量。從而這個被返回的對象就成為了單例的公共接口,外部可以通過它的公有方法訪問私有變量而無權直接修改。總結一下就是兩點:
立即執行函數可以創建一個塊級作用域, 避免在全局環境中添加變量。
閉包可以訪問外層函數中的變量。
3. 構造函數+閉包上面提到的對象字面是用來創建單例的方法之一,既然單例只能被實例化一次,不難想到,在使用構造函數新建實例時,先判斷實例是否已被新建,未被新建則新建實例,否則直接返回已被新建的實例。
var Singleton = function(name){ this.name = name; }; // 獲取實例對象 var getInstance = (function() { var instance = null; return function(name) { if(!instance) { instance = new Singleton(name); } return instance; } })(); var a = getInstance("1"); console.log(a); // {name: "1"} var b = getInstance("2"); console.log(b); // {name: "1"}
這里將構造函數和實例化過程進行了分離, getInstance()中存在一個閉包,它可以訪問到外部變量 instance,第一次 instance = null,則通過 new Singleton(name) 新建實例,并將這個實例保存在instance 中,之后再想新建實例,因為閉包訪問到的instance已經有值了,就會直接返回之前實例化的對象。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/108459.html
摘要:如何在初學就理解閉包你需要接著讀下去。這樣定義閉包是函數和聲明該函數的詞法環境的組合。小結閉包在中隨處可見。閉包是中的精華部分,理解它需要具備一定的作用域執行棧的知識。 這是本系列的第 4 篇文章。 作為 JS 初學者,第一次接觸閉包的概念是因為寫出了類似下面的代碼: for (var i = 0; i < helpText.length; i++) { var item = he...
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:當初看這個解釋有點懵逼,理解成閉包就是函數中的函數了。里的閉包最近不滿足于只干前端的活,開始用起了。里的閉包最近在學習語言,讓我們來看一下語言里的閉包。在中,閉包特指將函數作為值返回的情況,被返回的函數引用了生成它的母函數中的變量。 本人開始接觸編程是從js開始的,當時網上很多人說閉包是難點,各種地方對閉包的解釋也是千奇百怪。如今開始接觸js以外的各種編程語言,發現不光是js,php、...
摘要:閉包在我理解是一種比較抽象的東西。所以我寫了一篇博文來方便自己理解閉包。那么現在我們可以解釋一下閉包的第一個定義在計算機科學中,閉包是引用了自由變量的函數。循環中創建閉包在我們使用的關鍵字之前,閉包的一個常見問題就出現在循環中創建閉包。 零. 前言 從我開始接觸前端時就聽說過閉包,但是一直不理解閉包究竟是什么。上網看了各種博客,大家對閉包的說法不一。閉包在我理解是一種比較抽象的東西。所...
摘要:但是閉包也不是什么復雜到不可理解的東西,簡而言之,閉包就是閉包就是函數的局部變量集合,只是這些局部變量在函數返回后會繼續存在。可惜的是,并沒有提供相關的成員和方法來訪問閉包中的局部變量。 (收藏自 技術狂) 前言:還是一篇入門文章。Javascript中有幾個非常重要的語言特性——對象、原型繼承、閉包。其中閉包 對于那些使用傳統靜態語言C/C++的程序員來說是一個新的語言特性。本文將...
閱讀 3723·2021-11-24 09:39
閱讀 1870·2021-11-16 11:45
閱讀 616·2021-11-16 11:45
閱讀 1028·2021-10-11 10:58
閱讀 2475·2021-09-09 11:51
閱讀 1941·2019-08-30 15:54
閱讀 687·2019-08-29 13:13
閱讀 3466·2019-08-26 12:18