函數表達式與閉包

===============閉包與函數==
函數的兩種方式:函數聲明與函數表達式
函數申明的語法:
Function functionName(arg0,arg1,arg2){
}
其中function是關鍵字 +函數名 部分瀏覽器會給函數定義一個非標準的name屬性
通過這個屬性可以訪問到指定的名字 function.name

函數申明重要特徵是函數聲明提升(function declaration hoisting)意思是執行代碼前會讀取函數申明。

函數表達式var fucntionName()=function(arg0.arg1,arg2){

};
這裏寫圖片描述

這裏寫圖片描述

可以看到functionName也即是指向函數的引用
很明顯該創建的函數後面沒有標識符 因此爲匿名函數此時我們可以看到其實兩者指向的均爲函數本省
這裏寫圖片描述
這裏寫圖片描述

但兩者並不是完全一樣的 可以看到表達式中name屬性爲空字符串

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

注意使用函數申明與函數表達式的返回;
這裏寫圖片描述

此時函數返回爲undefined
這裏寫圖片描述

如果用表達式形式則會返回 此時我們需在前面聲明一個全局的變量接受返回
很明顯依照 if語句 if(condition) statement1 else statement2
這兩個語句可以是代碼或代碼塊:
If (condition) Alert(“statement1”);
或:
If(condition){Alert(“statement2”)}
所以上面例子中裏面加入函數明顯的代碼是不符合語法的但是瀏覽器會盡可能的修復,看出測試中返回的是undefined ,但是爲什麼使用函數表達式後會返回函數引用的值呢?
我們可以看到復值運算符返回類型:
這裏寫圖片描述
這樣也就很好解釋執行if裏面的語句後返回的是condition的值
這裏寫圖片描述
這裏寫圖片描述

有時候也會在函數的返回中
比如直接在函數中返回匿名函數
Function compare(){
Return function(arg0,arg1){

};
}

遞歸函數:
何爲遞歸函數:也即是通過函數名調用自身的情況

Function factoria(num){
If(num<=1){
return 1;
}else {
return num*factorial(num-1);
}

}
然而但我們在代碼中將factorial置空後就會產生很大的問題:
爲什麼會有將他置空的行爲了:很明顯在代碼開發中希望 以前的或別人的代碼不會影響到自己的代碼 2.當攻擊者或其他原因時會給代碼造成影響;
其實js設計的思想最好做到互不影響 如:少用全局變量,等
這裏寫圖片描述
第二行導致指向原始的引用只有一個而函數中又要調用factorial導致錯誤

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

Factorical
Indexnum

可以看到指向的是同一個函數的引用將factotial置空後也即是變爲空了。後面在找時也找不到

解決辦法:
1.非嚴格模式下:arguments.callee 指向正在執行的指針
2. 這裏寫圖片描述
這裏寫圖片描述
注意console若前面有錯誤則不會向下進行

此時仍正常返回指向函數 命名函數表達式 此時區別於匿名函數 因爲在函數代碼中要使用到該函數 並且要不受到外部的影響 此時用fac(5)即可執行該函數,此時大家會問爲什麼了。
其實我們在函數引用時候也是這樣用的使用 fac=f; fac(5);f(5) 這裏只是更加稍微饒了個彎而已。

這裏寫圖片描述
這裏寫圖片描述
這裏寫圖片描述

此時nonefunction(5)大家會很熟悉的 ,從上面例子可以看到將nonefunction置空後會刪除一個引用 而另外一個引用可以正常使用。可以認爲函數名與將函數值複製後的變量絕大部分的性質是相同的,當然前面提到過兩者的name屬性的返回不一樣。也可以看出js中是不存在指針的指針兩者都會指向同一原始引用。
我們可以用:兩個指針變量在驗證當將函數名置空後其他變量是否仍然可以返回。可以看到其他的兩個並不會受到置空的影響。
這裏寫圖片描述

