this

一、使用this的原因(why)

對于前端開發(fā)者來說慨蛙,this關(guān)鍵字是JavaScript中最復(fù)雜的機(jī)制之一,不同的位置使用纪挎,指向是不一樣的期贫,所以它到底有什么作用呢?為什么要花大量的時(shí)間去研究他呢

  • this提供了一種優(yōu)雅的方式來隱式的“傳遞”一個對象的引用异袄,可以方便我們簡潔的設(shè)計(jì)API通砍。特別是當(dāng)結(jié)構(gòu)越來越復(fù)雜時(shí)。
function GetObj (name) {
    this.name = name
}
GetObj.prototype.showName = function (){
alert(this.name)  // this指代GetObj這個對象
}
let p1 = new GetObj('張三')
p1.showName()

二烤蜕、了解this(what----this到底是什么)

1封孙、對this的誤解
  • 認(rèn)為this指向函數(shù)自身
function foo(num) {
   console.log('foo===', num)  // foo: 6 // foo: 7 // foo: 8 // foo: 9 
  // 記錄 foo 被調(diào)用的次數(shù) 
  this.count++
}
foo.count = 0
for(var i = 0; i< 10; i++){
   if (i > 5) {         
      foo( i );    
   } 
}
 console.log( foo.count ); // 0 

this并沒有指向自身的foo函數(shù),而是指向了window讽营。

  • 認(rèn)為this的作用域指向函數(shù)的作用域
    在某種情況下它 是正確的虎忌,但是在其他情況下它卻是錯誤的
function foo(){ 
    var a = 2; 
    this.bar(); 
} 
function bar() { 
    console.log(this); //window
    console.log(this.a); // undefined
} 
foo();

這個a作用域存在于foo函數(shù)中,所以在window這個作用域中找不到橱鹏。但是由于var定義的a膜蠢,在詞法分析階段會將a進(jìn)行變量提升堪藐,所以window中會有一個a,但是沒有值

2挑围、與自然語言的對比

實(shí)際上js中的this和我們自然語言中的代詞有類似性礁竞。比如英語中我們寫"小明,你吃了么贪惹?"

注意上面的代詞"小明",我們當(dāng)然可以這樣寫:"小明苏章,小明吃了么?" ,這種情況下我們沒有使用this去重用代替小明奏瞬。
主要和執(zhí)行時(shí)候的上下文環(huán)境有關(guān)聯(lián)

3枫绅、那this到底是什么呢?

this只跟函數(shù)的調(diào)用位置有關(guān)硼端,是在函數(shù)被調(diào)用時(shí)發(fā)生綁定的并淋;this的指向取決于函數(shù)在哪里被調(diào)用。

this指向的最終對象珍昨,跟調(diào)用位置以及應(yīng)用的綁定規(guī)則有關(guān)

三县耽、綁定規(guī)則(how--如何尋找函數(shù)的調(diào)用位置)

1、 默認(rèn)綁定
  • 最常見的綁定規(guī)則镣典,獨(dú)立函數(shù)調(diào)用時(shí)默認(rèn)綁定兔毙,也可以看做無法應(yīng)用其他綁定規(guī)則時(shí)的默認(rèn)規(guī)則
function foo() {      
    console.log( this.a ); // this指向window
} 
var a = 2; 
 
foo(); // 2
  • this指向
    • 在全局環(huán)境中,this 指向全局對象兄春,在瀏覽器中澎剥,它就是 window 對象。
    • 普通函數(shù)是在全局環(huán)境中被調(diào)用:
      非嚴(yán)格模式下:指向全局對象window
      嚴(yán)格模式下:指向undefined(使用嚴(yán)格模式的時(shí)候赶舆,全局對象無法使用默認(rèn)綁定)
function foo() {      
  "use strict";      
  console.log( this );  // undefined
  console.log( this.a );  // Uncaught TypeError: Cannot read property 'a' of undefined
} 
 "use strict";  
console.log( this ) // window
var a = 2; 
foo(); 
2哑姚、 隱式綁定
  • 當(dāng)函數(shù)的調(diào)用存在上下文對象,或者說是否被某個對象擁有或者包含芜茵。this將會被綁定到這個上下文對象叙量。
    this 始終會指向直接調(diào)用函數(shù)的上一級對象
