欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

javascript高級(jí)編程之函數(shù)表達(dá)式 遞歸和閉包函數(shù)

 更新時(shí)間:2015年11月29日 10:25:52   作者:sunshinegirl_7  
這篇文章主要介紹了javascript高級(jí)編程之函數(shù)表達(dá)式 遞歸和閉包函數(shù)的相關(guān)資料,需要的朋友可以參考下

定義函數(shù)表達(dá)式有兩種方式:函數(shù)聲明和函數(shù)表達(dá)式.

函數(shù)聲明如下:

function functionName(arg0,arg1,arg2){
 //函數(shù)體
}

首先是function關(guān)鍵字,然后是函數(shù)的名字.

FF,Safrai,Chrome和Opera都給函數(shù)定義了一個(gè)非標(biāo)準(zhǔn)的name屬性,通過(guò)這個(gè)屬性可以訪問(wèn)到函數(shù)指定的名字.這個(gè)函數(shù)的值永遠(yuǎn)等于跟在function關(guān)鍵字后面的標(biāo)識(shí)符.

//只在FF,Safari,Chrome和Opera有效
alert(functionName.name)//functionName

函數(shù)聲明的特征就是函數(shù)聲明提升(function declaration hoisting),意思是在執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明.這就意味著可以把函數(shù)聲明放在調(diào)用它的語(yǔ)句后面.

sayHi();
function sayHi(){
 alert("Hi!");
}

這種例子不會(huì)拋出錯(cuò)誤,因?yàn)樵诖a執(zhí)行之前會(huì)先讀取函數(shù)聲明.

第二種是函數(shù)表達(dá)式.

var functionName=function(arg0,arg0,arg2){
 //函數(shù)體
}

這種形式看起來(lái)好像是常規(guī)的變量賦值語(yǔ)句,即創(chuàng)建一個(gè)函數(shù)并將它賦值給變量functionName.這種情況下創(chuàng)建的函數(shù)叫做匿名函數(shù)(anonymous function),因?yàn)閒unction關(guān)鍵字后面沒(méi)有標(biāo)識(shí)符.(匿名函數(shù)有時(shí)候也叫拉姆達(dá)函數(shù).)匿名函數(shù)的name屬性是空字符串.

函數(shù)表達(dá)式與其他表達(dá)式一樣,在使用前必須先賦值.

以下代碼會(huì)導(dǎo)致錯(cuò)誤:

syaHi();//Uncaught ReferenceError: syaHi is not defined
var sayHi=function(){
 alert("Hi!");
}

不要像下面這樣寫代碼,這在ECMAScript中屬于無(wú)效語(yǔ)法,JavaScript引擎會(huì)嘗試修正錯(cuò)誤,但不同瀏覽器修改不同.

//不要這樣做
if(condition){
 function sayHi(){
  alert("Hi!");
 }
}else{
 function sayHi(){
  alert("Yo!");
 }
}

如果是使用函數(shù)表達(dá)式,就沒(méi)什么問(wèn)題了.

//可以這樣做
var sayHi;
if(condition){
 sayHi=function(){
  alert("Hi!");
 }
}else{
 sayHi=function(){
  alert("Yo!");
 }
}

能夠創(chuàng)建函數(shù)再賦值給變量,也就能夠把函數(shù)作為其它函數(shù)的值返回.

function creatComparisonFunction(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;
  }
 };
}

creatComparisonFunction()就返回了一個(gè)匿名函數(shù).返回的函數(shù)可能會(huì)被賦值給一個(gè)變量,或者以其他方式被調(diào)用;不過(guò),在creatComparisonFunction()函數(shù)內(nèi)部,它是匿名的.在把函數(shù)當(dāng)成值來(lái)使用的情況下,都可以使用匿名函數(shù).

7.1 遞歸

遞歸函數(shù)是一個(gè)函數(shù)通過(guò)名字調(diào)用自己的情況下構(gòu)造的.

function factorial(num){
 if(num<=1){
  return 1;
 }else{
  return num*factorial(num-1);
 }
}

上面是一個(gè)經(jīng)典的遞歸階乘函數(shù).下面的代碼卻可能導(dǎo)致它出錯(cuò).

var anotherFactorial=factorial;
factorial=null;
alert(anotherFactorial(4));//Uncaught TypeError: factorial is not a function

