前言
之前發了一篇文章,寫了一些對于閉包的了解。現在補上閉包的應用篇,(很慚愧因為嚴重的拖延症一直拖到現在)。本文主要分享一些常見的閉包用法和分析,也希望能增加對閉包的了解。
簡單回顧
在之前的文章裡,講解了閉包的原理,如果忘記了可以點選這裡再看一下,在這裡我們簡單回顧一些知識點:
- 閉包的本質是一個函數
- 閉包可以通路函數内部變量
- 閉包的存在會使内部變量保留在記憶體中
閉包的應用
閉包常見的用法,就将圍繞這些特點展開:
1.模仿塊級作用域
首先簡單舉個例子來,解釋一下什麼是塊級作用域:
function A(num) {
for (var i = 0; i < num; i++) {
num++;
}
console.log(i)
}
在這個簡單的函數中,
變量i
是在
for
循環中定義的,如果是在C++或者Java中,這樣定義的變量,一旦循環結束,變量也就随之銷毀,i的作用範圍隻在循環這個小塊,就稱為塊級作用域。在javascript中,沒有這樣的塊級作用域,前面一篇文章已經提到,變量是定義在函數的活動對象中的,是以,從定義
i
開始,在函數内部可以随時通路它。
這樣的壞處顯而易見:由于javascript不會告訴你變量是否已經被聲明,容易造成命名沖突,如果是在全局環境定義的變量,就會污染全局環境,是以可以利用閉包特性來模拟塊級作用域。不過在此之前要先介紹另一個知識點:匿名立即執行函數。如果已經比較熟悉的同學可以直接跳過這一塊:
匿名立即執行函數
首先舉個例子(我比較喜歡舉例,感覺看例子比較更容易了解):
var helloWorld = function(){
alert('Hello world')
}
helloWorld();//執行函數
上面的簡短代碼一共就做兩件事:1.定義了一個匿名函數并指派給
helloWorld
;2.在
helloWorld
後面加括号表示調用函數,是以 匿名函數如果直接執行,是不是應該這樣寫:
function(){
alert('Hello world')
}()
這樣的寫法會報錯,因為在javascript中,function是函數聲明的标志,不允許在後面直接加括号,而應該寫成這樣:
(function(){
//函數體
alert('Hello world')
})()
也就是把聲明部分加括号即可,加了括号以後,這一段代碼就相當于執行了裡面的函數體部分,但是此時内部的變量已經不能被外部通路,請看下面詳細樣例
具體實作
現在我們講模拟塊級作用域的具體步驟,假設還是針對前面的
A函數
,如果我們想讓i變量隻有塊級作用域,可以這樣寫:
function A(num) {
//核心代碼
(funnction(){
for(var i = 0; i<num; i++) {
num++;
}
})()
//核心代碼結束
console.log(i)//underfined
}
注意看核心代碼部分,我們用剛剛講到的匿名自執行函數在内部形成了一個閉包,這個閉包在哪呢?一直強調,閉包的本質是函數,其實在這裡閉包就是那個匿名函數,這個閉包可以到
函數A
内部的活動變量,又能保證自己内部的變量在自執行後直接銷毀,這個應該不難了解了
優點分析
這種寫法的經常用在全局環境中,可以避免添加太多的全局變量和全局函數,特别是多人合作開發的時候,可以減少是以産生的命名沖突等,避免污染全局環境。
2.存儲變量
我們知道閉包的另一個特點是可以儲存外部函數的變量,原理是基于javascript中函數作用域鍊的特點,内部函數保留了對外部函數的活動變量的引用,是以變量不會被釋放(這一塊沒有了解清楚的請看前一篇文章,裡面講的比較詳細),然後我們再來愉快地舉例子:
function B(){
var x = 100;
return {
function(){
return x
}
}
}
var m = B()//運作B函數,生成活動變量 x被m引用
這是前文介紹過的一個最簡單的閉包例子,我們運作
B函數
,傳回值就是B内部的匿名函數,此時m引用了變量x,是以B執行後x不會被釋放,利用這一點,我們可以把比較重要或者計算耗費很大的值存在x中,隻需要第一次計算指派後,就可以通過m函數引用x的值,不必重複計算,同時也不容易被修改
優點分析
這種寫法可能會用在把一些不經常變動,但是計算比較複雜的值儲存起來,就可以節省每次通路的時間。
3.封裝私有變量
javascript中沒有私有成員的概念,我們可以把函數當做一個範圍,函數内的變量就是私有變量,在外部無法引用,比如:
function C(a,b){
var c = a - b ;
return c
}
在這個函數中,a b c都是私有變量,在外部無法訪,利用閉包的特點,我們可以就可以建立可以通路私有變量的方法:
function Person(){
var name = 'default';
this.getName:function(){
return name;
}
this,setName:function(value){
name = value;
}
}
console.log(Person.getName())//default
console.log(Person.setName('mike'))
console.log(Person.getName())//mike
在這個例子中,設定了兩個閉包函數來操作
Person函數内部的name變量
,除了這兩個函數,在外部無法再通路到name變量,name也就相當于是私有成員。在這個例子中,我們用的是在構造函數中定義公有方法,對于所有的Person執行個體,都分别建立了新的辦法,當然還可以使用其他形式來避免這個問題,要涉及到建立對象模式的一些知識,在這裡說明怕反而增加了閉包的了解難度,之後在寫對象和繼承的時候再提到(下一次更新一定不會這樣久了QAQ)。
小結
關于閉包的主要主要應用就講到這裡,本文中很多知識點與上一篇文章有關,又因為釋出相隔時間比較長(我的鍋),建議大家可以先看看上一篇複習一下,這篇相對來前一篇容易了解,而且在舉例過程盡量沒有加入其它的疑難知識點,希望能對看到的人有所幫助。以上内容屬于個人見解,如果有不同意見,歡迎指出和探讨。同時,碼字不易請尊重作者的版權,轉載請注明出處,如作商用,請與作者聯系,感謝!
補充
如果看完對您有幫助,順手點個推薦呗~