JavaScript中七種函式呼叫方式及對應 this 的含義

武文博KevinLM發表於2017-09-10
this 在 JavaScript 開發中佔有相當重要的地位,不過很多人對this這個東西都感覺到琢磨不透。要真正理解JavaScript的函式機制,就非常有必要搞清楚this到底是怎麼回事。

函式呼叫方式不同,this 含義也跟著不同。JavaScript語言中有七種呼叫函式方式:
 
第一種:呼叫方法
var obj = {
    method: function() { alert(this === obj); }
}
obj.method();
上面這行obj.method()顯然method是作為方法被呼叫,這種情況下,函式體中的this繫結的就是method的宿主物件,也就是obj。
從這種呼叫方式我們得出第一定律:
第一定律:以方法方式呼叫函式,this則繫結宿主物件。

第二種:呼叫全域性函式
var method = function(){alert(this === window);}
method();
上面這個函式是個全域性函式。我們知道,全域性變數或函式都相當於window物件的屬性。也就是說,上面這句實際上等同於下面這句:
window.method = function(){alert(this === window);}
window.method();
既然這樣,那麼這裡 this 繫結到 window 物件就顯而易見,很容易理解了。相當於方法呼叫模式的一個特例,並不違背第一定律。

第三種:全域性函式內呼叫內部函式

var m_ext = function() {
    alert(this === window);
    var m_inner = function() {
        alert(this === window);
    }
    m_inner();
}
m_ext();
執行上面這段程式碼,你會驚訝的發現,兩個布林表示式的值都是真。也就是說子函式孫函式,只要是以函式的方式呼叫,this 就鐵了心繫結 window 物件了。從這種呼叫方式我們得出第二定律:
第二定律:不論子函式孫函式,只要是以函式的方式呼叫,this 就鐵了心繫結 window 物件。

第四種:方法內呼叫內部函式

var obj = {};
obj.method = function() {
    alert(this === obj);
    var m_inner = function() {
        alert(this === window);
    }
    m_inner();
}
obj.method();
執行上面這段程式碼,第一個this繫結到obj你不奇怪,第二個this繫結到window其實也不奇怪。它仍然遵守第二定律,也就是不論子函式孫函式,只要是以函式的方式呼叫,this 就鐵了心繫結 window 物件。

第五種:作為建構函式呼叫

function Person(name, age){
    this.name = name;
    this.age = age;
}
var john = new Person('John', 38);
alert(JSON.stringify(john));
你會說,哇,這很眼熟。生成某個類的新物件啊。Javascript就是這麼另類,函式確實可以這麼調。
這次彈出的是這樣一個字串:{"name":"John","age":38}。哇,這次我明明沒在函式定義裡寫return 語句,它卻主動返回了一個物件給我。沒錯,即便你在函式定義裡return某個東西,它也不會理你。(注意:如果你return一個function就會有驚喜,不信你試試看)。從這種呼叫方式我們得出第三定律:
第三定律:如果在一個函式本身沒有返回值,在其前面加上 new 呼叫,就會建立一個連線到該函式 prototype 屬性的新物件,再把 this 繫結到該物件,然後執行該函式,最後返回該物件。如果該函式有返回值,且返回值為字串/數字/布林值等簡單物件的話,該返回值會被丟棄。但如果該函式的返回值為物件,函式或者陣列等複雜物件的話,該函式則會返回該返回值,拋棄this繫結的物件。據我測試,如果返回值是一個函式時,該函式會返回undefined。我暫時還不知道為什麼會這樣。

第六種:用函式物件的apply方法呼叫

var my_concat = function(stra, strb){
    alert(this);
    return stra + '' + strb;
}
var param = ['hello', ' world']
alert(my_concat.apply('bullshit', param));

你一定注意了,在這裡 this 繫結的是apply方法的第一個引數 'bullshit'。從這裡我們得出第四定律:
第四定律:用方法的apply/call方法呼叫方法時,this 則被繫結到apply/call方法的第一個引數。
想誰就是誰,嗯,吳媽也行。JavaScript的程式設計師們好幸福哦。

第七種:用函式物件的call方法呼叫
var my_concat = function(stra, strb){
    alert(this);
    return stra + '' + strb;
}
alert(my_concat.call('bullshit', 'hello',' world'));

對啦,你或許會多問一句,apply 的第二個引數有什麼規定?call方法和apply又有什麼不同?嗯 ,真是勤學好問,我就再囉嗦一下說說apply和call:

apply 和 call 的區別:

apply要求第二個引數必須是陣列。否則就會報
 TypeError: second argument to Function.prototype.apply must be an array. 而call則沒這麼嚴格啦,第二個引數要什麼型別?隨意啦,還可以有第三個第四個第五個第六個....啦。

我想我說的夠清楚了。感謝《JavaScript The Good Parts》和《JavaScript Definitive Guide》,要不然我也弄不明白呢!


轉自:http://blog.sina.com.cn/s/blog_621f1e120100rj21.html 

相關文章