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