深入Javascript函數(shù)、遞歸與閉包(執(zhí)行環(huán)境、變量對(duì)象與作用域鏈)使用詳解
函數(shù)表達(dá)式
1、JavaScript中定義函數(shù)有2鐘方法:
1-1.函數(shù)聲明:
function funcName(arg1,arg2,arg3){
//函數(shù)體
}
?、賜ame屬性:可讀取函數(shù)名。非標(biāo)準(zhǔn),瀏覽器支持:FF、Chrome、safari、Opera。
?、诤瘮?shù)聲明提升:指執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明。即函數(shù)調(diào)用可置于函數(shù)聲明之前。
1-2.函數(shù)表達(dá)式:
var funcName = function(arg1,arg2,arg3){
//函數(shù)體
};
?、倌涿瘮?shù)(anonymous function,或拉姆達(dá)函數(shù)):function關(guān)鍵字后無標(biāo)識(shí)符,name屬性值為空字符串。在把函數(shù)當(dāng)成值使用時(shí),都可用匿名函數(shù)。
?、陬愃破渌磉_(dá)式,函數(shù)表達(dá)式使用前需先賦值,故不存在"函數(shù)聲明提升"那樣的作用。
?、跡CMAScript中的無效函數(shù)語(yǔ)法:
if判斷中的重復(fù)函數(shù)聲明
if(condition){
function sayHi(){
alert("Hi!");
}
} else {
function sayHi(){
alert("Yo!");
}
}
瀏覽器JavaScript引擎修正錯(cuò)誤差異:大多瀏覽器會(huì)返回第二個(gè)聲明,忽略condition;FF則會(huì)在condition為true時(shí)返回第一個(gè)聲明。
使用函數(shù)表達(dá)式可解決并實(shí)現(xiàn):
if判斷 函數(shù)表達(dá)式
var sayHi;
if(condition){
sayHi = function(){
alert("Hi!");
}
} else {
sayHi = function(){
alert("Yo!");
}
}
2、遞歸
遞歸函數(shù),是在一個(gè)函數(shù)中通過名字調(diào)用自身的情況下構(gòu)成的。
function factorial(num){ //一個(gè)經(jīng)典的遞歸階乘函數(shù)
if (num <= 1){
return 1;
} else {
return num * factorial(num-1);
}
}
①若使用下列代碼調(diào)用該函數(shù),會(huì)出錯(cuò):
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4));
將factorial()函數(shù)保存到變量anotherFactorial中后,將factorial變量設(shè)為null后不再引用函數(shù),而anotherFactorial(4)中要執(zhí)行factorial()函數(shù),故出錯(cuò)。
使用argument.callee(指向正在執(zhí)行的函數(shù)的指針)可解決:
解決方案
function factorial(num){
if (num <= 1){
return 1;
} else {
return num * arguments.callee(num-1);
}
}
var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //24
在非嚴(yán)格模式,使用遞歸函數(shù)時(shí),用argument.callee代替函數(shù)名更保險(xiǎn)
在嚴(yán)格模式下,使用argument.callee會(huì)出錯(cuò),可用函數(shù)表達(dá)式 代替 函數(shù)聲明:
函數(shù)表達(dá)式代替函數(shù)聲明
var factorial = function f(num){
if (num <= 1){
return 1;
} else {
return num * f(num-1);
}
}
4、閉包
指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。(常見形式為函數(shù)嵌套)
function wai(pro){
return function(obj1,obj2){
var val1 = obj1[pro];
var val2 = obj2[pro];
if(val1<val2){
return -1;
}else if(val1>val2){
return 1;
}else{
return 0;
};
}
}
return匿名函數(shù)時(shí),匿名函數(shù)的作用域鏈初始化為包含函數(shù)的活動(dòng)對(duì)象和全局變量對(duì)象。即匿名函數(shù)包含wai()函數(shù)的作用域。
每個(gè)函數(shù)被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境、一個(gè)變量對(duì)象 及 相應(yīng)的作用域鏈。
4-1.執(zhí)行環(huán)境 及 作用域
執(zhí)行環(huán)境execution context簡(jiǎn)稱環(huán)境,定義了變量和函數(shù)有權(quán)訪問的其他數(shù)據(jù),并決定他們的各自行為。
?、倜總€(gè)執(zhí)行環(huán)境都有一個(gè)變量對(duì)象variable object,保存環(huán)境定義的所有變量和函數(shù)。該對(duì)象無法編碼訪問,但解析器在處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)使用它。
全局變量對(duì)象是最外圍的一個(gè)執(zhí)行環(huán)境。在Web瀏覽器中被認(rèn)為是window對(duì)象,故所有全局對(duì)象和函數(shù)都是window對(duì)象的屬性和方法創(chuàng)建的。
執(zhí)行環(huán)境中的代碼執(zhí)行完后,該環(huán)境就被銷毀,保存其中的變量和函數(shù)定義也隨之銷毀。
?、诖a在環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對(duì)象的一個(gè)作用域鏈scope chain,用于保證對(duì)執(zhí)行環(huán)境有權(quán)訪問的所有變量和函數(shù)的有序訪問。
作用域鏈前端,始終是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象。當(dāng)該環(huán)境為函數(shù)時(shí),會(huì)將活動(dòng)對(duì)象作為變量對(duì)象。
活動(dòng)對(duì)象最開始只包含一個(gè)變量,即argumnt對(duì)象。
作用域鏈中的下一個(gè)變量對(duì)象來自包含環(huán)境,而下一個(gè)變量對(duì)象來自下一個(gè)包含環(huán)境,直至延續(xù)到全局執(zhí)行環(huán)境。
?、蹣?biāo)識(shí)符解析:從前段開始,沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過程。【找不到通常會(huì)導(dǎo)致錯(cuò)誤發(fā)生】
4-2.函數(shù)創(chuàng)建、執(zhí)行時(shí):
function compare(val1,val2){
if(val1<val2){
return -1;
}else if(val1>val2){
return 1;
}else{
return 0;
};
}
var result = compare(5 , 10);
①創(chuàng)建函數(shù)compare()時(shí),會(huì)創(chuàng)建一個(gè)預(yù)先包含全局變量對(duì)象的作用域鏈,并保存在內(nèi)部[[scope]]屬性中。
?、诰植亢瘮?shù)compare()的變量對(duì)象,只在函數(shù)執(zhí)行的過程中存在。
當(dāng)調(diào)函數(shù)時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境,再通過復(fù)制函數(shù)的[[scope]]屬性中的對(duì)象 構(gòu)建起執(zhí)行環(huán)境的作用域鏈。
③第一次調(diào)用函數(shù)時(shí),如compare(),會(huì)創(chuàng)建一個(gè)包含this、argument、val1 和 val2的活動(dòng)對(duì)象。
④全局執(zhí)行環(huán)境的變量對(duì)象(包括this、result、compare)在compare()執(zhí)行環(huán)境的作用域鏈中處于第二位。
⑤作用域鏈 本質(zhì)是一個(gè)指向變量對(duì)象的指針列表,只引用但不實(shí)際包含變量對(duì)象。
⑥無論什么時(shí)候在函數(shù)中訪問一個(gè)變量,都會(huì)行作用域鏈中搜索具有相應(yīng)名字的變量。
4-3.閉包的作用域鏈
在另外一個(gè)函數(shù)內(nèi)部定義的函數(shù)會(huì)將包含函數(shù)的活動(dòng)對(duì)象添加到它的作用域鏈中。
?、賹⒑瘮?shù)對(duì)象賦值null,等于通知垃圾回收例程將其清除,隨著函數(shù)作用域鏈被銷毀,其作用域鏈(不除了全局作用域)也會(huì)被安全銷毀。
?、谟捎陂]包會(huì)攜帶包含函數(shù)的作用域,所以會(huì)比其他函數(shù)占用更多內(nèi)存。
4-4.閉包與變量
作用域鏈的一個(gè)副作用:閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值。
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(){
return i;
};
}
return result;
}
?、賑reateFunctions()函數(shù),將10個(gè)閉包賦值給數(shù)組result,再返回result數(shù)組。每個(gè)閉包都返回自己的索引,但實(shí)際上都返回10。
因?yàn)槊總€(gè)函數(shù)(閉包)的作用域鏈中都保存著createFunctions()函數(shù)的活動(dòng)對(duì)象,所以它們引用的是同一個(gè)變量i,當(dāng)createFunctions函數(shù)執(zhí)行完后i的值10,故閉包中的i也都為10。
?、诮鉀Q辦法,不使用閉包,創(chuàng)建一個(gè)匿名函數(shù),將i值賦值給其參數(shù):
function createFunctions(){
var result = new Array();
for (var i=0; i < 10; i++){
result[i] = function(num){
return function(){
return num;
};
}(i);
}
return result;
}
創(chuàng)建一個(gè)每次循環(huán)都會(huì)執(zhí)行一次的匿名函數(shù):將每次循環(huán)時(shí)包圍函數(shù)的i值作為參數(shù),存入匿名函數(shù)中。因?yàn)楹瘮?shù)參數(shù)是按值傳遞的,而非引用,所以每個(gè)匿名函數(shù)中的num值 都為每此循環(huán)時(shí)i值的一個(gè)副本。
4-5.this對(duì)象
this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的。
在全局函數(shù)中,this等于window;當(dāng)函數(shù)被某對(duì)象調(diào)用時(shí),this為該對(duì)象。
匿名函數(shù)的執(zhí)行環(huán)境有全局性,其this對(duì)象通常指window。通過call()或spply()改變函數(shù)執(zhí)行環(huán)境時(shí),this指向其對(duì)象。
①每個(gè)函數(shù)在被調(diào)用時(shí),都會(huì)自動(dòng)取得兩個(gè)特殊變量:this和argument。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí),只會(huì)搜索到期活動(dòng)對(duì)象為止,永遠(yuǎn)不可能訪問外部函數(shù)的這兩個(gè)變量。
不過將外部作用域的this對(duì)象保存在一個(gè)閉包能訪問的變量里,就可讓閉包訪問該對(duì)象。
閉包 訪問外部函數(shù)的this對(duì)象
var name = "The Window";
var object = {
name : "My Object",
getNameFunc : function(){
var that = this;
return function(){
return that.name;
};
}
};
alert(object.getNameFunc()()); //"MyObject"
包圍函數(shù)的argument對(duì)象 也可通過此方法被閉包訪問。
5、函數(shù)聲明 轉(zhuǎn)換為 函數(shù)表達(dá)式
JavaScript將function關(guān)鍵字昨晚函數(shù)聲明的開始,但函數(shù)聲明后面不能跟圓括號(hào),所以function(){......}();會(huì)出錯(cuò)。
要將函數(shù)聲明轉(zhuǎn)換為函數(shù)表達(dá)式,需為函數(shù)聲明加一對(duì)圓括號(hào):
(function(){
//塊級(jí)作用域
})();
相關(guān)文章
css配合JavaScript實(shí)現(xiàn)tab標(biāo)簽切換效果
本篇文章我們給大家分享一個(gè)關(guān)于CSS配合JS實(shí)現(xiàn)的TAB標(biāo)簽切換效果,需要的朋友們可以測(cè)試下。2018-10-10javascript之Array 數(shù)組對(duì)象詳解
本文主要是對(duì)javascript之Array 數(shù)組對(duì)象的詳解 ,比較詳細(xì),希望能給大家做一個(gè)參考。2016-06-06javascript Split方法,indexOf方法、lastIndexOf 方法和substring 方法
以下程序例子實(shí)現(xiàn)了split和整數(shù)字符串互轉(zhuǎn)的用法2009-03-03JavaScript中操作字符串之localeCompare()方法的使用
這篇文章主要介紹了JavaScript中操作字符串之localeCompare()方法的使用,是JS入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-06-06深入理解JavaScript系列(50):Function模式(下篇)
這篇文章主要介紹了深入理解JavaScript系列(50):Function模式(下篇),本篇我們介紹的一些模式稱為初始化模式和性能模式,主要是用在初始化以及提高性能方面,一些模式之前已經(jīng)提到過,這里只是做一下總結(jié),需要的朋友可以參考下2015-03-03關(guān)于javascript事件響應(yīng)的基礎(chǔ)語(yǔ)法總結(jié)(必看篇)
下面小編就為大家?guī)硪黄P(guān)于javascript事件響應(yīng)的基礎(chǔ)語(yǔ)法總結(jié)(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,祝大家游戲愉快哦2016-12-12