前端的那些事(四):this指向

前言

我們這里將從最底層的顯隱式this綁定等類型去闡述记焊,讓你對this有一個清晰的認(rèn)識巾腕。采用逆向思維去講述。

一句話解釋this綁定

常見說法:誰調(diào)用它棚点,this就指向誰。
嚴(yán)格來說:this 的指向湾蔓,是在調(diào)用函數(shù)時根據(jù)執(zhí)行上下文所動態(tài)確定的瘫析。

啥意思呢,咱們來解釋下,看如下demo

function f1 () {
    console.log(this)
}
function f2 () {
    'use strict'
    console.log(this)
}
f1() // window
f2() // undefined

此時我們調(diào)用的f1在window上颁股,所以指向window么库。嚴(yán)格模式就是undefined(ps:react,vue等就是嚴(yán)格模式)甘有。

this優(yōu)先級

new(構(gòu)造函數(shù)) > call/apply/bind > 上下文對象中 > 普通函數(shù)
(這里公理诉儒,可以通過綁定不同情況this驗證。)

通過優(yōu)先級進行this的判斷

  1. 函數(shù)是否在new中調(diào)用(new綁定)?如果是的話this綁定的是新創(chuàng)建的對象亏掀。
    var bar = new foo()
  2. 函數(shù)是否通過call忱反、apply(顯式綁定)或者硬綁定調(diào)用?如果是的話,this綁定的是 指定的對象滤愕。
    var bar = foo.call(obj2)
  3. 函數(shù)是否在某個上下文對象中調(diào)用(隱式綁定)?如果是的話温算,this 綁定的是那個上 下文對象。
    var bar = obj1.foo()
  4. 如果都不是的話间影,使用(默認(rèn)綁定)注竿。如果在嚴(yán)格模式下,就綁定到undefined魂贬,否則綁定到 全局對象巩割。
    var bar = foo()
  5. 箭頭函數(shù) => 箭頭函數(shù)會繼承外層函數(shù)調(diào)用的 this 綁定,self = this 機制一樣付燥。

前面記住宣谈,面試夠用了,想了解更深入的請看逐一分析键科,說實話平時寫代碼闻丑,直接箭頭函數(shù)和bind就完美解決這些尷尬問題了,但是如果你想寫更底層的東西勋颖,做更高級嗦嗡,甚至專家級別,以下也是必備的饭玲。

逐一分析:

ps: 我們只在非嚴(yán)格模式討論酸钦,嚴(yán)格模式默認(rèn)綁定指向underfined。

1. 默認(rèn)綁定

 function foo() {
   console.log(this.a)
 }
 var a = 1;
 foo();    // 1

分析:我們看 foo()在最外層的window上面咱枉,根據(jù)所以根據(jù)誰調(diào)用它卑硫,this就指向誰的原則,所以window上調(diào)用了foo蚕断,window.a就是我們在window上設(shè)置的var a = 1欢伏,則this.a = 1;

2. 隱式綁定

2.1正常情況

function foo() {
     console.log(this.a);
 }

 var obj = {
     a: 1,
     foo: foo
 }

 obj.foo();      // 1  

分析:我們看 foo()obj上面,根據(jù)所以根據(jù)誰調(diào)用它亿乳,this就指向誰的原則硝拧,所以obj上調(diào)用了foo径筏,那么this指向obj,所以this.a就是obj.a;

2.2 隱式綁定丟失(主動賦值)

function foo() {
  console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo
};
var a = 1;
var bar = obj.foo; // 只是賦值 沒有調(diào)用執(zhí)行
bar();  // 1

分析:我們看 foo()obj上面障陶,根據(jù)所以根據(jù)誰調(diào)用它滋恬,this就指向誰的原則,所以obj上調(diào)用了foo抱究,那么this指向obj恢氯,所以this.a就是obj.a;

看似是丟失,其實原因在于調(diào)用的位置鼓寺,不信咱稍微改一下這句話var bar = obj.foo

function foo() {
  console.log(this.a);
}
var obj = {
  a: 2,
  foo: foo
};
var a = 1;
var bar = obj.foo() ; // 2

