1. 程式人生 > >js基礎(函式)--定義及四種呼叫方式

js基礎(函式)--定義及四種呼叫方式

  • 函式的定義

下面分別展示了函式語句和表示式兩種方式的函式定義。注意,以表示式來定義函式只適用於它
作為一個大的表示式的一部分,比如在賦值和呼叫過程中定義函式:


//輸出o的每個屬性的名稱和值,返回undefined
function printprops(o){
    for(var p in o)
        console.log(p+":"+o[p]+"\n");
}

//計算兩個笛卡爾座標(x1,y1)和(x2,y2)之間的距離
function distance(x1,y1,x2,y2){
    var dx=x2-x1;
    var dy=y2-y1;
    return Math.sqrt(dx*dx+dy*dy);
}

//計算階乘的遞迴函式(呼叫自身的函式)
//x!的值是從x到x遞減(步長為1)的值的累乘
function factorial(x){
    if(x<=1)return 1;
    return x*factorial(x-1);
}

//這個函式表示式定義了一個函式用來求傳入引數的平方
//注意我們把它賦值給一個變數

var square=function(x){return x*x;}//函式表示式可以包含名稱,這在遞迴時很有用
var f=function fact(x){if(x<=1)return 1;else return x*fact(x-1);};//函式表達
式也可以作為引數傳給其他函式

data.sort(function(a,b){return a-b;});//函式表示式有時定義後立即呼叫
var tensquared=(function(x){return x*x;}(10));


函式名稱通常是動詞或以動詞為字首的片語。通常函式名的第一個字元為小寫,這是一種程式設計約
定。當函式名包含多個單詞時,一種約定是將單詞以下劃線分隔,就像like_this()。還有另外
一種約定,就是除了第一個單詞之外的單詞首字母使用大寫字母,就像likeThis()。有一些函式
是用做內部函式或私有函式(不是作為公用API的一部分),這種函式名通常以一條下劃線為字首。


請注意,例8-1中的大多數函式(但不是全部)包含一條return語句。return語句導致函式停止
執行,並返回它的表示式(如果有的話)的值給呼叫者。如果return語句沒有一個與之相關的表
達式,則它返回undefined值。如果一個函式不包含return語句,那它就只執行函式體中的每條
語句,並返回undefined值給呼叫者。
在JavaScript裡,函式可以巢狀在其他函式裡。例如:

function hypotenuse(a,b){
    function square(x){return x*x;}
    return Math.sqrt(square(a)+square(b));
}

巢狀函式的有趣之處在於它的變數作用域規則:它們可以訪問巢狀它們(或多重巢狀)的函式的引數
和變數。例如,在上面的程式碼裡,內部函式square()可以讀寫外部函式hypotenuse()定義的引數a
和b。

 

  • 函式的呼叫

          1.函式呼叫

使用呼叫表示式可以進行普通的函式呼叫也可進行方法呼叫。一個呼叫表示式由多個函
數表示式組成,每個函式表示式都是由一個函式物件和左圓括號、引數列表和右圓括號組成,引數列
表是由逗號分隔的零個或多個引數表示式組成。如果函式表示式是一個屬性訪問表示式,即該函式是
一個物件的屬性或陣列中的一個元素,那麼它就是一個方法呼叫表示式。下面將會解釋這種情形。下
面的程式碼展示了一些普通的函式呼叫表示式:

printprops({x:1});
var total=distance(0,0,2,1)+distance(2,1,3,5);
var probability=factorial(5)/factorial(13);

在一個呼叫中,每個引數表示式(圓括號之間的部分)都會計算出一個值,計算的結果作為引數傳遞
給另外一個函式。這些值作為實參傳遞給宣告函式時定義的形參。在函式體中存在一個形參的引用,
指向當前傳入的實參列表,通過它可以獲得引數的值。

對於普通的函式呼叫,函式的返回值成為呼叫表示式的值。如果該函式返回是因為直譯器到達結尾,
返回值就是undefined。如果函式返回是因為直譯器執行到一條return語句,返回值就是return之
後的表示式的值,如果return語句沒有值,則返回undefined。

根據ECMAScript 3和非嚴格的ECMAScript 5對函式呼叫的規定,呼叫上下文(this的值)是全域性
物件。然而,在嚴格模式下,呼叫上下文則是undefined。

以函式形式呼叫的函式通常不使用this關鍵字。不過,"this"可以用來判斷當前是否是嚴格模式。

//定義並呼叫一個函式來確定當前指令碼執行時是否為嚴格模式
var strict=(function(){return!this;}());

          2.方法呼叫

一個方法無非是個儲存在一個物件的屬性裡的JavaScript函式。如果有一個函式f和一個物件
o,則可以用下面的程式碼給o定義一個名為m()的方法:

o.m=f;

給物件o定義了方法m(),呼叫它時就像這樣:

o.m();

或者,如果m()需要兩個實參,呼叫起來則像這樣:

o.m(x,y);

上面的程式碼是一個呼叫表示式:它包括一個函式表示式o.m,以及兩個實參表示式x和y,函式表
達式本身就是一個屬性訪問表示式,這意味著該函式被當做一個方法,而不是作為一個普通函式
來呼叫。

對方法呼叫的引數和返回值的處理,和上面所描述的普通函式呼叫完全一致。但是,方法呼叫和
函式呼叫有一個重要的區別,即:呼叫上下文。屬性訪問表示式由兩部分組成:一個物件(本例
中的o)和屬性名稱(m)。在像這樣的方法呼叫表示式裡,物件o成為呼叫上下文,函式體可以
使用關鍵字this引用該物件。下面是一個具體的例子:

