關于函數聲明,它的一個重要特征就是函數聲明提升,意思是在執行代碼之間會讀取函數聲明,意思是在執行代碼之前會先讀取函數聲明。這就意味著可以把函數聲明放在調用它的語句
后面。
sayHi(); function sayHi(){ alert("Hi!"); }
理解函數提升的關鍵,就是理解函數聲明與函數表達式之間的區別。
//不要這樣做 if(condition){ function sayHi(){ alert("HI!"); } }else{ function sayHi(){ alert("Yo!"); } }
表面上看,以上代碼表示在condition為true時,使用一個sayHi()的定義;否則,就使用另一個定義。實際上,這在ECMSscript中屬于無效語法,JavaScript引擎會嘗試修正錯誤,
將其轉換為合理狀態。不過,如果是使用函數表達式,那就沒什么問題了。
//可以這樣做 var sayHi; if(condition){ sayHi=function(){ alert("Hi!"); } } else{ function sayHi(){ alert("Yo!"); } }
這個例子不會有什么意外,不同的函數會根據condition被賦值給sayHi.
遞歸
我們知道,argument.callee是一個指向正在執行的函數的指針,因此可以用它來實現對函數的遞歸調用,例如:
function factorial(num){ if(num<=1){ return 1; }else{ return num*argument.callee(num-1); } }
閉包
閉包是指有權訪問另一個函數作用域中的變更的函數。創建閉包的常見方式,就是在一個函數內部創建另一個函數。
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; } } }
閉包與變更
作用域鏈的這種配置機制引出一個值得注意的副作用,即閉包只能取得包含函數中任何變更的最后一個值。如下面這個例子:
function createFunctions(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]=function(){ return i; } } return result; }
這個函數會返回一個函數數組。表面上看,似乎每個函數都應該返回自己的索引值,即位置0的函數返回0,位置1的函數返回1,以此類推。但實際上,每個函數都返回10。因為每個
函數作用域鏈中都保存著createFunctions()函數的活動對象(只是保存著對匿名函數function(){return i;}的引用),所以它們返回的都是最后一個i.w但是,我們可以通過創建
另一個匿名函數強制讓閉包的行為符合預期,如下所示:
function createFunctions(){ var result=new Array(); for(var i=0;i<10;i++){ result[i]= function(num){ return function(){ return num; } }(i) } return result; }
關于this對象
我們知道,this對象是在運行時基于函數的執行環境綁定的:在全局函數中,this等于window,而當函數被作為某個對象的方法調用時,this等 于那個對象。不過,匿名函數的
執行環境具有全局性,因此,其this對象通過指向window
var name="The Window"; var object={ name:"My Object", getNameFunc:function(){ return function(){ return this.name; } } }
alert(object.getNameFunc()()); //"The Window" ("在非嚴格模式下")
前面提到過,每個函數在被調用時都會自動取得兩個特殊變更:this和arguments。內部函數在搜索這兩個變量時,只會搜索到其活動對象為止,因此永遠不可能直接訪問外部函數中
的這兩個。不過,把外部作用域中的this對象保存在一個閉包能夠訪問到的變量里,就可以讓閉包訪問該對象了。如下所示:
var name="The Window"; var object={ name:"My Object", getNameFunc:function(){ var that=this; return function(){ return this.name; } } }
模仿塊級作用域
如前所述,JavaScript沒有塊級作用域的概念。這意味磁卡在塊語句中定義的變量,實際上是包含在函數中而非語句中創建的,來看下面的例子.
function outputNumbers(count){ for(var i=0;i<count;i++){ alert(i); } alert(i); //計數 }
即使像下面這樣錯誤的重新聲明同一個變量,也不會改變它的值。
function outputNumbers(count){ for(var i=0;i<count;i++){ alert(i); } var i; //重新聲明變更 alert(i); //計數 }
JavaScript從來不會告訴你是否多次聲明了同一個變量;遇到這種情況,它只會對后續的聲明視而不見。匿名函數可以用來模仿塊級作用域并避免這個問題。
用作塊級作用域的匿名函數的語法如下所示:
(function(){ //這里是塊級作用域 })();
以上代碼定義并立即調用了一個匿名函數。將函數聲明包含在一對圓括號中,表示它實際上是一個函數表達式。而緊隨其后的另一對圓括號會立即調用這個函數。
無論在什么地方,只要臨時需要一些變量,就可以使用私有作用域,例如:
function outputNumbers(count){ (function(){ for(var i=0;i<count;i++){ alert(i); } })(); alert(i); //導致一個錯誤! }
文章列表