函數(shù) 閉包 作用域鏈

函數(shù)申明:

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

函數(shù)表達式:

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

遞歸

function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return num*factorial(num-1);
    }
}
function factorial(num){
    if(num<=1){
        return 1;
    }else{
        return arguments.callee(num-1)*num;
    }
}//這種方式在嚴(yán)格模式下會報錯
//最佳方法
var factorial = (function f(num){
    if(num<=1){
        return 1;
    }else{
        return num*f(num-1);
    }
});

閉包

閉包是有權(quán)訪問另一個函數(shù)作用域中的變量函數(shù)沉噩。

function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1<value2){
            return -1;
        }else{
            return 0;
        }
    };
}

即使函數(shù)中的匿名函數(shù)被返回了昆禽,而且在其他地方被調(diào)用了择卦,但它仍然可以訪問變量propertyName。這是因為內(nèi)部函數(shù)的作用域鏈中包含了createComparisonFunction()的作用域.

作用域鏈

  • 當(dāng)某個函數(shù)第一次被調(diào)用時祈噪,會創(chuàng)建一個 執(zhí)行環(huán)境 及相應(yīng)的 作用域鏈尚辑,并把作用域鏈賦值給一個特殊的內(nèi)部屬性(執(zhí)行環(huán)境中的[[Scopr]])。然后遂填,使用this澈蝙、arguments和其他命名參數(shù)的值來初始化函數(shù)的 活動對象。外部函數(shù)的活動對象始終處于第二位礁击,外部函數(shù)的外部函數(shù)的活動對象處于第三位.....
function compare(value1, value2){
    if(value1 < value2){
        return -1;
    } else{
        return 1;
    }
}

執(zhí)行環(huán)境及相應(yīng)的作用域鏈


image
  • 后臺的每個 執(zhí)行環(huán)境 都有一個表示變量的對象—— 變量對象逗载。全局環(huán)境的變量對象始終存在厉斟,而想compare()函數(shù)這樣的局部環(huán)境的變量對象,只有在函數(shù)執(zhí)行的過程中存在(活動對象)擦秽。

  • 在創(chuàng)建compare()函數(shù)時,會創(chuàng)建一個預(yù)先包含全局變量對象的作用域鏈缩搅,這個作用域鏈保存在內(nèi)部的[[Scope]]屬性中。當(dāng)調(diào)用compare()時究飞,會為函數(shù)創(chuàng)建一個執(zhí)行環(huán)境堂鲤,然后通過復(fù)制函數(shù)的[[Scope]]屬性中的對象構(gòu)建執(zhí)行環(huán)境的作用域鏈。此后,又有一個活動對象(變量對象)被創(chuàng)建并被推入執(zhí)行環(huán)境作用域鏈的前端丝蹭。

  • 作用域鏈的前端奔穿,始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對象。如果這個環(huán)境是函數(shù)贱田,則將其活動對象作為變量對象。 (活動對象就是作用域鏈上正在被執(zhí)行和引用的變量對象)


function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        if(value1<value2){
            return -1;
        }else{
            return 0;
        }
    };
}
Markdown

在匿名函數(shù)從createComparisonFunction()中被返回后,它的作用域鏈被初始化為包含createComparisonFunction()函數(shù)的 活動對象全局變量對象耗拓。這樣,匿名函數(shù)就可以訪問在createComparisonFunction()中定義的所有變量樟插。當(dāng)createComparisonFunction()函數(shù)返回后竿刁,其執(zhí)行環(huán)境的作用域鏈被銷毀,但它的活動對象仍然會留在內(nèi)存中鸵熟,直到匿名函數(shù)被銷毀后负甸,createComparisonFunction()的活動對象才會被銷毀齿桃。

閉包與變量

閉包只能取得包含函數(shù)中任何變量的最后一個值煮盼。

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

這個函數(shù)會返回一個函數(shù)數(shù)組僵控,每個函數(shù)都返回10。因為每個函數(shù)的作用域鏈中都保存著createFuntion函數(shù)的活動對象报破,并返回了createFunction中變量i的值充易,由于經(jīng)過for循環(huán)i的值變?yōu)榱?0,所以result中每個函數(shù)都只會返回10盹靴。

