javascript復習筆記(3)

this

上次講了閉包,其實我感覺我上次沒講什么,閉包的確神秘,但是又不是很復雜,需要長篇大論去解釋。剛好,我看到了js中的另一個重點,this。this也是會讓初學者頭疼的一個知識點。有時候感覺莫名其妙,直覺上應該是這個答案,實際上卻是錯的。

錯誤的認識

  • this不只在js中出現,java, c++ 這些強類型語言也是有this的,不過它們都是叫this, 本質是不太一樣的。學過這些強類型語言的同學很容易就認為this是指向自己的。然而,這在js中,是不正確的。下面的代碼會打臉。
function fun (val) {
  this.a = val;
}
fun(0);
fun.a++;
console.log(fun.a);
console.log(a);

如果this是指向自己的話,那么現在fun.a應該是1咯,對吧。然而真的是這樣么? 打開chrome, F12將代碼拷貝在控制臺看看結果,fun.a的輸出是NaN,第二個log出卻是0。傻眼了吧?好吧,其實當時我也是一臉懵逼。這個結果,告訴了我們js的this不如你想象的那樣。這是你不知道的javaScript的this。

  • 除了認為this指向自己,《你不知道的javaScript》中也提到另一種錯誤的理解。this指向函數的作用域。詳細點說,就是一個函數,它的this是指向它的父級作用域。嗯,好吧,我覺得這種理解很自然,很舒服。下面的代碼,就是這種錯誤理解導致的錯誤寫法。
function a () {
  console.log(this.c);
}
function b() {
  let c = 1;
  this.a();
}
b(); // undefined

其實上面的結果不會輸出1,所以說明了這個理解也是錯誤的。

this的正確使用姿勢

this的指向呢,其實一句話就概況了。

this的綁定和函數聲明的位置沒有任何關系,只取決于函數的調用方式。

其實this的指向就看誰調用了它吧,總結下來,有以下4種情況:

  1. 默認綁定
    默認綁定,我曾經也聽到說成是直接函數調用。下面的代碼就是默認綁定
function fun () {
  console.log(this.a);
}
var a = 1;
fun() // 1

默認綁定的情況,函數中的this是綁定在window上,當然是在非嚴格模式下。在嚴格模式下,this是指向undefined, 上面的代碼就會拋出一個錯誤。說回非嚴格模式下的默認綁定,由于this指向了window。所以, this.a其實就是window.a。那么自然輸出了1。

  1. 隱式綁定
    當一個函數是某個對象的屬性時,我們直接對象.函數名調用該函數時,該函數的this是指向該對象的。其實,我個人認為,默認綁定的情況下 fun() 等價于 window.fun(),這也正好解釋了為什么默認綁定時,this是指向了全局對象。下面是隱式綁定的一個例子:
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

obj.func() // '123'

注意:隱式綁定當使用不當時,會出現this的丟失現象。比如:

// 情況一
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}
func = obj.func;
func(); // '1234'

// 情況二
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }
  }
}
func = obj.func();
func(); // '1234'

// 情況三
window.name = '1234';
let obj = {
  name: '123',
  func () {
    console.log(this.name);
  }
}

setTimeout(obj.func, 1000); // '1234'

情況一:不是隱式綁定了obj么,輸出應該是123才對啊。結果卻輸出了1234。嗯,我們要根據代碼來分析,this的指向要對比四種情況,然后去找到對應的情況來確定。func()很明顯就是之前我們說的默認綁定,所以輸出了全局對象中的a屬性的值。
情況二:情況二其實就是返回了一個函數,然后這個函數在全局作用域直接調用了,所以就是默認綁定,如果需要綁定原來的obj,可以用以下的辦法:

// 解法1
window.name = '1234';
let obj = {
  name: '123',
  func () {
    let that = this;
    return function () {
      console.log(that.name); 
    }
  }
}
func = obj.func();
func(); // '123'

// 解法2
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return () => console.log(this.name); 
   }
 }
func = obj.func();
func(); // '123'

// 解法3
window.name = '1234';
let obj = {
  name: '123',
  func () {
    return function () {
      console.log(this.name); 
    }.bind(this);
   }
 }
func = obj.func();
func(); // '123'

情況三: setTimeout的第一個參數是一個函數,其實setTimeout的內部實現的偽代碼應該是這樣的

function setTimeout (fn, delay) {
  // 等了 delay 毫秒
  fn()
}

所以其實也是變成了默認綁定嘛。

  1. 顯式綁定
    顯式綁定就是用js的call, apply, bind的這些函數來綁定this啦。
window.name = 'window';
let xiaoming = { name: 'xiaoming' };
function bar() { console.log(this.name); }
// call
bar.call(xiaoming); // 'xiaoming';
// apply
bar.apply(xiaoming); // 'xiaoming';
// bind
bar.bind(xiaoming)(); // 'xiaoming';

它們間的異同:

    • 它們三者可以用來改變函數this的指向, 第一個參數都是要指向的對象
    • apply 和 call 接受的參數不太一樣,call只能一個一個傳入參數,apply可以傳入一個參數的數組
    • bind返回的是綁定后的函數,apply 和 call是綁定時同時執行。
  1. new 綁定
    在js中,我們使用new運算符時,其實,經過了下面的操作

1、創建(或者說構造)一個全新的對象
2、這個新對象會被執行[[原型]]連接
3、這個新對象會被綁定到函數的this
4、如果函數沒有返回其他對象,那么new表達式中的函數調用會自動返回這個新對象

綜上,那么下面的代碼,

function fn (a) { this.a = a; }
let a = new fn(1);
console.log(a.a); // 1

就是上面的四個步驟過一遍的結果了,this綁定到了的這個新對象a上。

總結

逼逼了這么多。。其實this的指向只要弄清楚這四種情況,絕大部分的情況都可以自己判斷出來。其實還是會有些很奇葩的情況的,如果我沒記錯,但是根據我目前的開發經驗,只要你或者你同事不作死,就不會寫出那么奇葩的情況。如果說是面試題,當我沒說。來來來,最后總結遍,四個方法就是:

  • 如果是new出來的對象,那么this指向該對象無誤。
  • 如果有用顯式綁定,那么this指向綁定的對象
  • 是否作為一個對象的屬性來調用,是的話,this指向該對象。當然要注意this丟失的情況。
  • 如果上面都沒有,那么默認綁定應該無誤。

多練習慢慢就會一眼就看出this的指向了,工多手熟。如果有誤,請指出。還有祝大家國慶快樂!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,803評論 18 139
  • -1- 很少玩游戲的我,也被身邊朋友們安利了這款老少皆宜、帶妹兒無敵、堪稱社交神器的—王者榮耀! 下面就來說說我遇...
    8小雪兒8閱讀 915評論 0 1
  • 喜歡綠色,因為孕育生命 喜歡藤蔓,因為她無可依靠 卻堅強地追逐高度、陽光 不愿匍匐在低處,隨波逐流 夢想,正如藤蔓...
    會飛的豬心形的月亮閱讀 245評論 0 1
  • 有句話說:因為一個人,愛上一座城。而我是因為“一個人”,愛上一座城! 我是一個北方人,度過了一個人的世界已經28年...
    當年飯店閱讀 536評論 0 0
  • “我覺得我男朋友要跟我分手了,我是不是得先把他踹了。” 正在吃飯的我一口菜湯噴在桌子上,“何以見得?”為了不表現出...
    小小橘子皮閱讀 777評論 0 3