一路上感謝各位讀者們的支持和回饋。
本 30 天系列文目前已經將篇幅重新整理、編纂成冊。
《JavaScript 概念三明治》在天瓏書局上架囉!
喜歡這個系列,想閱讀更詳細原理說明的讀者可以參考:
https://www.tenlong.com.tw/products/9789864347575
今天要談到的是 JS 裡面最常被提出來討論的部分,也就是 this 的指向,前面有提到當全域執行環境被產生出來之後,除了全域物件 window ,一個指向這個 window 物件的 this 也會跟著被產生。所以接下來你就可以用 this 來指稱 window 物件,除此之外, this 並不永遠都指向 window 物件,根據不同的呼叫方式,this 所指向的值也會不一樣,所以,你「如何呼叫」這件事情就會很大一部分影響 this 的指向。
在正式進入 this
解說之前,我們先來了解一下為什麼 this 這麼重要, this
讓我們可以很方便地從執行環境內部取得外部物件,用另一個方式說就是,this
可以讓我們在呼叫函式時們決定要指向哪一個物件。
不過如果沒有好好使用的話,就會出現 this
指向錯誤的物件之類的不如預期的情況出現,所以我們在使用之前,一定要先了解 this
檯面下的運作方式。
所謂 this
的繫結指的是指向哪一個物件, this
大致上一共有四種繫結,讓我們一個一個來看看:
預設繫結:foo 的 this 被 bind 到全域物件Window底下,這是最常見也最好理解的繫結。
function foo(){
console.log(this.a);
}
var a=2;
foo() //2;
隱含繫結:隱含的指出 this
綁定的對象,使用 .
可以取用到物件底下的屬性,同時也在告訴 JS this
的指向:
var foo = {
a:'I am in foo',
bar:function(){
console.log(this.a);
},
}
foo.bar() //I am in foo;
當你用隱含的繫結去呼叫物件內的函式時, this
會正確的指向該物件,但是一但你將這個函式指派給另外一個變數時,這個變數就只會參考到該函式,而不是擁有該函式的整個物件,這個時候再去執行的時候, this
就會因為找不到該物件而指向全域,這個現象就稱為隱含繫結的失去:
var obj = {
a:'obj a',
foo: function foo(){
console.log(this.a);
},
}
var bar = obj.foo;
var a = ' global a'; //Something Happened.
bar(); // global a
在JS 裡面,函是可以使用 call()、apply(),來指定綁定物件的 this
,call
與 apply
在使用上兩個還蠻相像的,只差在參數傳入的方式,第一個參數都是指定 this
指向的物件, 而第二個以後的參數則是要傳入該函式的參數,apply
是以陣列的方式來決定傳入函式的參數順序,而 call
則是直接以第二個參數後的數量及順序來決定:
function foo(arg1,arg2){
console.log(this.a);
}
var obj ={
a:2,
}
foo() // undefined
foo.call(obj , arg1 , arg2);//2
foo.apply(obj,[arg1,arg2]);//2
//call 跟 apply 基本上行為相同,只差在參數傳入的方式不同
Hard Bind 是明確繫結的一種變化.可以確保某個 function 的 this 每次被呼叫的時候都與目標物件綁定,可以看到因為多包一層function的關係,即時bar在怎麼用call指定this環境,裡面的主要function :foo.call(obj)依然不會受到影響。
function foo(){
console.log(this.a);
}
var obj = {
a:2
}
var bar = function(){
console.log('this= '+this);
foo.call(obj);
}
bar(); //this= [object Window]
//2
bar.call(window); //this= [object Window]
//2
當一個函式被以 new 的方式呼叫時,神奇的事情發生了:
函式搭配 new
關鍵字來創造物件的方式,也是早期物件導向宣告新物件的方式,而後來 class
關鍵字的出現,也讓我們用更直觀的方式宣告物件,因此像這樣使用 function
創造物件的方式也就比較不常見了。
function foo(){
this.a=2;
}
var bar = new foo();
//{}
//this = {}
//this.a=2
//{a:2}
//return {a:2}
//bar.a=2
console.log(bar.a); //2
當 this 的繫結重複的時候,會以下面的優先順序決定採用哪一種繫結:
預設 < 隱含 < 明確繫結 < new 繫節
本篇文章參考 You Dont Know JS 系列的 Scope & Closure
請問在 1. 預設繫結 ( Default Binding )
為什麼console.log出來是2呢?
不應該是undefined嗎?