以上代碼先把factorial()函數(shù)保存在變量anotherFactorial中,之后又將factorial變量設(shè)為null,結(jié)果指向原始引用只剩下一個(gè).接下來(lái)調(diào)用anotherFactorial()時(shí),由于必須執(zhí)行factorial(),而factorial()已經(jīng)不再是函數(shù),所以會(huì)導(dǎo)致錯(cuò)誤.

這種情況下,使用arguments.callee可以解決.

arguments.callee是一個(gè)指向正在執(zhí)行的函數(shù)的指針,因此可以用它來(lái)實(shí)現(xiàn)對(duì)函數(shù)的遞歸調(diào)用.

function factorial(num){
 if(num<=1){
  return 1;
 }else{
  return num*arguments.callee(num-1);
 }
}

在編寫遞歸函數(shù)時(shí),使用arguments.callee總比使用函數(shù)名更保險(xiǎn),因?yàn)樗梢源_保無(wú)論怎么調(diào)用函數(shù)都不會(huì)出問(wèn)題.

但在嚴(yán)格模式下,不能通過(guò)腳本訪問(wèn)arguments.callee.

不過(guò)可以使用函數(shù)表達(dá)式來(lái)達(dá)成相同的結(jié)果.

var factorial=(function f(num){
 if(num<=1){
  return 1;
 }else{
  return num*f(num-1);
 }
});
console.log(factorial(4));//24

7.2 閉包

閉包是指有權(quán)訪問(wèn)另一個(gè)函數(shù)作用域中的變量的函數(shù).創(chuàng)建閉包的常見(jiàn)方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù).

function creatComparisonFunction(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;
  }
 };
}

加粗的兩行代碼是內(nèi)部函數(shù)(一個(gè)匿名函數(shù))中的代碼,這兩行代碼訪問(wèn)了外部函數(shù)中的變量propertyName.即使這個(gè)內(nèi)部函數(shù)被返回了,而且是在其他地方被調(diào)用了,但它仍然可以訪問(wèn)變量propertyName.之所以還能夠訪問(wèn)這個(gè)變量,是因?yàn)閮?nèi)部函數(shù)的作用域鏈中包含creatComparisonFunction()的作用域.

當(dāng)某個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境(execution context)及相應(yīng)的作用域鏈.然后,使用arguments和其他命名參數(shù)的值來(lái)初始化函數(shù)的活動(dòng)對(duì)象(activation object).但在作用域鏈中,外部函數(shù)的活動(dòng)對(duì)象始終處于第二位,外部函數(shù)的外部函數(shù)的活動(dòng)對(duì)象處于第三位,....直至作為作用域鏈終點(diǎn)的全局執(zhí)行環(huán)境.

在函數(shù)執(zhí)行過(guò)程中,為讀取和寫入變量的值,就需要姑作用域鏈中查找變量.

function compare(value1,value2){
  if(value1<value2){
   return -1;
  }else if(value1>value2){
   return 1;
  }else{
   return 0;
  }
 }
 var result=compare(5,10)
 console.log(result)//-1

以上代碼先定義了compare()函數(shù),然后又在全局作用域中調(diào)用了它.當(dāng)調(diào)用compare()時(shí),會(huì)創(chuàng)建一個(gè)包含arguments,value1,value2的活動(dòng)對(duì)象.全局執(zhí)行環(huán)境的變量對(duì)象(包含result和compare)在compare()執(zhí)行環(huán)境的作用域鏈中則處于第二位

后臺(tái)的每個(gè)執(zhí)行環(huán)境都有一個(gè)表示變量的對(duì)象--變量對(duì)象.全局環(huán)境的變量對(duì)象始終存在,而像compare()函數(shù)這樣的局部環(huán)境的變量對(duì)象,則只在函數(shù)執(zhí)行的過(guò)程中存在.在創(chuàng)建compare()函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)預(yù)先包含全局變量對(duì)象的作用域鏈,這個(gè)作用域鏈被保存在內(nèi)部的[[Scope]]屬性中.當(dāng)調(diào)用compare()函數(shù)時(shí),會(huì)為函數(shù)創(chuàng)建一個(gè)執(zhí)行環(huán)境,然后通過(guò)復(fù)制函數(shù)的[[Scope]]屬性中的對(duì)象構(gòu)建起執(zhí)行環(huán)境的作用域鏈.此后,又有一個(gè)活動(dòng)對(duì)象(在此作為變量對(duì)象使用)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域鏈的前端.

