一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

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

 蕎麥888 2015-11-10

在JavaScript中,函數(shù)是個(gè)非常重要的對(duì)象,函數(shù)通常有三種表現(xiàn)形式:函數(shù)聲明,函數(shù)表達(dá)式和函數(shù)構(gòu)造器創(chuàng)建的函數(shù)。

本文中主要看看函數(shù)表達(dá)式及其相關(guān)的知識(shí)點(diǎn)。

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

首先,看看函數(shù)表達(dá)式的表現(xiàn)形式,函數(shù)表達(dá)式(Function Expression, FE)有下面四個(gè)特點(diǎn):

  • 在代碼中須出現(xiàn)在表達(dá)式的位置
  • 有可選的函數(shù)名稱
  • 不會(huì)影響變量對(duì)象(VO)
  • 在代碼執(zhí)行階段創(chuàng)建

下面就通過一些例子來看看函數(shù)表達(dá)式的這四個(gè)特點(diǎn)。

FE特點(diǎn)分析

例子一:在下面代碼中,"add"是一個(gè)函數(shù)對(duì)象,"sub"是一個(gè)普通JavaScript變量,但是被賦值了一個(gè)函數(shù)表達(dá)式" function (a, b){ return a - b; } ":

復(fù)制代碼
function add(a, b){
    return a + b;
}

var sub = function (a, b){
    return a - b;
}


console.log(add(1, 3));
// 4
console.log(sub(5, 1));
// 4
復(fù)制代碼

通過這個(gè)例子,可以直觀的看到函數(shù)表達(dá)式的前兩個(gè)特點(diǎn):

  • 在代碼中須出現(xiàn)在表達(dá)式的位置
    • function (a, b){ return a - b; } "出現(xiàn)在了JavaScript語句中的表達(dá)式位置
  • 有可選的函數(shù)名稱
    • function (a, b){ return a - b; } "這個(gè)函數(shù)表達(dá)式?jīng)]有函數(shù)名稱,是個(gè)匿名函數(shù)表達(dá)式

 

例子二:為了解釋函數(shù)表達(dá)式另外兩個(gè)特點(diǎn),繼續(xù)看看下面的例子。

復(fù)制代碼
console.log(add(1, 3));
// 4
console.log(sub);
// undefined
console.log(sub(5, 1));
// Uncaught TypeError: sub is not a function(…)

function add(a, b){
    return a + b;
}

var sub = function (a, b){
    return a - b;
}
復(fù)制代碼

在這個(gè)例子中,調(diào)整了代碼的執(zhí)行順序,這次函數(shù)"add"執(zhí)行正常,但是對(duì)函數(shù)表達(dá)式的執(zhí)行失敗了。

對(duì)于這個(gè)例子,可以參考"JavaScript的執(zhí)行上下文"一文中的內(nèi)容,當(dāng)代碼開始執(zhí)行的時(shí)候,可以得到下圖所示的Global VO。

在Global VO中,對(duì)"add"函數(shù)表現(xiàn)為JavaScript的"Hoisting"效果,所以即使在"add"定義之前依然可以使用;

但是對(duì)于"sub"這個(gè)變量,根據(jù)"Execution Context"的初始化過程,"sub"會(huì)被初始化為"undefined",只有執(zhí)行到" var sub = function (a, b){ return a - b; } "語句的時(shí)候,VO中的"sub"才會(huì)被賦值。

通過上面這個(gè)例子,可以看到了函數(shù)表達(dá)式的第四個(gè)特點(diǎn)

  • 在代碼執(zhí)行階段創(chuàng)建

 

例子三:對(duì)上面的例子進(jìn)一步改動(dòng),這次給函數(shù)表達(dá)式加上了一個(gè)名字"_sub",也就是說,這里使用的是一個(gè)命名函數(shù)表達(dá)式。

復(fù)制代碼
var sub = function _sub(a, b){
    console.log(typeof _sub);
    return a - b;
}

console.log(sub(5, 1));
// function
// 4
console.log(typeof _sub)
// undefined
console.log(_sub(5, 1));
// Uncaught ReferenceError: _sub is not defined(…)
復(fù)制代碼

根據(jù)這段代碼的運(yùn)行結(jié)果,可以看到"_sub"這個(gè)函數(shù)名,只能在"_sub"這個(gè)函數(shù)內(nèi)部使用;當(dāng)在函數(shù)外部訪問"_sub"的時(shí)候,就是得到"Uncaught ReferenceError: _sub is not defined(…)"錯(cuò)誤。

所以通過這個(gè)可以看到函數(shù)表達(dá)式的第三個(gè)特點(diǎn):

  • 不會(huì)影響變量對(duì)象(VO)

 