function foo() {      
    console.log( this.a );   // 指向obj
} 
var obj = {      
    a: 2,     
    foo: foo  //--->foo: function () {      console.log( this.a );  } 
}; 
obj.foo(); // 2
  • 函數(shù)嵌套,每個function函數(shù)(非箭頭函數(shù))在每次調(diào)用時(shí)都會在函數(shù)內(nèi)生成一個自己的this九串。
    當(dāng)兩個函數(shù)嵌套定義時(shí)绞佩,內(nèi)層函數(shù)中的this與外層函數(shù)中的this是完全獨(dú)立的。函數(shù)內(nèi)this的值是在函數(shù)調(diào)用時(shí)才確定的蒸辆,函數(shù)的調(diào)用方式不同征炼,this也就不同
    1,當(dāng)函數(shù)直接調(diào)用時(shí) fn(); 函數(shù)內(nèi)this的值是window對象躬贡,(在js嚴(yán)格模式"use strict";下谆奥,函數(shù)內(nèi)this的值是null)
    2,當(dāng)把這個函數(shù)賦值給一個對象的方法obj.abc = fn;
    調(diào)用obj.abc()時(shí)拂玻,函數(shù)內(nèi)this的值是obj對象,也就是函數(shù)所在的對象酸些。
var obj = {
    y: function() {
        console.log(this === obj);   // true
        console.log(this);   // Object {y: function}
        fn();  // 嵌套的函數(shù)不是對象的方法宰译,直接調(diào)用,所以this指向window

        function fn() {
            console.log(this === obj);   // false
            console.log(this);   // Window 全局對象
        }
    }
}

obj.y();  
  • 問題:被隱式綁定的函數(shù)會丟失綁定對象魄懂,會應(yīng)用默認(rèn)綁定沿侈,從而把 this 綁定到全局對象或者 undefined 上,取決于是否是嚴(yán)格模式市栗。
    1缀拭、 函數(shù)引用傳遞給新的全局變量
function foo() {      
    console.log( this.a ); 
} 
var obj = {      
    a: 2,     
    foo: foo  
}; 
var bar = obj.foo; // bar是全局變量
var a = "oops, global"; // a 是全局對象的屬性 
bar(); // "oops, global",bar() 其實(shí)是一個不帶任何修飾的函數(shù)調(diào)用

2填帽、將函數(shù)作為參數(shù)傳入回調(diào)函數(shù)中

function foo() {      
    console.log( this.a ); 
} 
function doFoo(fn) {     // fn 其實(shí)引用的是 foo 
    fn(); // <-- 調(diào)用位置蛛淋! 
} 
var obj = {     
    a: 2,     
    foo: foo  
}; 
var a = "oops, global"; // a 是全局對象的屬性 
doFoo( obj.foo ); // "oops, global"
3、顯式綁定
使用 call()和apply() 方法進(jìn)行強(qiáng)制綁定
  • 他們的第一個參數(shù)都是指定函數(shù)運(yùn)行時(shí)的this指向篡腌,第一個參數(shù)不傳(參數(shù)為空)褐荷。或者為null嘹悼、undefined叛甫。默認(rèn) this 指向全局對象(非嚴(yán)格模式)或 undefined(嚴(yán)格模式)。
var x = 1;

var obj = {
  x: 2
}

function fn() {
    console.log(this);
    console.log(this.x);
}

fn.call(obj)
// Object {x: 2}
// 2
fn.apply(obj)     
// Object {x: 2}
// 2

fn.call()         
// Window 全局對象
// 1

fn.apply(null)    
// Window 全局對象
// 1

fn.call(undefined)    
// Window 全局對象
// 1
  • 使用 call() 和 apply() 傳入的this對象為原始值(字符串類型杨伙,布爾類型或者數(shù)字類型)其监,這個原始值就會被轉(zhuǎn)換成它的對象形式(new String()、new Bollean()限匣、new Number())
  • 區(qū)別
    call()的第二個以及后續(xù)的參數(shù)是一個列表
    apply()的第二個參數(shù)是一個數(shù)組棠赛,所有的參數(shù)放在這個數(shù)組中
    參數(shù)列表和參數(shù)數(shù)組都將作為函數(shù)的參數(shù)進(jìn)行執(zhí)行
var x = 1
var obj = {
    x: 2
}

function sum (y, z) {
    console.log(this.x + y +z)
}
sum(3, 4) // 8
sum.call(obj, 3, 4) // 9
sum.apply(obj, [3, 4]) // 9
使用 bind() 方法進(jìn)行強(qiáng)制綁定

