淺析Javascript 中的 this 的七種使用場景

Javascript 中的 this,有時候讓人迷惑,所以總結(jié)了一下關(guān)于this指向的問題。

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

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

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

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

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

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

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

var x = 10;

function foo(){

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

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

}

foo();

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

所謂的構(gòu)造函數(shù)就是由一個函數(shù) new 出來的對象,一般構(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 出來的對象。

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

function Foo(){

this.x = 10;

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

}

var foo = Foo();

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

情況三:對象方法

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

var obj = {

x: 10,

foo: function () {

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

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

}

};

obj.foo();

注意:若是在對象方法中定義函數(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)部定義的,但它仍然屬于一個普通函數(shù),this 仍指向 window。

在這里,如果想要調(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ù)不作為對象方法被調(diào)用:

var obj = {

x: 10,

foo: function () {

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

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

}

};

var fn = obj.foo;

fn();

obj.foo 被賦值給一個全局變量,并沒有作為 obj 的一個屬性被調(diào)用,那么此時 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 對象。不僅僅如此,即便是在整個原型鏈中,this 代表的也是當前對象的值。

情況五:函數(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)();

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

情況六:DOM event this

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

function Listener(){

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

}

Listener.prototype.handleClick = function (event) {

console.log(this);? ? //

}

var listener = new Listener();

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

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

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)用該方法的對象。

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

當使用箭頭函數(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ù)完全修復了 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ù)時,無法對 this 進行綁定,即傳入的第一個參數(shù)被忽略。

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

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

  • 工廠模式類似于現(xiàn)實生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實現(xiàn)同樣的效果;這時候需要使用工廠模式。簡單...
    舟漁行舟閱讀 8,110評論 2 17
  • 單例模式 適用場景:可能會在場景中使用到對象,但只有一個實例,加載時并不主動創(chuàng)建,需要時才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,311評論 1 10
  • 在面向?qū)ο蟮恼Z言中(例如Java,C#等),this含義是明確且具體的,即指向當前對象。一般在編譯期綁定。而在ja...
    一木_qintb閱讀 435評論 0 0
  • 與其他語言相比,函數(shù)的this關(guān)鍵字在JavaScript中的表現(xiàn)略有不同,此外,在嚴格模式和非嚴格模式之間也會有...
    codingC閱讀 637評論 0 0
  • 你再說我胖,我就說你丁丁小。 壹 濱崎步又被掛熱搜了,繼濱崎步聾了,濱崎步結(jié)婚了,濱崎步離婚了后,濱崎步胖了光榮登...
    是年閱讀 499評論 0 2

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