JavaScript 函數(shù)表達(dá)式

定義函數(shù)有兩種方式:函數(shù)聲明和函數(shù)表達(dá)式咒吐。
Firefox但荤、Chrome早芭、Safari彼城、Opera都給函數(shù)定義了一個(gè)非標(biāo)準(zhǔn)的name屬性,通過這個(gè)屬性可以訪問到函數(shù)名退个。
函數(shù)聲明最重要的特征就是函數(shù)聲明提前(function declaration hoisting)募壕。在執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明,意味著可以把函數(shù)聲明放在調(diào)用語句的后面语盈。

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

創(chuàng)建一個(gè)函數(shù)并將它賦值給變量functionName司抱。這種情況下創(chuàng)建的函數(shù)叫匿名函數(shù)(anonymous function),有時(shí)候也叫Lambda函數(shù)。匿名函數(shù)的name屬性是空字符串黎烈。
函數(shù)表達(dá)式與其他表達(dá)式一樣习柠,必須先賦值后使用。

遞歸


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

下面的代碼可能出錯(cuò):

var anotherFactorial = factorial;
factorial = null;
alert(anotherFactorial(4)); //出錯(cuò)

可以使用arguments.callee來解決這個(gè)問題照棋。arguments.callee是一個(gè)指向正在執(zhí)行的函數(shù)的指針资溃。

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

但在嚴(yán)格模式下,不能通過腳本訪問arguments.callee烈炭,訪問這個(gè)屬性會(huì)導(dǎo)致錯(cuò)誤溶锭。不過可以使用命名函數(shù)表達(dá)式來達(dá)成相同的結(jié)果。

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

閉包


匿名函數(shù)和閉包經(jīng)撤叮混淆趴捅。閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。
創(chuàng)建閉包的常見方式霹疫,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)拱绑。

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;
        }
    };
}

閉包與變量

作用域鏈的這種配置機(jī)制引出了一個(gè)值得注意的副作用,即閉包只能取得包含函數(shù)中任何變量的最后一個(gè)值丽蝎。別忘了閉包所保存的是整個(gè)變量對(duì)象猎拨,而不是某個(gè)特殊的變量。

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

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

    return result;
}

事實(shí)上result中的每個(gè)函數(shù)都返回10。但是红省,我們可以通過創(chuàng)建另一個(gè)匿名函數(shù)強(qiáng)制讓閉包的行為符合預(yù)期额各。

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

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

    return result;
}

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