作用域鏈本質(zhì)上是一個(gè)指向變量對(duì)象的指針列表,它只引用但不實(shí)際包含變量對(duì)象.

無(wú)論什么時(shí)候在函數(shù)中訪問(wèn)一個(gè)變量時(shí),就會(huì)從作用域鏈中搜索具有相應(yīng)名字的變量.一般來(lái)講,當(dāng)函數(shù)執(zhí)行完畢后,局部活動(dòng)對(duì)象就會(huì)被銷毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對(duì)象).但是,閉包的情況又有所不同.

在另一個(gè)函數(shù)內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)(即外部函數(shù))的活動(dòng)對(duì)象添加到它的作用域中.

var compare=creatComparisonFunction("name");
var result=comapre({name:"Nicholas"},{name:"Greg"});

下圖展示了上面代碼代碼執(zhí)行時(shí),包含函數(shù)與內(nèi)部匿名函數(shù)的作用域.


當(dāng)createComparisonFunction()函數(shù)返回后,其執(zhí)行環(huán)境的作用域會(huì)被銷毀,但它的活動(dòng)對(duì)象仍然會(huì)留在內(nèi)存中;直到匿名函數(shù)被銷毀后,createComparisonFunction()的活動(dòng)對(duì)象都會(huì)被銷毀.

//創(chuàng)建函數(shù)
 var compare=creatComparisonFunction("name");
 //調(diào)用函數(shù)
 var result=comapre({name:"Nicholas"},{name:"Greg"});
 //解除對(duì)匿名函數(shù)的引用(以便釋放內(nèi)存)
 compareNames=null;

通過(guò)將compareNames設(shè)置為等于null解除該函數(shù)的引用,就等于通知垃圾回收例程將其清除.隨著匿名函數(shù)函數(shù)的作用域鏈被銷毀,其他作用域(除了全局作用域)也都可以安全地銷毀了.

由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存.過(guò)度使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過(guò)多,慎重使用閉包.

7.2.1 閉包和變量

作用域鏈的這種配置的機(jī)制引出了一個(gè)值得注意的副作用,即閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值.

閉包里所保存的是整個(gè)變量對(duì)象,而不是某個(gè)特殊的變量.

function createFunctions(){
 var result=new Array();

 for(var i=0;i<10;i++){
  result[i]=function(){
   return i;
  };
 }
 return result;
}

上面代碼里這個(gè)函數(shù)會(huì)返回一個(gè)函數(shù)數(shù)組.表面上看,似乎每個(gè)函數(shù)都應(yīng)該返回自己的索引值,但實(shí)際上,每個(gè)函數(shù)都返回10.因?yàn)槊總€(gè)函數(shù)的作用域鏈中都保存著createFunctions()函數(shù)的活動(dòng)對(duì)象,所以它們引用的都是同一個(gè)變量i.當(dāng)createFunction()函數(shù)返回后,變量i的值是10,此時(shí)每個(gè)函數(shù)都引用著保存變量i的同一個(gè)變量對(duì)象,所以在每個(gè)函數(shù)內(nèi)部i的值都是10.

但是,我們可以通過(guò)創(chuàng)建另一個(gè)匿名函數(shù)強(qiáng)制讓閉包的行為符合預(yù)期.

function createFunctions(){
 var result=new Array();

 for(var i=0;i<10;i++){
  result[i]=function(num){
   return function(){
    return num;
   }
  }(i);
 }
 return result;
}

重寫之后,每個(gè)函數(shù)就會(huì)返回各自不同的索引值了.在這個(gè)版本中,我們沒(méi)有直接把閉包賦值給數(shù)組,而是定義了一個(gè)匿名函數(shù),并將立即執(zhí)行匿名函數(shù)的結(jié)果賦給數(shù)組.這里的匿名函數(shù)有一個(gè)參數(shù)num,也就是最終的函數(shù)要返回的值.在調(diào)用每個(gè)匿名函數(shù)時(shí),我們傳入了變量i.由于函數(shù)參數(shù)是按值傳遞的,所以就會(huì)將變量i的當(dāng)前值復(fù)制給參數(shù)num.而在這個(gè)匿名函數(shù)內(nèi)部,又創(chuàng)建并返回了一個(gè)訪問(wèn)num的閉包.這個(gè)一來(lái),result數(shù)組中的每個(gè)函數(shù)都有自己num變量的一個(gè)副本,因此就可以返回各自不同的數(shù)值了.