通過創(chuàng)建另一個匿名函數(shù)強制讓閉包的行為符合預(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;
}

由于函數(shù)參數(shù)傳遞是按值傳遞的稿静,所以就會將變量i的當(dāng)前值復(fù)制給參數(shù)num,而在這個匿名函數(shù)內(nèi)部改备,又創(chuàng)建并返回了一個訪問num的閉包。這樣一來悬钳,result數(shù)組中的每函數(shù)都有自己num變量的一個副本,因此就可以返回各自不同的數(shù)值了毙驯。

this對象

var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc: function(){
    return function(){
      return this.name;
    };
  }
};
alert(object.getNameFunc()()); // The Window
//object.getNameFunc()返回的是一個匿名函數(shù)灾测,想要調(diào)用該匿名函數(shù)還需要加一個()

出現(xiàn)這樣的現(xiàn)象的原因分析(自己理解):
在創(chuàng)建匿名函數(shù)時媳搪,會創(chuàng)建一個預(yù)先包含全局變量對象的作用域鏈(包括this,此時this為GLobal對象)秦爆,這個作用域鏈被保存在內(nèi)部的[[Scope]]屬性中。當(dāng)調(diào)用匿名函數(shù)時爸吮,會為函數(shù)創(chuàng)建一個執(zhí)行環(huán)境,然后通過復(fù)制函數(shù)的[[Scope]]屬性中的對象構(gòu)建起執(zhí)行環(huán)境的作用域鏈锰霜。因此該例中桐早,匿名函數(shù)中的this仍然為Global對象。
在javascript中友存,函數(shù)的調(diào)用一共有4種方式:

  • Function Invocation Pattern
    諸如foo()的調(diào)用形式被稱為Function Invocation Pattern陶衅,是函數(shù)最直接的使用形式,注意這里的foo是作為單獨的變量出現(xiàn)膨俐,而不是屬性奕巍。
    在這種模式下儒士,foo函數(shù)體中的this永遠(yuǎn)為Global對象,在瀏覽器中就是window對象诅福。
  • Method Invocation Pattern
    諸如foo.bar()的調(diào)用形式被稱為Method Invocation Pattern拖叙,注意其特點是被調(diào)用的函數(shù)作為一個對象的屬性出現(xiàn),必然會有“.”或者“[]”這樣的關(guān)鍵符號咖气。
    在這種模式下挖滤,bar函數(shù)體中的this永遠(yuǎn)為“.”或“[”前的那個對象,如上例中就一定是foo對象伶唯。
  • Constructor Pattern
    new foo()這種形式的調(diào)用被稱為Constructor Pattern惧盹,其關(guān)鍵字new就很能說明問題瞪讼,非常容易識別粹断。
    在這種模式下,foo函數(shù)內(nèi)部的this永遠(yuǎn)是new foo()返回的對象背亥。
  • Apply Pattern
    foo.call(this悬赏,Object)foo.apply(this,Object)的形式被稱為Apply Pattern盾戴,使用了內(nèi)置的callapply函數(shù)兵多。
    在這種模式下,callapply的第一個參數(shù)就是foo函數(shù)體內(nèi)的this衅斩,如果thisObject是nullundefined怠褐,那么會變成Global對象。
// 把外部作用域中的this對象保存在一個閉包能夠訪問到的變量里奠涌,就可以讓閉包訪問該對象了
var name = "The Window";
var object = {
  name : "My Object",
  getNameFunc: function(){
    var that = this;
    return function(){
      return that.name;
    };
  }
};
alert(object.getNameFunc()()); // The Object

模仿塊級作用域

(function(){
  //這里是塊級作用域
}) ();

以上代碼定義并立即調(diào)用了一個匿名函數(shù)磷杏。將函數(shù)申明包含在一對圓括號中极祸,表示它實際上是一個 函數(shù)表達式 。而緊隨其后的另一對圓括號會立即調(diào)用這個函數(shù)遥金。

私有變量

  • JavaScript中沒有私有成員的概念汰规;所有的對象屬性都是公有的。但是有私有變量的概念溜哮。私有變量包括函數(shù)的參數(shù)、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)餐茵。
  • 如果在函數(shù)內(nèi)部創(chuàng)建一個閉包,那么閉包通過自己的作用域鏈也可以訪問這些變量锣笨,利用這點道批,可以創(chuàng)建用于訪問私有變量的公有方法。
