Javascript-this到底該如何理解

談及 Javascript 中的 this,竟然讓人覺得頭疼,它不像 Java,C++ 中的 this 指向調(diào)用 this 的該對(duì)象。

在函數(shù)中 this 到底取何值,是在函數(shù)真正被調(diào)用執(zhí)行的時(shí)候確定下來的,函數(shù)定義的時(shí)候確定不了。

因?yàn)?this 的取值是執(zhí)行上下文環(huán)境的一部分,每次調(diào)用函數(shù),都會(huì)產(chǎn)生一個(gè)新的執(zhí)行上下文環(huán)境。當(dāng)你在代碼中使用了 this,這個(gè) this 的值就直接從執(zhí)行的上下文中獲取了,而不會(huì)從作用域鏈中搜尋。


關(guān)于 this 的取值,大體上可以分為以下八種情況:


情況一:全局 & 調(diào)用普通函數(shù)


在全局環(huán)境中,this 永遠(yuǎn)指向 window。


console.log(this === window);? ? //true

普通函數(shù)在調(diào)用時(shí)候(注意不是構(gòu)造函數(shù),前面不加 new),其中的 this 也是指向 window。


但是如果在嚴(yán)格模式下調(diào)用的話會(huì)報(bào)錯(cuò):


var x = 10;

function foo(){

? ? console.log(this);? ? // undefined

? ? console.log(this.x);? // Uncaught TypeError: Cannot read property 'x' of undefined

}

foo();

情況二:構(gòu)造函數(shù)


所謂的構(gòu)造函數(shù)就是由一個(gè)函數(shù) new 出來的對(duì)象,一般構(gòu)造函數(shù)的函數(shù)名首字母大寫,例如像 Object,F(xiàn)unction,Array 這些都屬于構(gòu)造函數(shù)。


function Foo(){

? ? this.x = 10;

? ? console.log(this);? ? //Foo {x:10}

}

var foo = new Foo();

console.log(foo.x);? ? ? //10


上述代碼,如果函數(shù)作為構(gòu)造函數(shù)使用,那么其中的 this 就代表它即將 new 出來的對(duì)象。

但是如果直接調(diào)用 Foo 函數(shù),而不是 new Foo(),那就變成情況1,這時(shí)候 Foo() 就變成普通函數(shù)。

function Foo(){

? ? this.x = 10;

? ? console.log(this);? ? //Window

}

var foo = Foo();

console.log(foo.x);? ? ? //undefined

情況三:對(duì)象方法

如果函數(shù)作為對(duì)象的方法時(shí),方法中的 this 指向該對(duì)象。

var obj = {

? ? ? ? x: 10,

? ? foo: function () {

? ? ? ? console.log(this);? ? ? ? //Object

? ? ? ? console.log(this.x);? ? ? //10

? ? }

};

obj.foo();

注意:若是在對(duì)象方法中定義函數(shù),那么情況就不同了。

var obj = {

? ? x: 10,

? ? foo: function () {

? ? ? ? function f(){

? ? ? ? ? ? console.log(this);? ? ? //Window

? ? ? ? ? ? console.log(this.x);? ? //undefined

? ? ? ? }

? ? ? ? f();

? ? }

}

obj.foo();

可以這么理解:函數(shù) f 雖然是在 obj.foo 內(nèi)部定義的,但它仍然屬于一個(gè)普通函數(shù),this 仍指向 window。(這是個(gè)坑,要記牢)

在這里,如果想要調(diào)用上層作用域中的變量 obj.x,可以使用 self 緩存外部 this 變量。

var obj = {

? ? x: 10,

? ? foo: function () {

? ? ? ? var self = this;

? ? ? ? function f(){

? ? ? ? ? ? console.log(self);? ? ? //{x: 10}

? ? ? ? ? ? console.log(self.x);? ? //10

? ? ? ? }

? ? ? ? f();

? ? }

}

obj.foo();

如果 foo 函數(shù)不作為對(duì)象方法被調(diào)用:

var obj = {

? ? x: 10,

? ? foo: function () {

? ? ? ? console.log(this);? ? ? //Window

? ? ? ? console.log(this.x);? ? //undefined

? ? }

};

var fn = obj.foo;

fn();

obj.foo 被賦值給一個(gè)全局變量,并沒有作為 obj 的一個(gè)屬性被調(diào)用,那么此時(shí) this 的值是 window。

情況四:構(gòu)造函數(shù) prototype 屬性

function Foo(){

? ? this.x = 10;

}

Foo.prototype.getX = function () {

? ? console.log(this);? ? ? ? //Foo {x: 10, getX: function}

? ? console.log(this.x);? ? ? //10

}

var foo = new Foo();

foo.getX();

在 Foo.prototype.getX 函數(shù)中,this 指向的 foo 對(duì)象。不僅僅如此,即便是在整個(gè)原型鏈中,this 代表的也是當(dāng)前對(duì)象的值。

