ES5函數調用三種形式:
func(p1, p2) obj.child.method(p1, p2) func.call(context, p1, p2) // 先不講 apply
前兩種都是語法糖,可以等價地變為 call 形式:轉換代碼
func(p1, p2) 等價於
func.call(undefined, p1, p2)
obj.child.method(p1, p2) 等價於
obj.child.method.call(obj.child, p1, p2)
func.call(context, p1, p2)
this,就是上面代碼中的 context。就這么簡單。
this 是你 call 一個函數時傳的 context,由於你從來不用 call 形式的函數調用,所以你一直不知道。
瀏覽器里有一條規則:
如果你傳的 context 就 null 或者 undefined,那么 window 對象就是默認的 context(嚴格模式下默認 context 是 undefined)
因此上面的打印結果是 window。
[ ] 語法
function fn (){ console.log(this) }
var arr = [fn, fn2]
arr[0]() // 這里面的 this 又是什么呢?
我們可以把 arr[0]( ) 想象為arr.0( ),雖然后者的語法錯了,但是形式與轉換代碼里的 obj.child.method(p1, p2) 對應上了,於是就可以愉快的轉換了:
arr[0]()
假想為 arr.0()
然后轉換為 arr.0.call(arr)
那么里面的 this 就是 arr 了 :)
總結
-
this 就是你 call 一個函數時,傳入的 context。
-
如果你的函數調用形式不是 call 形式,請按照「轉換代碼」將其轉換為 call 形式。
-
如果一個函數中有this,但是它沒有被上一級的對象所調用,那么this指向的就是window,這里需要說明的是在js的嚴格版中this指向的不是window,非嚴格模式為undefined。
-
如果一個函數中有this,這個函數有被上一級的對象所調用,那么this指向的就是上一級的對象。
-
如果一個函數中有this,這個函數中包含多個對象,盡管這個函數是被最外層的對象所調用,this指向的也只是它上一級的對象。
例:
var o = { a:10, b:{ // a:12, fn:function(){ console.log(this.a); //undefined } } } o.b.fn();
盡管對象b中沒有屬性a,這個this指向的也是對象b,因為this只會指向它的上一級對象,不管這個對象中有沒有this要的東西。
4.還有一種比較特殊的情況:這里this指向的是window,this永遠指向的是最后調用它的對象
var o = { a:10, b:{ a:12, fn:function(){ console.log(this.a); //undefined console.log(this); //window } } } var j = o.b.fn; j(); //this指向window
5. 構造函數版this:
function Fn(){ this.user = "Caraxiong"; } var a = new Fn(); console.log(a.user); //Caraxiong
這里之所以對象a可以點出函數Fn里面的user是因為new關鍵字可以改變this的指向,將這個this指向對象a.調用這個函數Fn的是對象a,那么this指向的自然是對象a,那么為什么對象Fn中會有user,因為你已經復制了一份Fn函數到對象a中,用了new關鍵字就等同於復制了一份。
6.當this碰到return時
function fn() { this.user = 'Caraxiong'; return {}; //返回一個對象 } var a = new fn; console.log(a.user); //undefined
function fn() { this.user = 'Caraxiong'; return function(){}; //返回一個對象
} var a = new fn; console.log(a.user); //undefined
function fn() { this.user = 'Caraxiong'; return 1; //非對象 } var a = new fn; console.log(a.user); //Caraxiong function fn() { this.user = 'Caraxiong'; return undefined; //非對象
}
var a = new fn;
console.log(a.user); //Caraxiong
如果返回值是一個對象,那么this指向的就是那個返回的對象,
如果返回值不是一個對象那么this還是指向函數的實例。
注:還有一點就是雖然null也是對象,但是在這里this還是指向那個函數的實例,因為null比較特殊。
function fn() { this.user = 'Caraxiong'; return null; } var a = new fn; console.log(a.user); //Caraxiong
7. ES6中的this指向
- 函數體內的
this對象,就是定義時所在的對象,而不是使用時所在的對象。 - 不可以當作構造函數,也就是說,不可以使用
new命令,否則會拋出一個錯誤。 - 不可以使用
arguments對象,該對象在函數體內不存在。如果要用,可以用Rest參數代替。 - 不可以使用
yield命令,因此箭頭函數不能用作Generator函數。 -
function Timer() { this.a1 = 0; this.a2 = 0; // 箭頭函數 setInterval(() => this.a1++, 1000); // 普通函數 setInterval(function () { this.a2++; }, 1000); } var timer = new Timer(); setTimeout(() => console.log('a1: ', timer.a1), 3100); setTimeout(() => console.log('a2: ', timer.a2), 3100); // a1: 3 // a2: 0上面代碼中,
Timer函數內部設置了兩個定時器,分別使用了箭頭函數和普通函數。前者的this綁定定義時所在的作用域(即Timer函數),后者的this指向運行時所在的作用域(即全局對象)。 this指向的固定化,並不是因為箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構造函數。- 另外,由於箭頭函數沒有自己的
this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向
注:
- new操作符會改變函數this的指向問題,為什么this會指向a?
- 首先new關鍵字會創建一個空的對象,然后會自動調用一個函數apply方法,將this指向這個空對象,這樣的話函數內部的this就會被這個空的對象替代。