調(diào)用 f.bind(someObject) 會創(chuàng)建一個與 f 具有相同函數(shù)體和作用域的函數(shù),但是在這個新函數(shù)中膛腐,新函數(shù)的 this 會永久的指向 bind 傳入的第一個參數(shù),無論這個函數(shù)是如何被調(diào)用的鼎俘。

var x = 1
var obj1 = {
    x: 2
}
var obj2 = {
    x: 3
}

function fn () {
    console.log(this)
    console.log(this.x)
}
var a = fn.bind(obj1)
var b = a.bind(obj2)

fn() // window 1

a() // {x: 2} 2

b() // {x: 2} 2 

a.call(obj2) // {x: 2} 2

在上面的例子中哲身,雖然我們嘗試給函數(shù) a 重新指定 this 的指向,但是它依舊指向第一次 bind 傳入的對象贸伐,即使是使用 call 或 apply 方法也不能改變這一事實(shí)勘天,即永久的指向 bind 傳入的第一次參數(shù)。

4捉邢、new綁定

使用 new 關(guān)鍵字脯丝,通過構(gòu)造函數(shù)生成一個實(shí)例對象。此時(shí)伏伐,this 便指向這個新對象

var x = 1;

function Fn() {
   this.x = 2;
    console.log(this);  // Fn {x: 2}
}

var obj = new Fn();   // obj和Fn(..)調(diào)用中的this進(jìn)行綁定
console.log(obj.x)   // 2

綁定規(guī)則優(yōu)先級

如果某個調(diào)用位置可以應(yīng)用多條規(guī)則該怎么辦宠进?為了 解決這個問題就必須給這些規(guī)則設(shè)定優(yōu)先級。
毫無疑問藐翎,默認(rèn)綁定的優(yōu)先級別最低材蹬。
隱式綁定和顯示綁定誰的優(yōu)先級別更高呢实幕?

function fn(){
    console.log(this.a)
}
let obj1 = {
    a: 1,
    fn: fn
}
let obj2 = {
    a: 2,
    fn: fn
}
obj1.fn()//1
obj2.fn()//2
obj1.fn.call(obj2)//2
obj2.fn.apply(obj1)//1
  • 可以看到,顯式綁定優(yōu)先級更高

隱式綁定和new()綁定誰的優(yōu)先級別更高呢堤器?

function foo(something) {      
    this.a = something; 
} 
var obj1 = {      
    foo: foo 
}; 
var obj2 = {}; 
obj1.foo( 2 );  
console.log( obj1.a ); // 2 

obj1.foo.call( obj2, 3 );  
console.log( obj2.a ); // 3 
 
var bar = new obj1.foo( 4 );  
console.log( obj1.a ); // 2  
console.log( bar.a ); // 4
  • 可以看到 new 綁定比隱式綁定優(yōu)先級高昆庇。

但是 new 綁定和顯式綁定誰的優(yōu)先級更高呢

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

bar 被硬綁定到 obj1 上,但是 new bar(3) 并沒有像我們預(yù)計(jì)的那樣把 obj1.a 修改為 3闸溃。相反整吆,new 修改了硬綁定(到 obj1 的)調(diào)用 bar(..) 中的 this。因?yàn)槭褂昧?new 綁定辉川,我們得到了一個名字為 baz 的新對象表蝙,并且 baz.a 的值是 3。

當(dāng)某個函數(shù)調(diào)用應(yīng)用了這四種規(guī)則中的多條员串,那么優(yōu)先級:new綁定 > 顯示綁定 > 隱式綁定 > 默認(rèn)綁定

5勇哗、箭頭函數(shù)中this指向

箭頭函數(shù)表達(dá)式的語法比函數(shù)表達(dá)式更簡潔,并且沒有自己的this寸齐,arguments欲诺,super或new.target。箭頭函數(shù)表達(dá)式更適用于那些本來需要匿名函數(shù)的地方渺鹦,并且它不能用作構(gòu)造函數(shù)扰法。
箭頭函數(shù)沒有自己的this綁定。箭頭函數(shù)中使用的this毅厚,其實(shí)是直接包含它的那個函數(shù)或函數(shù)表達(dá)式中的this