我們此時就直接 var bar = obj.foo()勋拟,咋答案就是2了呢?

分析:我們對比下第一種方法是用var bar = obj.foo妈候;吧obj.foo賦值給bar敢靡,然后我們調(diào)用了bar(),調(diào)用的時候已經(jīng)在window上了苦银。而第二種我們是直接obj.foo()啸胧,其實還是在obj上調(diào)用的(就是普通的隱式綁定)。所以原理就是在哪調(diào)用這個函數(shù)幔虏,this就指向誰纺念。

2.3 隱式綁定丟失(函數(shù)傳參)

function foo() {
  console.log('a', this.a);
}
var baz = {
  a: 2,
  foo: foo
};

var a = 1;

setTimeout(baz.foo, 100);

分析:一句話,函數(shù)傳參就是隱式賦值所计,跟主動賦值是一個道理柠辞。

3. 顯式綁定(call团秽、apply主胧、bind)

3.1普通顯式綁定call、apply

function foo() {
  console.log(this.a);
}
var obj = { a: 2 };
var obj2 = {a: 3}
foo.call(obj); // 2
foo.call(obj2); // 3

分析call习勤,apply的參數(shù)是誰踪栋,this就指向誰。
此時你會發(fā)現(xiàn)之前說的誰調(diào)用它图毕,this就指向誰夷都,就看不出來了,所以嚴(yán)格來說this 的指向予颤,是在調(diào)用函數(shù)時根據(jù)執(zhí)行上下文所動態(tài)確定的囤官。

這里順帶說一下call,apply區(qū)別蛤虐,亮代碼:

function foo(b, c) {
  console.log(this.a, b, c, arguments);
}
var obj = { a: 2 };
foo.call(obj, 3, 4); // 2,3,4

foo.apply(obj, [5, 6]); // 2,5,6

不同的:傳參不同党饮,call是單個傳參依次傳遞,apply是傳遞數(shù)組驳庭,傳遞的就是foo規(guī)定好的參數(shù)刑顺。
(我們也可以通過arguments獲取傳遞來的參數(shù)氯窍。)

3.2 硬綁定與Bind

大家看上面普通顯式綁定,每一次call不同的對象蹲堂,指向就發(fā)生變化狼讨,那么我們想讓this一直不變呢,那就使用硬綁定:

function foo() {
  console.log(this.a);
}
var obj = { a: 2 };
var obj2 = { a: 3 };

var bar = function() {
  foo.call(obj);
};

bar(); // 2
bar.call(obj); // 2
bar.call(obj2); // 3

分析:我們創(chuàng)建了函數(shù) bar()柒竞,并在它的內(nèi)部手動調(diào)用了foo.call(obj)政供,因此強制把foo 的 this 綁定到了 obj。無論之后如何調(diào)用函數(shù) bar能犯,它 總會手動在 obj上調(diào)用foo鲫骗。這種綁定是一種顯式的強制綁定,因此我們稱之為硬綁定踩晶。

Bind是es5內(nèi)置的硬綁定:

function foo() {
  console.log(this.a);
}
var obj = { a: 2 };
var obj2 = { a: 3 };

var bar = foo.bind(obj);

bar(); // 2
bar.call(obj); // 2
bar.call(obj2); // 3

源碼分析执泰,請看這篇文章,說得很好:從一道面試題渡蜻,到“我可能看了假源碼”

3.3 硬綁定:softBind
這里不介紹了术吝,用的很少,有興趣同學(xué)可以研究下茸苇。

4. 構(gòu)造函數(shù)new 和 this

首先介紹一個常見面試題:new做了什么排苍?

  1. 創(chuàng)建一個新的對象;
  2. 將構(gòu)造函數(shù)的 this 指向這個新對象学密;
  3. 為這個對象添加屬性淘衙、方法等;
  4. 最終返回新對象腻暮。

代碼解釋:

var obj  = {}
obj.__proto__ = Foo.prototype
Foo.call(obj)

如下是介紹this指向

  1. 普通版