/* demo01 */
function MyObject(){
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  this.publicMethod = function(){
    privateVariable++;
    return privateFunction();
  };
}

var object = new MyObject();
object.publicMethod(); //false

如果需要構(gòu)建多個實例椭岩,則需為每個實例創(chuàng)建同樣的方法

靜態(tài)私有變量

(function(){
  var privateVariable = 10;
  function privateFUncion(){
    return false;
  }

  MyObject = function(){

  };

  MyObject.prototype.publicMethod = function(){
    privateVariable++;
    return privateFunction();
  }
})
// 使用
var object = new MyObject();

注意 :定義構(gòu)造函數(shù)時璃赡,沒有使用函數(shù)生命,而是使用了函數(shù)表達式碉考。函數(shù)聲明只能創(chuàng)建局部函數(shù)。出于同樣的原因锌仅,在聲明MyObject時也沒有使用var關(guān)鍵字良蒸。** 記住 ** : ** 初始話未經(jīng)聲明的變量伍玖,總是會創(chuàng)建一個全局變量 ** 窍箍。因此MyObject就成為一個全局變量,能夠在私有作用域之外被訪問椰棘。 ** 靜態(tài)私有變量 ** privateVariable時被所有實例共享的邪狞,它可以任何實例修改(通過publicMethod方法)

模塊模式

模塊模式通過為單例添加私有變量和特權(quán)方法能夠使其得到增強

var singleton = function(){
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  // 返回一個對象
  return {
    publicProperty: true;
    publicMethod: function(){
      privateVariable++;
      return privateFunction();
    }
  };
}();
singleton.publicProperty;
singleton.publicMethod();

這個模塊模式使用了一個返回對象的匿名函數(shù)。

增強的模塊模式

返回特定類型的對象

var singleton = function(){
  var privateVariable = 10;
  function privateFunction(){
    return false;
  }
  // 可以返回特定類型的對象
  var object = new CustomType();
  object.publicProperty = true;
  object.publicMethod = function(){
    privateVariable++;
    return privateFunction();
  }
}();
singleton.publicProperty;
singleton.publicMethod();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末巨朦,一起剝皮案震驚了整個濱河市糊啡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌棚蓄,老刑警劉巖梭依,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異邪蛔,居然都是意外死亡扎狱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門匠抗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來污抬,“玉大人印机,你說我怎么就攤上這事∩淙” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵竣灌,是天一觀的道長初嘹。 經(jīng)常有香客問我沮趣,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任甸箱,我火速辦了婚禮芍殖,結(jié)果婚禮上谴蔑,老公的妹妹穿的比我還像新娘。我一直安慰自己窃躲,他們只是感情好钦睡,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著洒琢,像睡著了一般褐桌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呛踊,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天啦撮,我揣著相機與錄音,去河邊找鬼愉择。 笑死聘鳞,一個胖子當(dāng)著我的面吹牛要拂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搏嗡,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼采盒!你這毒婦竟也來了旧乞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤磅氨,失蹤者是張志新(化名)和其女友劉穎尺栖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烦租,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡延赌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了叉橱。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挫以。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窃祝,死狀恐怖掐松,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情粪小,我是刑警寧澤大磺,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站探膊,受9級特大地震影響量没,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜突想,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一殴蹄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猾担,春花似錦袭灯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至工腋,卻和暖如春姨丈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背擅腰。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工蟋恬, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趁冈。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓歼争,卻偏偏與公主長得像拜马,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沐绒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容