js裡函式呼叫的四種模式

weixin_34365417發表於2017-04-29

js 裡函式呼叫有4種模式:方法呼叫、正常函式呼叫、構造器函式呼叫、apply/call呼叫。同時,無論哪種函式呼叫除了你宣告時定義的形參外,還會自動新增2個形參,分別是thisarguments

1.方法呼叫

這個很好理解,函式是一個物件的屬性,比如

var a = {
    v : 0,
    f : function(xx) {
        this.v = xx;
    }
}
a.f(5);

這個時候,上面函式裡的 this 就繫結的是這個物件 a。所以this.v 可以取到物件a 的屬性 v

2.正常函式呼叫:

依然看程式碼

function f(xx) {
    this.x = xx;
}
f(5);

這個時候,函式 f裡的this 繫結的是全域性物件,如果是在瀏覽器執行的直譯器中,一般來說是樓上說的 window物件。所以這裡 this.x訪問的其實是 window.x,當然,如果 window沒有 x 屬性,那麼你這麼一寫,按照 js 的坑爹語法,就是給 window 物件新增了一個 x 屬性,同時賦值。

3.構造器函式呼叫:

建構函式一直是我認為是 js 裡最坑爹的部分,因為它和 js 最初設計的基於原型的物件導向實現方式格格不入,就好像是特意為了迎合大家已經被其他基於類的面相物件實現給慣壞了的習慣。
如果你在一個函式前面帶上 new關鍵字來呼叫,那麼 js 會建立一個prototype屬性是此函式的一個新物件,同時在呼叫這個函式的時候,把this繫結到這個新物件上。當然new 關鍵字也會改變return語句的行為。看程式碼

function a(xx) {
    this.m = xx;
}
var b = new a(5);

上面這個函式和正常呼叫的函式寫法上沒什麼區別,只不過在呼叫的時候函式名前面加了關鍵字new 罷了,這麼一來,this 繫結的就不再是前面講到的全域性物件了,而是這裡說的建立的新物件,所以說這種方式其實很危險,因為光看函式,你不會知道這個函式到底是準備拿來當建構函式用的,還是一般函式用的,所以我們可以看到,在jslint裡,它會要求你寫的所有建構函式,也就是一旦它發現你用了 new關鍵字,那麼後面那個函式的首字母必須大寫,這樣通過函式首字母大寫的方式來區分,我個人只有一個看法:坑爹:)

4.apply/call 呼叫:

我們知道,在 js 裡,函式其實也是一個物件,那麼函式自然也可以擁有它自己的方法,有點繞,就好像函式可以自己有屬性也是一個函式。其中每個函式都擁有apply()這個方法,讓我們構造一個引數陣列傳遞給函式,同時可以自己來設定this 的值,這就是它最強大的地方,上面的3種函式呼叫方法,你可以看到,this 都是自動繫結的,沒辦法由你來設,當你想設的時候,就可以用 apply()了。apply接收2個引數,第一個是將傳遞給這個函式的 this的值,第二個是引數陣列。看程式碼:

function a(xx) {
    this.b = xx;
}
var o = {};
a.apply(o, [5]);
alert(a.b);    // undefined
alert(o.b);    // 5

是不是很神奇,函式 a居然可以給o 加屬性值。當然,如果你 apply的第一個引數傳遞 null,那麼在函式a 裡面 this 指標依然會繫結全域性物件。你可能要問了,apply 函式是哪來的,因為在 js 裡所有的函式都有一個共同的prototype,也就是傳說中的 Function.prototype, 這個原型裡有兩個神奇的方法,一個就是這裡的apply ,另一個就是讓題主疑惑的 call
call()方法和 apply() 方法很類似,它們的存在都是為了改變this 的繫結,那call()apply() 有什麼區別呢?就我個人看來,沒啥鳥區別。。。開玩笑!剛剛說了,上面 apply()接收兩個引數,第一個是你想要 this 繫結的物件,第二個是一個引數陣列,注意是一個陣列,你想傳遞給這個函式的所有內容都放在陣列裡,然後 apply() 函式會在傳遞形參時自動幫你展開,同時加入我上面提到的另一個神奇形參arguments。而 call() 呢,它的第一個引數也是你想要this 繫結的物件,但是後面可以接受不定引數,而不再是一個陣列,也就是你可以像平時給函式傳參那樣把這些引數一個一個傳遞,當然,神奇形參 arguments還是不會少的。所以如果一定要說有什麼區別的話,看起來是這樣的

function a(xx, yy) {
    alert(xx, yy);
    alert(this);
    alert(arguments);
}
a.apply(null, [5, 55]);
a.call(null, 5, 55);

僅此而已。

相關文章