在閉包中使用this對(duì)象也可能會(huì)導(dǎo)致一些問題。我們知道吧恃,this對(duì)象是在運(yùn)行時(shí)基于函數(shù)的執(zhí)行環(huán)境綁定的:在全局函數(shù)中虾啦,this等于window,而當(dāng)函數(shù)被作為某個(gè)對(duì)象的方法調(diào)用時(shí)痕寓,this等于那個(gè)對(duì)象傲醉。不過,匿名函數(shù)的執(zhí)行環(huán)境具有全局性厂抽,因此其this對(duì)象通常指向window。但有時(shí)候由于編寫閉包的方式不同丁眼,這一點(diǎn)可能不會(huì)那么明顯筷凤。

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í),其活動(dòng)對(duì)象都會(huì)自動(dòng)取得兩個(gè)特殊變量:this和arguments苞七。內(nèi)部函數(shù)在搜索這兩個(gè)變量時(shí)藐守,只會(huì)搜索到其活動(dòng)對(duì)象為止,因此永遠(yuǎn)不可能直接訪問外部函數(shù)中的這兩個(gè)變量蹂风。不過卢厂,把外部作用域中的this對(duì)象保存在一個(gè)閉包能夠訪問到的變量里,就可以讓閉包訪問對(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"

this和arguments也存在同樣的問題慎恒。如果想訪問作用域中的arguments對(duì)象,必須將對(duì)象的引用保存到另一個(gè)閉包能夠訪問的變量中撵渡。

在幾種特殊情況下融柬,this的值可能會(huì)意外地改變。

var name = "The Window";

var object = {
    name : "My Object",
    getName : function () {
        return this.name;
    }
};

object.getName(); //"My Object"
(object.getName)(); //"My Object"
(object.getName = object.getName)(); //"The Window",在非嚴(yán)格模式下

內(nèi)存泄露

由于IE9之前的版本對(duì)JScript對(duì)象和COM對(duì)象使用不同的垃圾收集例程趋距,因此閉包在IE的這些版本中會(huì)導(dǎo)致一些特殊的問題:如果閉包的作用域鏈中保存著一個(gè)HTML元素粒氧,那么就意味著該元素將無法被銷毀。

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

這個(gè)問題可以通過修改下代碼來解決节腐。

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

模仿塊級(jí)作用域


私有變量


嚴(yán)格來講外盯,JavaScript中沒有私有成員的概念;所有對(duì)象屬性都是公有的翼雀。不過饱苟,到是有一個(gè)私有變量的概念。任何函數(shù)中定義的變量狼渊,都可以認(rèn)為是私有變量掷空。私有變量包括函數(shù)的參數(shù)、局部變量和在函數(shù)內(nèi)部定義的其他函數(shù)。

閉包可以通過自己的作用域鏈也可以訪問局部變量坦弟,利用這一點(diǎn)护锤,就可以創(chuàng)建用于訪問私有變量的公有方法。

我們把有權(quán)訪問私有變量和私有函數(shù)的公有方法稱為特權(quán)方法(privileged method)酿傍。有兩種在對(duì)象上創(chuàng)建特權(quán)方法的方式:
第一種是在構(gòu)造函數(shù)中定義特權(quán)方式烙懦。

function MyObject () {
    //私有變量和私有函數(shù)
    var privateVariable = 10;

    function privateFunction () {
        return false;
    }
    
    //特權(quán)方法
    this.publicMethod = function () {
        privateVariable++;
        return privateFunction();
    };
}

不過,在構(gòu)造函數(shù)中定義特權(quán)方法也有一個(gè)缺點(diǎn)赤炒,那就是你必須使用構(gòu)造函數(shù)模式來達(dá)到這個(gè)目的氯析。構(gòu)造函數(shù)模式的缺點(diǎn)是針對(duì)每個(gè)實(shí)例都會(huì)創(chuàng)建同樣一組新方法,而使用靜態(tài)私有變量來實(shí)現(xiàn)特權(quán)方法就可以避免這個(gè)問題莺褒。

靜態(tài)私有變量

通過在私有作用域中定義私有變量和函數(shù)掩缓,同樣也可以創(chuàng)建特權(quán)方法。

(function () {
    //私有變量和私有函數(shù)
    var privateVariable = 10;
    function privateFunction () {
        return false;
    }

    //構(gòu)造函數(shù)
    MyObject = function () {};
    
    //公有/特權(quán)方法
    MyObject.prototype.publicMethod = function () {
        privateVariable++;
        return privateFunction();
    };
})();

模塊模式

前面的模式是用于自定義類型創(chuàng)建私有變量和特權(quán)方法的遵岩。而道格拉斯所說的模塊模式(module patten)則是為單例創(chuàng)建私有變量和方法你辣。所謂單例(singleton),指的就是一個(gè)實(shí)例的對(duì)象尘执。

var signleton = function () {
    //私有變量和私有函數(shù)
    var privateVariable = 10;

    function privateFunction () {
        return false;
    }

    //公有/特權(quán)方法
    return {
        publicProperty : true;
        publicMethod : function() {
            privateVariable++;
            return privateFunction();
        }
    };
}();

增強(qiáng)的模塊模式

這種增強(qiáng)的模塊模式適合那些單例必須是某種類型的實(shí)例舍哄,同時(shí)還必須添加某些屬性和(或)方法對(duì)其加以增強(qiáng)的情況。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末誊锭,一起剝皮案震驚了整個(gè)濱河市表悬,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌丧靡,老刑警劉巖蟆沫,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異温治,居然都是意外死亡饥追,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門罐盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來但绕,“玉大人,你說我怎么就攤上這事惶看∧笏常” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵纬黎,是天一觀的道長幅骄。 經(jīng)常有香客問我,道長本今,這世上最難降的妖魔是什么拆座? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任主巍,我火速辦了婚禮,結(jié)果婚禮上挪凑,老公的妹妹穿的比我還像新娘孕索。我一直安慰自己,他們只是感情好躏碳,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布搞旭。 她就那樣靜靜地躺著,像睡著了一般菇绵。 火紅的嫁衣襯著肌膚如雪肄渗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天咬最,我揣著相機(jī)與錄音翎嫡,去河邊找鬼。 笑死永乌,一個(gè)胖子當(dāng)著我的面吹牛惑申,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铆遭,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼硝桩,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼沿猜!你這毒婦竟也來了枚荣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤啼肩,失蹤者是張志新(化名)和其女友劉穎橄妆,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體祈坠,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡害碾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赦拘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片慌随。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖躺同,靈堂內(nèi)的尸體忽然破棺而出阁猜,到底是詐尸還是另有隱情,我是刑警寧澤蹋艺,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布剃袍,位于F島的核電站,受9級(jí)特大地震影響捎谨,放射性物質(zhì)發(fā)生泄漏民效。R本人自食惡果不足惜憔维,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望畏邢。 院中可真熱鬧业扒,春花似錦、人聲如沸棵红。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逆甜。三九已至虱肄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間交煞,已是汗流浹背咏窿。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留素征,地道東北人集嵌。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像御毅,于是被迫代替她去往敵國和親根欧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

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

  • 定義函數(shù)的兩種方式 函數(shù)聲明:函數(shù)聲明提升端蛆,在執(zhí)行代碼之前會(huì)先讀取函數(shù)聲明凤粗,意味著可以把函數(shù)聲明放在調(diào)用它的語句后...
    soso101閱讀 318評(píng)論 0 0
  • 定義函數(shù)的方式有兩種:函數(shù)聲明和函數(shù)表達(dá)式。 函數(shù)聲明的一個(gè)重要特征就是函數(shù)聲明提升今豆,意思是在執(zhí)行代碼前會(huì)先讀取函...
    oWSQo閱讀 665評(píng)論 0 0
  • 閉包 閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)嫌拣。創(chuàng)建閉包的常見方式就是在一個(gè)函數(shù)的內(nèi)部創(chuàng)建另一個(gè)函數(shù)。 當(dāng)某...
    胖胖冰閱讀 313評(píng)論 0 1
  • 在JavaScript 編程中呆躲,函數(shù)表達(dá)式是一種非常有用的技術(shù)异逐。使用函數(shù)表達(dá)式可以無須對(duì)函數(shù)命名,從而實(shí)現(xiàn)動(dòng)態(tài)編程...
    shanruopeng閱讀 142評(píng)論 0 2
  • 等到那一天,我足夠優(yōu)秀,你我足夠成熟插掂。等到那一天,我知道如何去愛一個(gè)人灰瞻。那時(shí),我想遇到你辅甥。 戀愛是一種責(zé)任,我選擇...
    西瓜愛肚肚閱讀 147評(píng)論 0 0