前幾天發佈的Javavscript基礎——原型和原型鏈 收藏轉化率還挺高,看來你們對於JS基礎知識仍是很看重的,因爲JS語言設計的關係,不少語言特性不是那麼清晰。好比經典的this在哪的問題。javascript
本文研究一下Javascript的this指向。相信學完以後應該沒啥問題。java
Javascript的this指向問題,有些人可能以爲很簡單,有些人卻以爲撲朔迷離,看完本文以後相應會對this的掌握有一個直觀的判斷,而不是"開局全靠猜"。面試
調用方式
肯定,跟定義環境無關。定義環境
決定,與調用方式無關
,也不能夠bind(this)
。window
undefined
如下討論均爲非嚴格模式
,這個不影響今天的討論。segmentfault
說結論每每是讓人難以理解的,下面經過不一樣的調用場景對this作一個說明。app
function test() { console.log(this); } test(); // 輸出undefined
直接調用是最簡單的, 大部分人在這裏都能回答的很好。函數
直接調用時this指向
全局做用域
。this
- 非嚴格模式this指向window
- 嚴格模式this指向undefined
'use strict' var n = 1; var a = { n: 2, b: function() { console.log(this.n); } }; a.b(); // 輸出2 var b = a.b; b(); // 輸出1
非嚴格模式下,輸出2和1,嚴格模式下輸出2和一個報錯(this指向undefined,訪問undefined的n屬性確定報錯)
那若是你這麼回答,滿分
!設計
知其然還要知其因此然,咱們分析一下:code
爲何輸出2?對象
由於
a.b()
是對象調用方式,因此b()中的this指向a
爲何輸出1?
這個很是有意思,並且也頗有迷惑性,面試的時候常常問到,也常常有人被問倒。
var b = a.b
把a.b
賦值給變量b
,b就是一個函數,請注意: 這裏只是賦值,沒有調用,因此b中的this指向還不肯定
。
b();
調用函數b
,這是什麼調用方式? 普通調用
,因此this指向全局做用域。
對象調用方式下this指向調用對象。
是否GET? 若是沒有GET,請關注公衆號NodeJs之路
,我在線給你答疑。
開胃菜已經吃了,下面來點"有難度的(實際上也沒啥難度)"。
var n = 1; var a = { n: 2, b: { n: 3, c: function() { console.log(this.n) } } };
正確答案:未肯定調用環境
的狀況下,this的指向不肯定
。錯誤答案:指向a.b對象,Too young too simple!
var n = 1; var a = { n: 2, b: { n: 3, c: function() { console.log(this.n) } } }; a.b.c(); // 輸出3 a.c = a.b.c; a.c(); // 輸出2 var c = a.b.c; c(); // 輸出1
這道題跟以前那道對象調用
很像。
爲何輸出3?
對象調用方式下指向調用對象,a.b.c()中c()是經過a.b
對象調用,指向對象a.b
爲何輸出?
a.c = a.b.c 給a對象定義一個函數c,注意,此時沒有調用!this指向不肯定a.c() 經過a對象來調用c(),因此this指向對象
a
爲何輸出1?
var c = a.b.c 函數賦值給普通變量,注意,此時沒有調用!c(); 普通方式調用,指向window
嵌套對象調用方式下,this指向最終調用
函數的對象。
a.b.c.d.e.f.g.h()
h函數中的this指向a.b.c.d.e.f.g
var name = 1; function Person() { this.name = 2; } var p1= Person(); // p1爲undefined console.log(p1.name); // 報錯 var p2 = new Person(); console.log(p2.name); // 輸出2
p1爲何是undefined?
這道題比較坑,跟調用方式和this指向無關,由於Person函數沒有返回值,js中,默認會返回undefined.
p2.name爲何是2?
使用new操做符時,構造函數的返回值
默認
指向對象實例,因此p2.name就是Person()中的this.name
若是在Person()
函數中加上return this
的話,Person()
返回值仍是this
,由於這是普通調用。
原則上構造函數不該該有返回值,可是若是真的寫了會怎麼樣?咱們來探討一下。
function Person() { this.name = 2; return {}; } var p1 = new Person(); console.log(p1.name)// 輸出undefined
雖然Js只有對象,可是有一些如string,number這種通常叫作簡單對象,date,regex,array,object等等叫複雜對象。
function Person() { this.name = 2; return 1; } var p1 = new Person(); console.log(p1.name)// 輸出2
使用typeof null
返回的是[object]
,證實null是個對象,不過我們來看看構造函數返回null的表現。
function Person() { this.name = 2; return null; } var p1 = new Person(); console.log(p1.name)// 輸出2
構造函數中this指向對象實例自己,若是構造函數指明瞭返回值,那麼表現以下:
var a = { n: 1 }; var b = { n: 2 } function f() { console.log(this.n); } var fa = f.bind(a); var fb = fa.bind(b); fa(); // 輸出1 fb(); // 輸出1
第1個輸出1應該不難理解,bind能夠更改function內部的this指向。屢次bind已經bind過的函數,this指向不變。
bind的實現原理有點複雜,將在下一篇文章進行詳細解讀。
bind能夠手動綁定function的this,this
指向第1次
bind時的this。
這兩個函數在this指向上表現一致,放到一塊兒講
var a = { n: 1 }; var b = { n: 2 } function f() { console.log(this.n); } f.call(a); // 輸出1 f.apply(b); // 輸出2
call和apply的第1個參數爲function執行時的this,這個this是肯定的,對未使用過bind的函數進行屢次apply/call,this指向都會改變。
var n = 1; var b = { n: 2, a: () => { console.log(this.n); } } b.a(); // 輸出1 b.a.call({n:3}); // 輸出1
b.a定義時的this和n
,b
所在的this一致
,默認狀況下爲全局做用域
箭頭函數的this指向定義時所在的this,這個是明確的,可是若是定義時所在的是1個function,那麼this指向同上面7點。
說下我以前學JS遇到過的問題: ES5下function纔會有做用域隔離, {}這種玩意不會隔離做用域。
構造函數方式調用指向對象實例
可是並不執行函數
this
指向爲定義
箭頭函數的this