度過一個愉快短暫的週末,又能夠開始學習了!我愛學習,學習令人進步。今天學習函數表達式,着重學習下閉包函數。html
函數表達式前端
能夠在定義的函數聲明以前調用它,可是不能在定義函數表達式以前調用它 c#
/** * 理解函數聲明和函數表達式的區別,學習拉姆達(Lambda)表達式 */
functionName(a);
aFuncton(a);//錯誤 function fuctionName(arg){};//函數聲明 var aFunction=function (arg) {};//函數表達式(匿名函數) /* 函數聲明:在執行代碼以前會先讀取函數聲明,函數聲明能夠放在調用它的語句後面。
Lambda表達式:建立匿名函數的一種方法,=>來表示
運算符右邊的表達式對運算符左邊指定的參數執行操做
*/
var x=3,
fun=
x=>{returnx*x;};//lambda表達式
遞歸:在函數內部調用本身數組
/**
* 借用arguments.callee和使用函數命名錶達式實現遞歸
*/
//遞歸函數
function factorial(num){
if(num<=1){ return 1; }else{ return num*factorial(num-1); } }; var anotherFactorial=factorial; factorial=null; alert(anotherFactorial(4));//出錯,factorial值發生變化。 //解決方案:1.非嚴格模式下 function factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } }; //2.命名函數表達式 var factorial=(function f(num){ if(num<=1){ return 1; }else { return num * f(num - 1); } });
閉包:可以訪問另外一個函數做用域中的變量的函數。閉包
做用域鏈:本質上是一個指向變量對象的指針列表函數
建立函數時,會建立一個預先包含全局變量對象的的做用域鏈,這個做用域鏈被保存在內部的[[Scope]]屬性中,調用函數時建立一個執行環境,而後複製[[Scope]]屬性中的對象構建起執行環境的做用域鏈,而後有一個函數的活動對象被建立並推入執行環境做用域鏈的前端。學習
在另外一個函數內部定義的函數會將包含函數(即外部函數)的活動對象添加到它的做用域鏈中 this
function createComparisonFunction(propertyName) {
return function(object1,object2){ var value1=object1[propertyName]; var value2=object2[propertyName]; if(value1<value2){return -1;} else if(value1>value2){return 1;} else{return 0;} }; } //建立函數 var compareNames=createComparisonFunction('name'); //調用函數 var result=compareNames({name:'趙雲'},{name:'馬超'}); //解除對匿名函數的引用,以便釋放內存 compareNames=null;
做用域鏈能夠想象成一個列表,裏面的函數建立時候內部建立它,這個列表中,最上面的spa
上面的例子中,有兩個做用域鏈,createComparisonFunction和匿名函數function(object1,object2)的做用域鏈分別是A和B。匿名函數從compareName()被返回後,B包含全局變量對象、createComparisonFunction函數的活動對象和匿名函數的活動對象的引用,createComparisonFunction函數執行完畢A被銷燬,可是它的活動對象被B引用,雖然這樣能夠在全局環境中訪問外部這個函數的變量了,但這個外部函數的活動對象已然存在。因此調用函數結束後要解除對匿名函數的引用。設計
function a(){
var result=[]; for (var i=0;i<10;i++){ result[i]=function () { return i;}; } }
上面的例子中,最後result數組中每一個值都是10! 由於i是a函數活動對象中的變量,每一個內部函數做用域鏈中都有a的活動對象,因此引用的是同一個i,a函數返回後i=10。
解決方案:使用當即執行函數
1 function a() {
2 var result = []; 3 for (var i = 0; i < 10; i++) { 4 result[i] = function (num) { 5 return function () { 6 return num; 7 } 8 } 9 return result; 10 } 11 }
在做用域中聲明的變量,可能會被其餘人不當心用同名的變量給覆蓋掉,做用域鏈的特性,用匿名函數做爲一個容器,容器內部能夠訪問外部的變量,而外部環境不能訪問容器內部的變量,因此( function(){…} )()內部定義的變量不會和外部的變量發生衝突,至關於命名空間。
閉包中使用this對象
this對象是在運行時基於函數的執行環境綁定的:全局函數,this等於window,而當函數被最爲某個對象的方法調用時,this就是那個對象。因爲匿名環境具備全局性,它的this一般指向window.
1 var name='Window';
2 var object={ 3 name:'object', 4 getName:function(){ 5 return function () { 6 return this.name 7 } 8 } 9 };; 10 console.log(object.getName()) //Window
例子中getName方返回一個匿名函數,這個匿名函數返回this.name,調用內部匿名函數的時候,你不函數在搜索this和arguments兩個變量時,只會搜索到活動對象爲止。
解決方案:
var name='Window';
var object={ name:'object', getName:function(){ var that=this; return function () { return that.name; } } }; console.log(object.getName());
閉包函數會致使內存泄露,在垃圾清理的引用計數模式中,若是有閉包函數做用域鏈中有html元素,則該元素的引用至少一直是1,所以它所佔用的內存不會被回收,所以要吧該元素的賦值變量設爲null;
模仿塊級做用域:
(function(){})();//把函數聲明轉爲函數表達式
當即調用了一個匿名函數
在一個由不少開發人員共同參與的大型應用程序中,過多的全局變量和函數容易致使命名衝突,經過建立私有做用域解決。
私有變量
任何函數中定義的變量,能夠認爲是私有變量。包括函數的參數,局部變量和函數內部定義跌其它函數。
訪問私有變量方法:1構造函數中建立閉包
靜態私有變量:利用原型。
重點:模塊模式
對象字面量:實際是單例(只有一個實例的對象)。模塊模式就是爲單例建立私有變量和特權方法。
1 var singleton=function(){
2 //私有變量和私有函數
3 var privateVaribale=10; 4 function privateFunction(){ 5 return false; 6 } 7 return{ 8 publicProperty:true; 9 publicMethod:funciont(){ 10 privateVaribale++; 11 return privateFunction(); 12 } 13 }; 14 }();
這個模塊模式使用了一個返回對象的匿名函數。在這個匿名函數內部,首先定義了私有變量和函數,有一個對象字面量做爲函數的值返回,所以它的公有方法有權訪問私有變量和函數。從本質上來講,這個對象字面量定義的是單例的公共接口。這種模式在須要對單例進行某些初始化,同時又須要維護其私有變量時是很是有用的。
模塊模式用來設計一個加載器什麼的頗有用,專門寫一篇來分析。