var a = 1;
let obj = {
    a: 2,
    fn1: () => {
        console.log('fn1',this.a) // 1 this指向window
        let fn3 = () => {
            console.log('fn3',this.a) // 1 this指向window
        }
        fn3()
        
        let fn4 = () => {
            console.log('fn4',this.a) // 1 this指向window
        }
        fn4.call(obj)
        // 由于箭頭函數(shù)沒有自己的this指針塞颁,通過 call() 或apply() 方法調(diào)用一個函數(shù)時(shí),只能傳遞參數(shù)吸耿,他們的第一個參數(shù)會被忽略祠锣。
        fn4.apply(obj)
    },
    fn2: function() {
        console.log('fn2',this.a) // 2 this指向obj
    }
}
obj.fn1()
obj.fn2()

箭頭函數(shù)的this看外層的是否有函數(shù),如果有咽安,外層函數(shù)的this就是內(nèi)部箭頭函數(shù)的this伴网,如果沒有,則this是window妆棒。- 從父作用域繼承this
同 bind 一樣澡腾,箭頭函數(shù)也很“頑固”,無法通過 call 和 apply 來改變 this 的指向糕珊,即傳入的第一個參數(shù)被忽略动分。

圖片轉(zhuǎn)自掘金小冊-前端面試之道

image.png

總結(jié)

如果要判斷一個運(yùn)行中函數(shù)的 this 綁定,就需要找到這個函數(shù)的直接調(diào)用位置红选。找到之后 就可以順序應(yīng)用下面這四條規(guī)則來判斷 this 的綁定對象澜公。

  1. 由 new 調(diào)用?綁定到新創(chuàng)建的對象纠脾。
  2. 由 call 或者 apply(或者 bind)調(diào)用玛瘸?綁定到指定的對象蜕青。
  3. 由上下文對象調(diào)用?綁定到那個上下文對象糊渊。
  4. 默認(rèn):在嚴(yán)格模式下綁定到 undefined右核,否則綁定到全局對象。
    一定要注意渺绒,有些調(diào)用可能在無意中使用默認(rèn)綁定規(guī)則贺喝。如果想“更安全”地忽略 this 綁 定,你可以使用一個 DMZ 對象宗兼,比如 ? = Object.create(null)躏鱼,以保護(hù)全局對象。
    ES6 中的箭頭函數(shù)并不會使用四條標(biāo)準(zhǔn)的綁定規(guī)則殷绍,而是根據(jù)當(dāng)前的詞法作用域來決定 this染苛,具體來說,箭頭函數(shù)會繼承外層函數(shù)調(diào)用的 this 綁定(無論 this 綁定到什么)主到。這 其實(shí)和 ES6 之前代碼中的 self = this 機(jī)制一樣茶行。

https://www.cnblogs.com/kidsitcn/p/10985338.html
https://zhuanlan.zhihu.com/p/71490991
https://zhuanlan.zhihu.com/p/28536635
嵌套函數(shù)指向

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市登钥,隨后出現(xiàn)的幾起案子畔师,更是在濱河造成了極大的恐慌,老刑警劉巖牧牢,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件看锉,死亡現(xiàn)場離奇詭異,居然都是意外死亡塔鳍,警方通過查閱死者的電腦和手機(jī)伯铣,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轮纫,“玉大人懂傀,你說我怎么就攤上這事±校” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵恃泪,是天一觀的道長郑兴。 經(jīng)常有香客問我,道長贝乎,這世上最難降的妖魔是什么情连? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮览效,結(jié)果婚禮上却舀,老公的妹妹穿的比我還像新娘虫几。我一直安慰自己,他們只是感情好挽拔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布辆脸。 她就那樣靜靜地躺著,像睡著了一般螃诅。 火紅的嫁衣襯著肌膚如雪啡氢。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天术裸,我揣著相機(jī)與錄音倘是,去河邊找鬼。 笑死袭艺,一個胖子當(dāng)著我的面吹牛搀崭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播猾编,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼瘤睹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了袍镀?” 一聲冷哼從身側(cè)響起默蚌,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎苇羡,沒想到半個月后绸吸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡设江,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年锦茁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叉存。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡码俩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出歼捏,到底是詐尸還是另有隱情稿存,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布瞳秽,位于F島的核電站瓣履,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏练俐。R本人自食惡果不足惜袖迎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧燕锥,春花似錦辜贵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至连霉,卻和暖如春榴芳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背跺撼。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工窟感, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人歉井。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓柿祈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親哩至。 傳聞我的和親對象是個殘疾皇子躏嚎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354