7.2.2 關(guān)于this對(duì)象

this對(duì)象是在運(yùn)行時(shí)基本函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象的方法調(diào)用時(shí),this等于那個(gè)對(duì)象.不過(guò),匿名函數(shù)的執(zhí)行環(huán)境具有全局性,因此其this對(duì)象通常指向window.

var name="the window";
var object={
 name:"my object",

 getNameFunc:function(){
  return function(){
   return this.name;
  };
 }
};
alert(object.getNameFunc()());//the window(在非嚴(yán)格模式下)

每個(gè)函數(shù)在被調(diào)用時(shí)都會(huì)自動(dòng)取得兩個(gè)特殊變量:this和arguments.內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到其活動(dòng)對(duì)象為止,因此永遠(yuǎn)不可能直接訪問(wèn)外部函數(shù)中的這兩個(gè)變量.

不過(guò),把外部作用域中的this對(duì)象保存在一個(gè)閉包能夠訪問(wèn)到的變量里,就可以讓閉包訪問(wèn)該對(duì)象了.

var name="the window";
var object={
 name:"my object",

 getNameFunc:function(){
  var that=this;
  return function(){
   return that.name;
  };
 }
};
alert(object.getNameFunc()());//my object

在定義匿名函數(shù)之前,我們把this對(duì)象賦值給了一個(gè)名叫that的變量.而在定義了閉包之后,閉包也可以訪問(wèn)這個(gè)變量,因?yàn)樗俏覀冊(cè)诎瘮?shù)中特意聲明的一個(gè)變量.即使在函數(shù)返回之后,that也仍然引用著object,所以調(diào)用object.getNameFunc()()就返回了my object.

注意:this和arguments也存在同樣的問(wèn)題.如果想訪問(wèn)作用域中的arguments對(duì)象,必須將對(duì)該對(duì)象的引用保存到另一個(gè)閉包能夠訪問(wèn)的變量中.

var name="the window";

var object={
 name:"my object",

 getName:function(){
  return this.name;
 }
};
console.log(object.getName());//my object
console.log((object.getName)());//my object
console.log((object.getName=object.getName)());//the window

最后一行代碼先執(zhí)行了一條賦值語(yǔ)句,然后再調(diào)用賦值后的結(jié)果.因?yàn)檫@個(gè)賦值表達(dá)式的值是函數(shù)本身,所以this的值不能得到維持,結(jié)果就返回了"this window".

7.2.3 內(nèi)存泄露

由于IE9之前的版本對(duì)JScript對(duì)象和COM對(duì)象使用不同的垃圾收集例程,因此閉包在IE的這些版本中會(huì)導(dǎo)致一些特殊的問(wèn)題.具體來(lái)說(shuō),如果閉包的作用域鏈中保存著一個(gè)HTML元素,那么就意味著該元素將無(wú)法被銷毀.

function assignHandlet(){
 var element=document.getElementById("someElement");
 element.onclick=function(){
  alert(element.id);
 };
}

以上代碼創(chuàng)建了一個(gè)作為element元素事件處理程序的閉包,而這個(gè)閉包則又創(chuàng)建了一個(gè)循環(huán)引用.由于匿名函數(shù)保存了一個(gè)對(duì)assignHandler()的活動(dòng)對(duì)象的引用,因此應(yīng)付導(dǎo)致無(wú)法減少element的引用數(shù).只要匿名函數(shù)存在,element的引用數(shù)至少也是1,因此它所占用的內(nèi)存就永遠(yuǎn)不會(huì)被回收.不過(guò)這個(gè)問(wèn)題可以通過(guò)稍微改寫一下代碼來(lái)解決.

function assignHandlet(){
 var element=document.getElementById("someElement");
 var id=element.id;

 element.onclick=function(){
  alert(id);
 };
 element=null;
}