FE的函數(shù)名

到了這里,肯定會(huì)有一個(gè)問題,"_sub"不在VO中,那在哪里?

其實(shí)對(duì)于命名函數(shù)表達(dá)式,JavaScript解釋器額外的做了一些事情:

  1. 當(dāng)解釋器在代碼執(zhí)行階段遇到命名函數(shù)表達(dá)式時(shí),在函數(shù)表達(dá)式創(chuàng)建之前,解釋器創(chuàng)建一個(gè)特定的輔助對(duì)象,并添加到當(dāng)前作用域鏈的最前端
  2. 然后當(dāng)解釋器創(chuàng)建了函數(shù)表達(dá)式,在創(chuàng)建階段,函數(shù)獲取了[[Scope]] 屬性(當(dāng)前函數(shù)上下文的作用域鏈)
  3. 此后,函數(shù)表達(dá)式的函數(shù)名添加到特定對(duì)象上作為唯一的屬性;這個(gè)屬性的值是引用到函數(shù)表達(dá)式上
  4. 最后一步是從父作用域鏈中移除那個(gè)特定的對(duì)象

下面是表示這一過程的偽代碼:

復(fù)制代碼
specialObject = {};
 
Scope = specialObject + Scope;
 
_sub = new FunctionExpression;
_sub.[[Scope]] = Scope;
specialObject. _sub = _sub; // {DontDelete}, {ReadOnly} 
 
delete Scope[0]; // 從作用域鏈中刪除特殊對(duì)象specialObject
復(fù)制代碼

函數(shù)遞歸

這一小節(jié)可能有些鉆牛角尖,但是這里想演示遞歸調(diào)用可能出現(xiàn)的問題,以及通過命名函數(shù)表達(dá)式以更安全的方式執(zhí)行遞歸。

下面看一個(gè)求階乘的例子,由于函數(shù)對(duì)象也是可以被改變的,所以可能會(huì)出現(xiàn)下面的情況引起錯(cuò)誤。

復(fù)制代碼
function factorial(num){
    if (num <= 1){
        return 1;
    } else {
        return num * factorial(num-1);
    }
} 

console.log(factorial(5))
// 120
newFunc = factorial
factorial = null
console.log(newFunc(5));
// Uncaught TypeError: factorial is not a function(…)
復(fù)制代碼

這時(shí),可以利用函數(shù)的arguments對(duì)象的callee屬性來解決上面的問題,也就是說在函數(shù)中,總是使用"arguments.callee"來遞歸調(diào)用函數(shù)。

復(fù)制代碼
function factorial(num){
    if (num <= 1){
        return 1;
    } else {
        return num * arguments.callee(num-1);
    }
}
復(fù)制代碼

但是上面的用法也有些問題,當(dāng)在嚴(yán)格模式的時(shí)候"arguments.callee"就不能正常的工作了。

比較好的解決辦法就是使用命名函數(shù)表達(dá)式,這樣無論"factorial"怎么改變,都不會(huì)影響函數(shù)表達(dá)式" function f(num){…} "

復(fù)制代碼
var factorial = (function f(num){
    if (num <= 1){
        return 1;
    } else {
        return num * f(num-1);
    }
});
復(fù)制代碼

代碼模塊化

在JavaScript中,沒有塊作用域,只有函數(shù)作用域,函數(shù)內(nèi)部可以訪問外部的變量和函數(shù),但是函數(shù)內(nèi)部的變量和函數(shù)在函數(shù)外是不能訪問的。

所以,通過函數(shù)(通常直接使用函數(shù)表達(dá)式),可以模塊化JavaScript代碼。

創(chuàng)建模塊

為了能夠到達(dá)下面的目的,我們可以通過函數(shù)表達(dá)式來建立模塊。

  • 創(chuàng)建一個(gè)可以重用的代碼模塊
  • 模塊中封裝了使用者不必關(guān)心的內(nèi)容,只暴露提供給使用者的接口
  • 盡量與全局namespace進(jìn)行隔離,減少對(duì)全局namespace的污染

下面看一個(gè)簡單的例子:

復(fù)制代碼
var Calc = (function(){
    var _a, _b;
    
    return{
        add: function(){
            return _a + _b;
        },
        
        sub: function(){
            return _a - _b;
        },
        
        set: function(a, b){
            _a = a;
            _b = b;
        }
    }
}());

Calc.set(10, 4);
console.log(Calc.add());
// 14
console.log(Calc.sub());
// 6
復(fù)制代碼