函數閉包:
閉包是指有權訪問另一個函數作用域中的變量值
相信作用域鏈的問題大家很苦惱,其實函數執行會有上下文的環境裏面存放的東西我們暫且不關心 類似於在c語言中函數調用要保存上下文環境一樣。

我們以書本(javascript)的函數爲例子詳細解析這段代碼的執行
這裏寫圖片描述

關注參數object1,object2對象模型 之後用到了object1[propertyName]和object2[propertyName]
此時我們複習以下對象的初始化:可以使用對象字面量或new強攻的方式
New{}強攻的方式
Var person=new object();
Person.name=”object”;
對象字面量的方式
Var person={
name:”object”, //逗號分隔 屬性名:屬性值(自動轉字符串) 屬性名
5:true
};
雜交法:
Var person={}
Person.name=”object”;
訪問對象屬性可以使用點表示法或者使用方括號的表示法(. ,[])

調用傳遞對象字面量參數的時候 display({name:object,age:25})

當每個函數第一次調用時都會創建一個執行環境(execution context)與相應的作用域鏈 並把作用域鏈賦值給特殊的內不屬性:[[scope]] (在jquery或angularjs中都會用到),然後使用this argument或其他命名參數的值來初始化函數的活動對象(active object)
作用域鏈中外部函數始終處於第二位 ,之後的便會由此開始遞推

提取從其中的比較函數並命名爲compare()函數進行分析
Function compare(value1,value2){

}
Var result=compare(5,10);

這裏寫圖片描述

由上面的圖可以很清楚的看到每次函數的活動對象將會壓入鏈表頭 作用域鏈本質是指向變量對象的指針列表,只引用但不包含實際的變量對象
函數中訪問一個變量時,就會從作用域中搜索具有相同名字的變量
函數只想完後局部活動對象銷燬,只包含全局的作用域。
但是前面提到過閉包是指有權訪問另一個函數作用域中的對象,既然不採用閉包時的作用域採用的是如上圖的形式 每訪問變量都會從作用域鏈中進行查找。很明顯我們必須在作用域鏈中加入一個指向外部函數的引用,那麼外部函數放在哪裏呢?很顯然前面說過外部函數始終放在第二位開始(序號爲1),一旦鏈接上,此時內部函數可以訪問外部函數作用域內的任何變量。
當執行完後返回後,其作用域的會被銷燬但活動對象仍然會留在內存中,直至內部函數的引用完成後纔可以銷燬。

This,
This對象基於函數的執行環境綁定,全局中this等於windows,而當函數作爲某個對象的方法時 this指向該對象。
匿名函數的執行環境具有全局性,因此其this對象通常指向window,
這裏寫圖片描述
這裏寫圖片描述

Object.getNameFunc()表示執行了外部函數
這裏寫圖片描述
在加上一個()即object.getNameFunc()()
可以看到返回的是全局中的自變量
爲什麼我在函數裏面卻訪問到的卻是外部的東西了,其實每次都會從作用域鏈上進行查找
每個活動調用時都會取得兩個特殊的變量:this和arguments。當內部函數搜索是都只會搜索到該活動對象爲止
匿名函數的作用域具有全局性
也即是作用域頂上指向的全局windows,並不會找到其外部函數也即是兩者清白的沒有發生任何關係 只是住在了一起。如果鑰匙忘帶了的話還是得房東先處理

如果在外部中加入
Var that=this;

而在內部函數中 return that.name 則會相當於兩者發生了微妙的關係 比如房東知道後 通知了你住在一起的人,住在一起那傢伙的配了把鑰匙給你 that=this 你回來時候必須叫他的名字才能開門 that.name很明顯兩者的作用域鏈是存在交叉的
這裏寫圖片描述
這裏寫圖片描述

此時輸出的是

區分上面的不同 在於調用getNameFunc()返回的是匿名函數或者是表達式 匿名函數裏再返回什麼與表達式返回什麼

YcUstc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章