var calculator={//物件直接量
    operand1:1,
    operand2:1,
    add:function(){//注意this關鍵字的用法,this指代當前物件
        this.result=this.operand1+this.operand2;
    }
};

calculator.add();//這個方法呼叫計算1+1的結果
calculator.result//=>2

大多數方法呼叫使用點符號來訪問屬性,使用方括號(的屬性訪問表示式)也可以進行屬性訪問
操作。下面兩個例子都是函式呼叫:

o["m"](x,y);//o.m(x,y)的另外一種寫法
a[0](z)//同樣是一個方法呼叫(這裡假設a[0]是一個函式)

方法呼叫可能包括更復雜的屬性訪問表示式:

customer.surname.toUpperCase();//呼叫customer.surname的方法
f().m();//在f()呼叫結束後繼續呼叫返回值中的方法m()

方法和this關鍵字是面向物件程式設計範例的核心。任何函式只要作為方法呼叫實際上都會傳入一個
隱式的實參——這個實參是一個物件,方法呼叫的母體就是這個物件。通常來講,基於那個物件的
方法可以執行多種操作,方法呼叫的語法已經很清晰地表明瞭函式將基於一個物件進行操作,比
較下面兩行程式碼:

rect.setSize(width,height);
setRectSize(rect,width,height);

我們假設這兩行程式碼的功能完全一樣,它們都作用於一個假定的物件rect。可以看出,第一行的
方法呼叫語法非常清晰地表明這個函式執行的載體是rect物件,函式中的所有操作都將基於這個物件。

方法鏈
當方法的返回值是一個物件,這個物件還可以再呼叫它的方法。這種方法呼叫序列中(通常稱
為“鏈”或者“級聯”)每次的呼叫結果都是另外一個表示式的組成部分。比如,基於jQuery庫,我們常常會這樣寫程式碼:

//找到所有的header,取得它們id的對映,轉換為陣列並對它們進行排序
$(":header").map(function(){return this.id}).get().sort();

當方法並不需要返回值時,最好直接返回this。如果在設計的API中一直採用這種方式(每個方
法都返回this),使用API就可以進行“鏈式呼叫”[3]風格的程式設計,在這種程式設計風格中,只要指
定一次要呼叫的物件即可,餘下的方法都可以基於此進行呼叫:

shape.setX(100).setY(100).setSize(50).setOutline("red").setFill("blue").draw();

不要將方法的鏈式呼叫和建構函式的鏈式呼叫混為一談.


需要注意的是,this是一個關鍵字,不是變數,也不是屬性名。JavaScript的語法不允許給this賦值。

和變數不同,關鍵字this沒有作用域的限制,巢狀的函式不會從呼叫它的函式中繼承this。如果
巢狀函式作為方法呼叫,其this的值指向呼叫它的物件。如果巢狀函式作為函式呼叫,其this值
不是全域性物件(非嚴格模式下)就是undefined(嚴格模式下)。很多人誤以為呼叫巢狀函式時
this會指向呼叫外層函式的上下文。如果你想訪問這個外部函式的this值,需要將this的值保
存在一個變數裡,這個變數和內部函式都同在一個作用域內。通常使用變數self來儲存this,比如:

var o={//物件o
    m:function(){//物件中的方法m()
        var self=this;//將this的值儲存至一個變數中
        console.log(this===o);//輸出true,this就是這個物件o
        f();//呼叫輔助函式f()
        function f(){//定義一個巢狀函式f()
            console.log(this===o);//"false":this的值是全域性物件或undefined
            console.log(self===o);//"true":self指外部函式的this值
        }
    }
};

o.m();//呼叫物件o的方法m()

          3.建構函式呼叫

如果函式或者方法呼叫之前帶有關鍵字new,它就構成建構函式呼叫。建構函式呼叫和普通的函式調
用以及方法呼叫在實參處理、呼叫上下文和返回值方面都有不同。

如果建構函式呼叫在圓括號內包含一組實參列表,先計算這些實參表示式,然後傳入函式內,這和函
數呼叫和方法呼叫是一致的。但如果建構函式沒有形參,JavaScript建構函式呼叫的語法是允許省
略實參列表和圓括號的。凡是沒有形參的建構函式呼叫都可以省略圓括號,比如,下面這兩行程式碼就
是等價的:

var o=new Object();
var o=new Object;

建構函式呼叫建立一個新的空物件,這個物件繼承自建構函式的prototype屬性。建構函式試圖初始
化這個新建立的物件,並將這個物件用做其呼叫上下文,因此建構函式可以使用this關鍵字來引用
這個新建立的物件。注意,儘管建構函式看起來像一個方法呼叫,它依然會使用這個新物件作為呼叫
上下文。也就是說,在表示式new o.m()中,呼叫上下文並不是o。

建構函式通常不使用return關鍵字,它們通常初始化新物件,當建構函式的函式體執行完畢時,它
會顯式返回。在這種情況下,建構函式呼叫表示式的計算結果就是這個新物件的值。然而如果構造函
數顯式地使用return語句返回一個物件,那麼呼叫表示式的值就是這個物件。如果建構函式使用
return語句但沒有指定返回值,或者返回一個原始值,那麼這時將忽略返回值,同時使用這個新對
象作為呼叫結果。

          4.間接呼叫

JavaScript中的函式也是物件,和其他JavaScript物件沒什麼兩樣,函式物件也可以包含方法。其
中的兩個方法call()和apply()可以用來間接地呼叫函式。兩個方法都允許顯式指定呼叫所需的
this值,也就是說,任何函式可以作為任何物件的方法來呼叫,哪怕這個函式不是那個物件的方
法。兩個方法都可以指定呼叫的實參。call()方法使用它自有的實參列表作為函式的實參,
apply()方法則要求以陣列的形式傳入引數。
  •