代碼中通過匿名函數(shù)表達(dá)式創(chuàng)建了一個(gè)"Calc"模塊,這是一種常用的創(chuàng)建模塊的方式:

  • 創(chuàng)建一個(gè)匿名函數(shù)表達(dá)式,這個(gè)函數(shù)表達(dá)式中包含了模塊自身的私有變量和函數(shù);
  • 通過執(zhí)行這個(gè)函數(shù)表達(dá)式可以得到一個(gè)對(duì)象,對(duì)象中包含了模塊想要暴露給用戶的公共接口。

除了返回一個(gè)對(duì)象的方式,有的模塊也會(huì)使用另外一種方式,將包含模塊公共接口的對(duì)象作為全局變量的一個(gè)屬性。

這樣在代碼的其他地方,就可以直接通過全局變量的這個(gè)屬性來使用模塊了。

例如下面的例子:

復(fù)制代碼
(function(){
    var _a, _b;
    
    var root = this;
    
    var _ = {
        add: function(){
            return _a + _b;
        },
        
        sub: function(){
            return _a - _b;
        },
        
        set: function(a, b){
            _a = a;
            _b = b;
        }
    }
    
    root._ = _;
    
}.call(this));

_.set(10, 4);
console.log(_.add());
// 14
console.log(_.sub());
// 6
復(fù)制代碼

立即調(diào)用的函數(shù)表達(dá)式

在上面兩個(gè)例子中,都使用了匿名的函數(shù)表達(dá)式,并且都是立即執(zhí)行的。如果去看看JavaScript一些開源庫的代碼,例如JQuery、underscore等等,都會(huì)發(fā)現(xiàn)類似的立即執(zhí)行的匿名函數(shù)代碼。

立即調(diào)用的函數(shù)表達(dá)式通常表現(xiàn)為下面的形式:

復(fù)制代碼
(function () { 
    /* code */ 
})();

(function () { 
    /* code */ 
} ()); 
在underscore這個(gè)JavaScript庫中,使用的是下面的方式:
(function () { 
    // Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;

/* code */ 

} .call(this)); 
復(fù)制代碼

在這里,underscore模塊直接對(duì)全局變量this進(jìn)行了緩存,方便模塊內(nèi)部使用。

總結(jié)

本文簡單介紹了JavaScript中的函數(shù)表達(dá)式,并通過三個(gè)例子解釋了函數(shù)表達(dá)式的四個(gè)特點(diǎn)。

  • 在代碼中須出現(xiàn)在表達(dá)式的位置
  • 有可選的函數(shù)名稱
  • 不會(huì)影響變量對(duì)象(VO)
  • 在代碼執(zhí)行階段創(chuàng)建

通過函數(shù)表達(dá)式可以方便的建立JavaScript模塊,通過模塊可以實(shí)現(xiàn)下面的效果:

  • 創(chuàng)建一個(gè)可以重用的代碼模塊
  • 模塊中封裝了使用者不必關(guān)心的內(nèi)容,只暴露提供給使用者的接口
  • 盡量與全局namespace進(jìn)行隔離,減少對(duì)全局namespace的污染

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請遵守用戶 評(píng)論公約

    類似文章 更多

    国产亚洲欧美一区二区| 国产精品欧美激情在线播放| 国产精品偷拍视频一区| 国产欧美日产久久婷婷| 国产又粗又硬又大又爽的视频| 亚洲午夜av一区二区| 亚洲日本韩国一区二区三区| 亚洲精品中文字幕在线视频| 麻豆国产精品一区二区三区| 超碰在线免费公开中国黄片| 国产又色又爽又黄又大| 国内精品一区二区欧美| 久久免费精品拍拍一区二区 | 国产又粗又硬又大又爽的视频| 在线免费视频你懂的观看| 大香伊蕉欧美一区二区三区| 在线观看视频国产你懂的| 欧美国产日韩变态另类在线看| 加勒比系列一区二区在线观看| 国产精品成人又粗又长又爽| 午夜福利黄片免费观看| 国产午夜福利片在线观看| 东京热男人的天堂社区| 久久老熟女一区二区三区福利 | 色狠狠一区二区三区香蕉蜜桃| 国产又粗又硬又长又爽的剧情| 国内外激情免费在线视频| 久久精品亚洲欧美日韩| 国产爆操白丝美女在线观看| 97人妻精品免费一区二区| 国产户外勾引精品露出一区| 国产日韩欧美一区二区| 亚洲一区二区精品久久av| 在线观看那种视频你懂的| 欧美黑人巨大一区二区三区| 国产精品午夜小视频观看| 日本熟妇五十一区二区三区| 成在线人免费视频一区二区| 国产内射一级二级三级| 免费啪视频免费欧美亚洲| 九九热精品视频免费在线播放|