先來談談ES5中的this
在ES5中,每個函數在被調用時都會自動取得this這個特殊的對象。因此,每個內部函數不能訪問到外部函數的this對象。(跟變量訪問一樣,如果局部環境存在某個變量,就不會去搜索全局環境的同名變量)。
** this對象是在運行時基于函數的執行環境決定的。**
執行環境:
a.全局環境
運行在全局上下文(在任何函數體外部),this指向全局對象,并且,無論是否是在嚴格模式下,this都指代全局對象。在瀏覽器中,this指向window對象。
console.log(this === window); // true;
b.函數環境
在函數內部,this的值取決于函數是如何調用的。
-
直接調用,非嚴格模式下,this默認指向全局對象。不管調用的函數是在全局函數還是局部環境。
function app() { console.log(this === window); // true } app(); // 全局調用 function foo() { function bar() { console.log(this === window); // true } bar(); // 局部調用 } foo();
-
直接調用,嚴格模式下,this默認指向undefined。不管調用的函數是在全局函數還是局部環境。
'use strict' function app() { console.log(this === undefined); // true } app(); function foo() { function bar() { console.log(this === undefined); // true } bar(); } foo();
-
作為對象調用
當函數以對象的方法被調用時,它的this是調用該函數的對象,并且是距離最近的對象。var o = { sayName: function() { console.log(this === o); // true } } o.sayName();
-
作為構造函數調用
當函數被當做構造函數調用時,this指向剛要被創建的新對象。
function Person() { this.name = 'noshower'; } var person = new Person(); console.log(person.name); //'noshower'
-
使用call和apply調用,改變函數內部this的綁定值。
function foo() { console.log(this.name); //'noshower' } var obj = { name: 'noshower' } foo.call(obj);
// 函數foo內部的this對象被綁定到了obj對象上。
-
使用bind方法,會永久把this與一個對象綁定。無論這個函數如何被調用,都改變不了this。
function foo() { console.log(this.name); // 'noshower' } var obj1 = { name: 'noshower' } var obj2 = { name: 'DaLin' } var a = foo.bind(obj1); //用bind方法,將this進行永久綁定。 a.call(obj2); // 此時使用call方法將改變不了this的指向。
下面我們開始講ES6箭頭函數中的this對象
箭頭函數被調用的時候,不會自動綁定一個this對象。換句話說,箭頭函數根本就沒有自己的this。它的this都是捕獲自其所在上下文的this值。
-
作為匿名函數被調用。
function foo() { console.log(this); //{name:'noshower'} setTimeout(() => { console.log('name:', this.name); //noshower }, 100); } var name = 'DaLin'; foo.call({ name: 'noshower' });
上面代碼中,我們用call()方法,將函數foo的this對象綁定到了對象{name:'noshower'}上。由于,箭頭函數沒有this變量,所以,箭頭函數能夠訪問到外部環境的this變量。此時,箭頭函數內部,訪問到的是foo函數的this對象。因此,this.name的值是'noshower'。
-
作為方法被調用
var obj = { name: 'noshower', sayName: () => this.name } var name = 'bar'; console.log(obj.sayName());
上面代碼中,箭頭函數是作為對象的方法。因為,箭頭函數本身是不綁定this對象的。因此,它只能從外部搬運this對象。上面代碼中,只有全局環境存在this對象,因此返回的是window.name,即'bar'。
-
在方法內部被調用
var obj = { name: 'noshower', sayName: function() { var a = () => this.name; console.log(a()); // noshower } } var name = 'bar'; obj.sayName();
上面代碼中,箭頭函數是在sayName方法中運行的。此時,箭頭函數的this對象,搬運的是sayName方法的this對象。當sayName函數作為方法調用時,它的this對象指向obj對象。因此,箭頭函數的this.name就是obj.name,即'noshower';
-
使用new 操作符調用,會拋出錯誤
var Obj = (name) => { this.name = name; } new Obj('noshower');
上面的代碼,箭頭函數作為構造函數被調用,會報錯。原因是,箭頭函數沒有自己的this對象,就沒法給this添加屬性。
-
使用call和apply,bind()調用
var o = (val) => { console.log(this.name, val); // bar,bar }; var obj = { name: 'foo' } var name = 'bar'; o.call(obj, name);
上面代碼中,箭頭函數使用了call方法來調用。原本想將函數的this綁定在obj對象上面。結果顯示,this被綁定在了全局對象上了。這是因為,箭頭函數沒有自己的this對象,此時使用call方法僅僅起到了傳遞參數的作用,沒有改變它的this對象。它的this對象依舊來自外部執行環境。
總結一下
箭頭函數沒有自己的this對象,它總是搬運外部環境的this對象。因此,只要離它最近的外部環境中的this改變,箭頭函數中的this就改變。如果離它最近的環境中的this,沒有改變。那么箭頭函數中的this就不會改變。