- 什么是閉包? 有什么作用
- 閉包的形成
在高級程序設計3中對于閉包的定義是這樣的
<blockquote>有權訪問另一個函數(shù)作用域中的變量的函數(shù)。</blockquote>
在Wikipedia中的定于是這樣的
<blockquote>引用了自由變量的函數(shù)。</blockquote>
一開始,我對這樣的抽象概念很模糊,但是當我理解了閉包之后,我又認為這樣的定義是簡介而有力的,如果從理解閉包的定義來說,可以認為JS中的所有函數(shù)都是閉包,因為每個函數(shù)都能訪問到全局作用域的自由變量
- 閉包的形成
var a = 1
function fn() {
console.log(a)
}
fn()
在這個例子中,fn可以訪問了全局變量a,這個函數(shù)就叫做閉包,當然,這只是從理解的角度來說,實際上這樣的全局作用域下的閉包是沒意義的,因為全局的變量對象不會被銷毀。在函數(shù)內部的閉包可以在chrome的開發(fā)者工具看到

要理解閉包的和閉包的作用,就要從作用域鏈的形成開始,通過下面的例子來說明
var a =2
function fn() {
var a = 1
function fn2() {
console.log(a)
}
fn2()
}
fn()
因為js是詞法作用域,當函數(shù)fn聲明的時候,就會預先包含全局作用域鏈,然后放在fn.Scope中,[[Scope]]是一個只有語言內部才能訪問的屬性,當函數(shù)fn被調用的調用的時候,會創(chuàng)建一個執(zhí)行環(huán)境,執(zhí)行環(huán)境里面有兩個東西,一個是變量對象在函數(shù)內部又叫活動對象,一個是作用域鏈,作用域鏈的形成就是復制fn.Scope里面的變量對象,然后推入當前的活動對象。當fn執(zhí)行完的時候,本來fn執(zhí)行環(huán)境的變量對象和作用域鏈都會銷毀,但是現(xiàn)在只有作用域鏈銷毀了,變量對象卻沒有銷毀,因為內部的函數(shù)fn2的作用域鏈在引用這fn的變量對象。因為fn2的作用域鏈里面包含fn的變量對象,所以可以訪問到fn的變量,所以也就形成了閉包。
- 閉包的缺點
1. 在IE9之前因為使用不同的垃圾收集機制會導致循環(huán)引用會造成內存泄漏
2. 閉包會攜帶包含函數(shù)的作用域,所以會比其他函數(shù)占用更多內存,過度使用閉包會造成內存占用過多
- 閉包的用法
其實保存變量現(xiàn)場,封裝私有變量都是對閉包特性的利用,并不是閉包的定義,不要混淆
1. 保存變量現(xiàn)場,主要利用了js函數(shù)傳遞參數(shù)的方式是按值傳遞
!function() {
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function () {
alert(i)
}
}
arr[1]()//5
}()
本來的打算應該數(shù)字的每一項都是一個函數(shù),可以打印出每一項的索引,但是因為所訪問的i是外部函數(shù)的,當循環(huán)完以后,i已經累加到5了,所以無論多少輸出當然是5,我們可以利用js函數(shù)傳遞是按值傳遞的方式來改造這個數(shù)組
!function() {
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function (n) {
return function () {
alert(n)
}
}(i)
}
arr[1]()//5
}()
改造后的函數(shù)輸出就是1,主要利用按值傳遞參數(shù)和閉包
2. 封裝私有變量
有時候我們常常需要隱藏一些數(shù)據(jù),而僅僅暴露一些接口供外部使用,起到封裝的效果,這時候就可以利用閉包
var Car = (function () {
var speed = 0
return {
setSpeed: function (s) {
speed = s
},
getSpeed: function () {
return speed
}
}
})()
Car.setSpeed(30)
Car.getSpeed()
這就封裝了一個汽車對象,其中是speed私有的變量,只能通過我們提供的接口setSpeed和getSpeed來訪問到
- setTimeout 0 有什么作用
setTimeout表示的是超時調用,setTimeout可以接受多個參數(shù),表示過延遲多長時間再執(zhí)行回調函數(shù)- 第一個參數(shù)表示回調函數(shù),可以是一個字符串或者是一個函數(shù)名,推薦使用函數(shù)名,因為字符串會導致一定的損耗和安全問題
- 第二個參數(shù)表示延遲的時間,單位是ms毫秒,因為js是單線程的解釋器,所以一次只能執(zhí)行一段代碼,為了控制要執(zhí)行的代碼,就有一個任務隊列,這些任務就會按照他們添加到任務隊列的順序執(zhí)行,延遲的時間表示再過多少ms才把當前任務添加到任務隊列,如果隊列前面沒有任務,當前任務就會立即執(zhí)行,如果隊列前面有任務,就要等到前面的任務執(zhí)行完之后再執(zhí)行
- 第三個和后面的參數(shù)表示參數(shù)回調函數(shù)的參數(shù)
setTimeout(f,0)表示的就是盡快的執(zhí)行函數(shù)f,他的作用可以調整時間的發(fā)生順序,例如在開發(fā)中,某個事件發(fā)生在子元素,然后冒泡到父元素,即子元素的回調函數(shù)會比父元素的回調函數(shù)先執(zhí)行,通過setTimeout(f,0)我們就可以讓子元素的回調函數(shù)排在隊列后面,從而讓父元素的回調函數(shù)先執(zhí)行
參考:
定時器-阮一峰