在 JavaScript 中, this 的指向問題常讓人一頭霧水于宙,以至于有些人無論什么場景下都直接將 this 綁定到一個(gè)變量浮驳,后面只使用變量,這就有些因噎廢食了捞魁。接下來讓我們一起揭秘 this “魔法”至会。
this 是什么
this 是一個(gè)特殊的對象。this 提供了一種更優(yōu)雅的方式來隱式“傳遞”一個(gè)對象引用谱俭。
如何判斷 this 的指向
當(dāng)一個(gè)函數(shù)被調(diào)用時(shí)奉件,JS 引擎會(huì)創(chuàng)建一個(gè)活動(dòng)記錄(也稱為執(zhí)行上下文)。這個(gè)記錄會(huì)包含函數(shù)在哪里被調(diào)用(調(diào)用棧)昆著、函數(shù)的調(diào)用方法瓶蚂、傳入的參數(shù)等信息。this 是記錄中的一個(gè)屬性宣吱,在函數(shù)被調(diào)用時(shí)才會(huì)完成 this 的綁定,所以它指向什么完全取決于函數(shù)在哪里被調(diào)用瞳别。并且征候,調(diào)用函數(shù)的方式?jīng)Q定著 this 的綁定規(guī)則,不同的綁定規(guī)則的優(yōu)先級也是不一致的祟敛,優(yōu)先級順序如下:
-
new 綁定:使用 new 調(diào)用函數(shù)疤坝。此時(shí) this 綁定的是新創(chuàng)建的對象(參見下方使用 new 調(diào)用函數(shù)時(shí),發(fā)生了什么)馆铁。
function foo(something) { this.a = something; } var obj1 = {}; var bar = foo.bind(obj1); bar(2); console.log(obj1.a); // 2 var baz = new bar(3); console.log(obj1.a); // 2 console.log(baz.a); // 3
-
顯式綁定:通過 call跑揉、apply、bind 方法調(diào)用函數(shù)埠巨。此時(shí) this 綁定的是 call历谍、apply、bind 方法中指定的對象辣垒。
function foo() { console.log(this.a); } var obj1 = { a: 2, foo: foo, }; var obj2 = { a: 3, foo: foo, }; obj1.foo(); // 2 obj2.foo(); // 3 obj1.foo.call(obj2); // 3 obj2.foo.call(obj1); // 2
-
隱式綁定: 在某個(gè)上下文對象中調(diào)用函數(shù)望侈。此時(shí) this 綁定的是這個(gè)上下文對象。
function foo() { console.log(this.a); } var obj = { a: 2, foo: foo }; obj.foo(); // 2
-
默認(rèn)綁定: 如果不是以上三種的話勋桶,使用默認(rèn)綁定脱衙。如果在嚴(yán)格模式下侥猬,就綁定到 undefined,否則綁定到運(yùn)行環(huán)境所提供的全局對象捐韩。
function foo() { console.log(this.a); } var a = 2; foo(); // 2
例外情況
箭頭函數(shù):箭頭函數(shù)中的 this 并不會(huì)使用以上四條標(biāo)準(zhǔn)的綁定規(guī)則退唠,而是根據(jù)當(dāng)前的詞法作用域來決定,具體來說荤胁,箭頭函數(shù)會(huì)繼承外層第一個(gè)非箭頭函數(shù)的 this 綁定瞧预。
-
匿名函數(shù):匿名函數(shù)沒有調(diào)用者,一般都是在全局環(huán)境下調(diào)用寨蹋,使用默認(rèn)綁定規(guī)則松蒜。
var a = 1; var obj = { a: 2, foo() { var a = 3; console.log(this.a); var bar = function () { console.log(this.a); }; bar(); }, }; obj.foo(); // 2 1
如果你把 null 或者 undefined 作為 this 的綁定對象傳入 call、apply已旧、 bind 方法秸苗,這些值在調(diào)用時(shí)會(huì)被忽略,實(shí)際應(yīng)用的是默認(rèn)綁定規(guī)則运褪。
關(guān)聯(lián)知識
使用 new 調(diào)用函數(shù)時(shí)惊楼,發(fā)生了什么
使用 new 來調(diào)用函數(shù),或者說發(fā)生構(gòu)造函數(shù)調(diào)用時(shí)秸讹,會(huì)自動(dòng)執(zhí)行下面的操作檀咙。
- 創(chuàng)建(或者說構(gòu)造)一個(gè)全新的對象
- 為步驟 1 新創(chuàng)建的對象添加屬性
__proto__
,將該屬性指向構(gòu)造函數(shù)的原型對象 - 將構(gòu)造函數(shù)中調(diào)用的 this 綁定到這個(gè)新對象
- 如果構(gòu)造函數(shù)沒有主動(dòng)返回對象璃诀,則返回 這個(gè)新對象