上面代碼中,通過(guò)把element.id的一個(gè)副本保存在一個(gè)變量中,并且在閉包中引用該變量消除了循環(huán)引用.

腳本之家友情提醒大家:閉包會(huì)引用包含函數(shù)的整個(gè)活動(dòng)對(duì)象,而其中包含著element.即使閉包不直接引用element,包含函數(shù)的活動(dòng)對(duì)象也仍然會(huì)保存一個(gè)引用.因此,有必要把element變量設(shè)置為null.這樣就能夠解除對(duì)DOM對(duì)象的引用,順利地減少其引用數(shù),確保正常回收其占用的內(nèi)存.

下面給大家介紹下函數(shù)表達(dá)式。

在JavaScript 編程中,函數(shù)表達(dá)式是一種非常有用的技術(shù)。使用函數(shù)表達(dá)式可以無(wú)須對(duì)函數(shù)命名,從而實(shí)現(xiàn)動(dòng)態(tài)編程。匿名函數(shù),也稱為拉姆達(dá)函數(shù),是一種使用JavaScript 函數(shù)的強(qiáng)大方式。以下總結(jié)了函數(shù)表達(dá)式的特點(diǎn)。

函數(shù)表達(dá)式不同于函數(shù)聲明。函數(shù)聲明要求有名字,但函數(shù)表達(dá)式不需要。沒(méi)有名字的函數(shù)表達(dá)式也叫做匿名函數(shù)。

在無(wú)法確定如何引用函數(shù)的情況下,遞歸函數(shù)就會(huì)變得比較復(fù)雜;遞歸函數(shù)應(yīng)該始終使用arguments.callee 來(lái)遞歸地調(diào)用自身,不要使用函數(shù)名——函數(shù)名可能會(huì)發(fā)生變化。

當(dāng)在函數(shù)內(nèi)部定義了其他函數(shù)時(shí),就創(chuàng)建了閉包。閉包有權(quán)訪問(wèn)包含函數(shù)內(nèi)部的所有變量,原理
如下。

在后臺(tái)執(zhí)行環(huán)境中,閉包的作用域鏈包含著它自己的作用域、包含函數(shù)的作用域和全局作用域。通常,函數(shù)的作用域及其所有變量都會(huì)在函數(shù)執(zhí)行結(jié)束后被銷毀。

但是,當(dāng)函數(shù)返回了一個(gè)閉包時(shí),這個(gè)函數(shù)的作用域?qū)?huì)一直在內(nèi)存中保存到閉包不存在為止。

使用閉包可以在JavaScript 中模仿塊級(jí)作用域(JavaScript 本身沒(méi)有塊級(jí)作用域的概念),要點(diǎn)如下。

創(chuàng)建并立即調(diào)用一個(gè)函數(shù),這樣既可以執(zhí)行其中的代碼,又不會(huì)在內(nèi)存中留下對(duì)該函數(shù)的引用。
結(jié)果就是函數(shù)內(nèi)部的所有變量都會(huì)被立即銷毀——除非將某些變量賦值給了包含作用域(即外部作用域)中的變量。
閉包還可以用于在對(duì)象中創(chuàng)建私有變量,相關(guān)概念和要點(diǎn)如下。即使JavaScript 中沒(méi)有正式的私有對(duì)象屬性的概念,但可以使閉包來(lái)實(shí)現(xiàn)公有方法,而通過(guò)公有方法可以訪問(wèn)在包含作用域中定義的變量。
有權(quán)訪問(wèn)私有變量的公有方法叫做特權(quán)方法。

可以使用構(gòu)造函數(shù)模式、原型模式來(lái)實(shí)現(xiàn)自定義類型的特權(quán)方法,也可以使用模塊模式、增強(qiáng)的模塊模式來(lái)實(shí)現(xiàn)單例的特權(quán)方法。

JavaScript 中的函數(shù)表達(dá)式和閉包都是極其有用的特性,利用它們可以實(shí)現(xiàn)很多功能。不過(guò),因?yàn)閯?chuàng)建閉包必須維護(hù)額外的作用域,所以過(guò)度使用它們可能會(huì)占用大量?jī)?nèi)存。

相關(guān)文章

最新評(píng)論