//外部調用返回函數
var next = setup();
alert(next()); //1
alert(next()); //2
alert(next()); //3
alert(next()); //4
通過以上代碼,可以看到2個現象: 1. 變量count是函數setup的私有變量,外部函數是無法直接訪問的,但是我們可以通過函數setup的內部匿名函數訪問。如果我們將這個內部函數返回,外部調用該函數的時候就可以間接訪問函數setup的私有變量。
2. 函數setup的私有變量count看起來像一個靜態變量,每次調用都可以在上一次調用的基礎上遞增1。
現象1: 在Javascript中,函數有兩個特別的特徵,一是前面說過的函數就是對象,二是函數提供局部作用域。這和Java中{}提供變量作用域是有區別的。 Javascript中的作用域鏈訪問模式:
<script>
//全局作用域
var global = "global";
function outer()
{
//函數outer的作用域
var outer_v = "outer";
alert(global); //global,能訪問全局作用域的變量
//alert(inner_v); //不能訪問內部函數作用域的變量
var inner = function(){
//函數inner的作用域
var inner_v = "inner";
alert(outer_v); //outer,能訪問外部函數作用域的變量
}
inner();
}
outer();
//alert(outer_v); // 不能訪問oute函數作用域的變量。 </script>
以上代碼就是爲了說明Javascript語言特有的“鏈式作用域”結構(chain scope)。即子對象會一級一級地向上尋找所有父對象的變量。所以,父對象的所有變量,對子對象都是可見的,反之則不成立。
現象2: 爲什麼函數setup的私有變量count的表現好似Java中的靜態變量? var next = setup();
不難理解,這句話調用之後,我們創建了一個全局變量next指向了函數setup的內部函數,所以setup的內部函數將一直存在於內存中,不會被垃圾回收器回收。而內部函數的存在是依賴外部函數setup的,所以setup也會一直存在於內存中而不被銷燬。所以其私有屬性的值不會被重置。特別注意,Javascript中函數不是類,是第一類對象,歸根結底是對象,相當於內存中存在一個不被銷燬的對象,所以該對象的屬性不會被改變,這和Java中的靜態變量是有區別的。
可以看出,隨意使用返回函數是很消耗性能的,因爲這些函數對象將一直存在於內存中。其實以上闡述的這種返回函數的模式,就是Javascript中所謂的閉包。
閉包的概念: 官方”的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該表達式的一部分。 我的理解是,閉包就是在函數外部使用函數內部的返回函數。也就是說:當函數a的內部函數b被函數a外的一個變量引用的時候,就創建了一個閉包。
閉包的作用主要就是爲了保護私有變量。使用閉包的注意事項: 1.由於閉包會使得函數中的變量都被保存在內存中,內存消耗很大,所以不能濫用閉包,否則會造成網頁的性能問題,在IE中可能導致內存泄露。 2.由於Javascript特殊的作用域鏈,閉包會在父函數外部,改變父函數內部變量的值。
|