前幾天發佈的Javavscript基礎——原型和原型鏈 收藏轉化率還挺高,看來你們對於JS基礎知識仍是很看重的,因爲JS語言設計的關係,不少語言特性不是那麼清晰。好比經典的this在哪的問題。 本文研究一下Javascript的this指向。javascript
Javascript的this指向問題,有些人可能以爲很簡單,有些人卻以爲撲朔迷離,看完本文以後相應會對this的掌握有一個直觀的判斷,而不是"開局全靠猜"。html
function函數this指向由調用方式
肯定,跟定義環境無關。java
箭頭函數this指向由定義環境
決定,與調用方式無關
,也不能夠bind(this)
。面試
非嚴格模式下,全局做用域下的this指向window
app
嚴格模式下,全局做用域下的this指向undefined
函數
如下討論均爲非嚴格模式
,這個不影響今天的討論。ui
說結論每每是讓人難以理解的,下面經過不一樣的調用場景對this作一個說明。this
function test() {
console.log(this);
}
test(); // 輸出undefined
複製代碼
直接調用是最簡單的, 大部分人在這裏都能回答的很好。spa
直接調用時this指向
全局做用域
。prototype
- 非嚴格模式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屬性確定報錯)
那若是你這麼回答,滿分
!
知其然還要知其因此然,咱們分析一下:
爲何輸出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