function Foo() {
    this.a = "a"
}
const f = new Foo()
console.log(f.a) // 'a'
  1. return 非對象版
function Foo() {
    this.a = "a"
    return 1
}
const f = new Foo()
console.log(f.a) // 'a'
  1. retrun 對象版
function Foo() {
    this.a = "a"
    return {a: "真假美猴王a"}
}
const f = new Foo()
console.log(f.a) // '真假美猴王a'

結(jié)論:如果構(gòu)造函數(shù)中顯式返回的是一個對象彤守,那么 this 就指向這個返回的對象;如果返回的不是一個對象哭靖,那么 this 仍然指向實例具垫。

4. 箭頭函數(shù)中的 this 指向

  1. 普通 function 版
const foo = {
  a: '1',
  fn: function() {
    setTimeout(function() {
      console.log(this);
    });
  }
};
foo.fn();  // window
  1. 箭頭函數(shù) => 版
const foo = {
  a: '1',
  fn: function() {
    setTimeout(() => {
      console.log(this);
    });
  }
};
foo.fn(); // {a: "1", fn: ?}

結(jié)論:箭頭函數(shù)會繼承外層函數(shù)調(diào)用的 this 綁定,和self = this 機制一樣试幽。


更多內(nèi)容可以看我的集錄: 全面攻陷js:更新中...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末筝蚕,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子铺坞,更是在濱河造成了極大的恐慌起宽,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件济榨,死亡現(xiàn)場離奇詭異坯沪,居然都是意外死亡,警方通過查閱死者的電腦和手機腿短,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門屏箍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來绘梦,“玉大人,你說我怎么就攤上這事赴魁⌒斗睿” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵颖御,是天一觀的道長榄棵。 經(jīng)常有香客問我,道長潘拱,這世上最難降的妖魔是什么疹鳄? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮芦岂,結(jié)果婚禮上瘪弓,老公的妹妹穿的比我還像新娘。我一直安慰自己禽最,他們只是感情好腺怯,可當(dāng)我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著川无,像睡著了一般呛占。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上懦趋,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天晾虑,我揣著相機與錄音,去河邊找鬼仅叫。 笑死帜篇,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的惑芭。 我是一名探鬼主播坠狡,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼继找,長吁一口氣:“原來是場噩夢啊……” “哼遂跟!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婴渡,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤幻锁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后边臼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體哄尔,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年柠并,在試婚紗的時候發(fā)現(xiàn)自己被綠了岭接。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片富拗。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸣戴,靈堂內(nèi)的尸體忽然破棺而出啃沪,到底是詐尸還是另有隱情,我是刑警寧澤窄锅,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布创千,位于F島的核電站,受9級特大地震影響入偷,放射性物質(zhì)發(fā)生泄漏追驴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一疏之、第九天 我趴在偏房一處隱蔽的房頂上張望殿雪。 院中可真熱鬧,春花似錦锋爪、人聲如沸冠摄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽河泳。三九已至,卻和暖如春年栓,著一層夾襖步出監(jiān)牢的瞬間拆挥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工某抓, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纸兔,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓否副,卻偏偏與公主長得像汉矿,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子备禀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1洲拇、調(diào)用位置 在理解this的綁定之前,首先理解調(diào)用位置,決定this的綁定 function a() { // ...
    winerss閱讀 298評論 0 0
  • 一曲尸、你不知道的JavaScript 1赋续、作用域 作用域 LHS RHS RHS查詢與簡單地查找某個變量的值別無二...
    頂兒響叮當(dāng)閱讀 345評論 0 0
  • JavaScript(面向?qū)ο?原型理解+繼承+作用域鏈和閉包+this使用總結(jié)) 一、面向?qū)ο?1另患、什么是面向?qū)?..
    老頭子_d0ec閱讀 297評論 0 0
  • 與其他語言相比纽乱,函數(shù)的this關(guān)鍵字在JavaScript中的表現(xiàn)略有不同,此外昆箕,在嚴(yán)格模式和非嚴(yán)格模式之間也會有...
    codingC閱讀 567評論 0 0
  • 特別說明,為便于查閱薯嗤,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 689評論 0 1