閉包指的是函數對象能夠經過做用域鏈相互關聯起來,函數體內部的變量均可以保存在函數做用域內,也就是說閉包有權訪問另外一個函數做用域中的變量的函數。
下面是一個簡單閉包的函數性能優化
function mackFn() { var name = "Husky" function sayName () { console.log('name:' + name) } return sayName } var myFn = mackFn() myFn()
mackFn()建立了一個局部變量name和一個名爲sayName()的函數。sayName()是定義在mackFn()裏的內部函數,sayName()能夠訪問到外部函數的變量,因此sayName()可使用父函數mackFn()函數中聲明的變量name。myFn是執行了mackFn時建立的sayName函數實例的引用,且mackFn()函數造成閉包,因此sayName實例仍可訪問其詞法做用域的變量,便可以訪問name。閉包
閉包其實用處很大,經過上面的例子,能夠了解到閉包容許將數據與其所操做的某些數據關聯起來,所以但咱們使用只有一個方法的對象的地方,就可使用閉包。閉包能夠用來訪問私有變量和模擬私有方法
閉包技術能夠用來共享私有變量,下面的例子建立了一個addPrivateProperty()函數來實現私有屬性存取器方法。這個函數給對象o增長了屬性存取器方法,方法名稱爲'get' + name
和'set' + name
。若是提供了一個斷定函數, setter方法就會用它來檢測參數的合法性,而後再存儲它, 若是斷定函數返回false,setter方法拋出異常。對於兩個存取器方法來講value這個變量是私有的,沒有辦法繞過存取器方法來設置或修改這個值。函數
function addPrivateProperty(o, name, predicate){ var value // 私有變量 // 私有函數 o['get' + name] = function() { return value } o['set' + name] = function(v) { if (predicate && !predicate(v)){ throw Error('set' + name + ': invalid value' + v) } else { value = v } } } var o = {} //設置一個空對象 addPrivateProperty(o, 'Name',function(x){ return typeof x == 'string' }) o.setName('Frank') //設置屬性值 console.log(o.getName()) // => 'Frank'
上述的例子中,在同一個做用域鏈中定義了兩個閉包,這兩個閉包共同享用一樣的私有變量或變量。性能
經過循環建立多個閉包會產生必定的缺陷,即閉包只能取得包含函數中任何變量的最後一個值, 下面的第一個例子中,定時器函數返回的是10, 由於每一個函數的做用域都保存着createFn()函數的活動對象。因此它們引用的都是同一個變量n。當createFn()函數中,執行完循環以後變量n的值是10,此時每一個定時器函數都應用這保存變量n的同一個變量對象,因此在每一個函數內部n的值都是10優化
function createFn() { for (var n = 0; n < 5; n++) { setTimeout(function() { return console.log(n); }, 100 * n); } } createFn() // => 5 5 5 5 5
能夠經過定義了一個匿名函數並將當即執行該匿名函數,在調用每一個匿名函數的時候,傳入了變量num,因爲函數參數是按值傳遞的,因此就會將變量k的當前值賦值給匿名函數的參數num,而在這個匿名函數的內部,又會建立並返回了一個訪問k的閉包,所以定時器函數中都有本身k變量的一個副本,即可以返回不一樣的數值。code
function createFn() { for (var k = 0; k < 10; k++) { (function(num) { setTimeout(function() { return console.log(num); }, 100 * k); })(k); } } createFn(); // => 1 2 3 4 5
一般,函數的做用域及其全部的變量都會在函數執行結束後被銷燬。可是,當函數一旦返回了閉包,這個函數的做用域將會一直在內存中保存到閉包不存在爲止。
可是能夠經過模仿塊級做用域來實現。經過建立並當即調用一個函數,函數內部的全部變量都會被當即銷燬(除某些變量賦值給了包含做用域中的變量),這樣既能夠執行其中的代碼,又不會在內存中留在對該函數的引用。對象
function Fn(count) { (function() { for(var i = 0; i < count; i++) { console.log(i) } })() console.log('i:' + i) // 拋出錯誤 } Fn(5)
經過建立函數Fn(),在for循環外部插入一個塊級做用域(私有做用域),在匿名函數中定義的任何變量,都會在執行結束的時候被銷燬。所以,變量i只能坐在循環中使用,使用後就被銷燬。而在使用做用域中可以訪問count變量,是由於這個匿名函數是一個閉包,它可以訪問包含做用域中的全部變量。ip
經過建立私有做用域,在全局做用域中被函數外部使用,從而限制向全局做用域中添加過多的變量和函數。經過此方法不只可使用本身變量,並且不用擔憂搞亂全局做用域。也就是說,能夠經過閉包建立私有做用域將某些變量做爲局部變量,避免使用全局變量而佔用過多的內存。內存
閉包有着其優勢,能夠利用其優勢實現不少功能,如閉包能夠用來訪問私有變量和模擬私有方法(訪問私有屬性的模式有不少,之後總結)等等,可是由於建立閉包必須維護額外的做用域,因此過渡使用閉包會佔用大量的內存。作用域