情況五:函數(shù)用 call、apply或者 bind 調(diào)用。

var obj = {

? ? x: 10

}

function foo(){

? ? console.log(this);? ? //{x: 10}

? ? console.log(this.x);? //10

}

foo.call(obj);

foo.apply(obj);

foo.bind(obj)();

當(dāng)一個(gè)函數(shù)被 call、apply 或者 bind 調(diào)用時(shí),this 的值就取傳入的對(duì)象的值。

情況六:DOM event this

在一個(gè) HTML DOM 事件處理程序里,this 始終指向這個(gè)處理程序所綁定的 HTML DOM 節(jié)點(diǎn):

function Listener(){?

? ? document.getElementById('foo').addEventListener('click', this.handleClick);? ? //這里的 this 指向 Listener 這個(gè)對(duì)象。不是強(qiáng)調(diào)的是這里的 this

}

Listener.prototype.handleClick = function (event) {

? ? console.log(this);? ? //<div id="foo"></div>

}

var listener = new Listener();

document.getElementById('foo').click();

這個(gè)很好理解,就相當(dāng)于是給函數(shù)傳參,使 handleClick 運(yùn)行時(shí)上下文改變了,相當(dāng)于下面這樣的代碼:

var obj = {

? ? x: 10,

? ? fn: function() {

? ? ? ? console.log(this);? ? ? ? //Window

? ? ? ? console.log(this.x);? ? ? //undefined

? ? }

};

function foo(fn) {

? ? fn();

}

foo(obj.fn);

你也可以用通過 bind 切換上下文:

function? Listener(){

? ? document.getElementById('foo').addEventListener('click',this.handleClick.bind(this));? ? ?

}

Listener.prototype.handleClick = function (event) {

? ? console.log(this);? ? //Listener {}

}

var listener = new Listener();

document.getElementById('foo').click();

前六種情況總結(jié)一句話為: this 指向調(diào)用該方法的對(duì)象。

情況七:箭頭函數(shù)中的 this

當(dāng)使用箭頭函數(shù)的時(shí)候,情況就有所不同了:箭頭函數(shù)內(nèi)部的 this 是詞法作用域,由上下文確定。

var obj = {

? ? x: 10,

? ? foo: function() {

? ? ? ? var fn = () => {

? ? ? ? ? ? return () => {

? ? ? ? ? ? ? ? return () => {

? ? ? ? ? ? ? ? ? ? console.log(this);? ? ? //Object {x: 10}

? ? ? ? ? ? ? ? ? ? console.log(this.x);? ? //10

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? fn()()();

? ? }

}

obj.foo();

現(xiàn)在,箭頭函數(shù)完全修復(fù)了 this 的指向,this 總是指向詞法作用域,也就是外層調(diào)用者 obj。

如果使用箭頭函數(shù),以前的這種 hack 寫法:

var self = this;

就不再需要了。

var obj = {

? ? x: 10,

? ? foo: function() {

? ? ? ? var fn = () => {

? ? ? ? ? ? return () => {

? ? ? ? ? ? ? ? return () => {

? ? ? ? ? ? ? ? ? ? console.log(this);? ? // Object {x: 10}

? ? ? ? ? ? ? ? ? ? console.log(this.x);? //10

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? fn.bind({x: 14})()()();

? ? ? ? fn.call({x: 14})()();

? ? }

}

obj.foo();

由于 this 在箭頭函數(shù)中已經(jīng)按照詞法作用域綁定了,所以,用 call()或者 apply()調(diào)用箭頭函數(shù)時(shí),無(wú)法對(duì) this 進(jìn)行綁定,即傳入的第一個(gè)參數(shù)被忽略。

補(bǔ)充說明:

this 為保留字,你不能重寫 this。

function test(){

? ? var this = {};? ? //Uncaught SyntaxError: Unexpected token this

}

宿主對(duì)象:

一門語(yǔ)言在運(yùn)行的時(shí)候,需要一個(gè)環(huán)境,叫做宿主環(huán)境。

對(duì)于JavaScript,宿主環(huán)境最常見的是 web 瀏覽器,瀏覽器提供了一個(gè) JavaScript 運(yùn)行的環(huán)境,這個(gè)環(huán)境里面,需要提供一些接口,好讓 JavaScript 引擎能夠和宿主環(huán)境對(duì)接。

JavaScript 引擎才是真正執(zhí)行 JavaScript 代碼的地方,常見的引擎有 V8(目前最快 JavaScript 引擎、Google 生產(chǎn))、JavaScript core。

在瀏覽器或者服務(wù)端( nodejs )都有自己的 JS 引擎,在瀏覽器中,全局對(duì)象為 window,而在 nodejs 中,全局對(duì)象為 global。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容