當面試官問ES6你都瞭解哪些啊?你大概率會說,箭頭函數。那麼面試官接著問,箭頭函數跟function有什麼區別啊?然後你又自信滿滿的說this指向的不同。那麼面試官又問,那請說明一個this指向都有哪些不同?這時部分同學會對this的概念表達很模糊,下面就來詳細說下箭頭函數this指向的問題~

1.function和箭頭函數的this指向有哪些不同?

要了解箭頭函數中的this,先看function中的this優點以及不足,再來看箭頭函數的設計會看的比較清晰。

function中的this指向的是使用時所在的對象,而箭頭函數中的this指向定義時所在的對象,這點非常重要,舉個例子~

function fn(){
console.log(this);
};
fn();

請問列印出來的this在瀏覽器中顯示什麼?

:應該顯示的是fn外層的對象,因為使用者為fn外層的對象,如果fn處在最頂層,那麼在瀏覽器中應該顯示window對象,在node.js中應該顯示global對象,也就是現在他們的頂級作用域對象。如果在fn的外層是一個function對象,那麼就指向function的this。聽起來可能有點暈,不過沒關係,多舉例子就明白了~

再舉個例子

birth = 1994;
var obj = {
birth: 2000,
getAge: function () {
console.log(this.birth);
}
};
obj.getAge();
var a = obj.getAge;//2000
a();//1994

那麼這兩個調用為什麼會一個輸出2000一個輸出1994呢?還是之前的答案,因為function中的this指向的是使用者的對象,第一個使用者為obj對象,所以this當然指向obj。第二個呢,上方birth沒有寫var 自動賦值給了頂級作用域(當然不推薦大家這樣賦值哈,就是做個例子^_^)然後當調用a()的時候其實是頂級對象在調用它,當然this指的是頂級作用域。

2.function這種動態的this指向哪些弊端能用箭頭函數解決?

所以在某些場景下會使用function就找不到需要的this,比如

class Test {
constructor() {
this.birth = 10;
}
submit(){
$.ajax({
type: "POST",//方法類型
dataType: "json",//預期伺服器返回的數據類型
url: "xxxxx" ,//url
data: "xxxxx",
success: function (result) {
console.log(this.birth);//undefined
},
error : function() {}
});
}
}
let test = new Test();
test.submit();//undefined

這時傳統的解決方案時這樣:把this賦值給一個變數。

class Test {
constructor() {
this.birth = 10;
}
submit(){
let self = this;
$.ajax({
type: "POST",
dataType: "json",
url: "xxxxx" ,//url
data: "xxxxx",
success: function (result) {
console.log(self.birth);//10
},
error : function() {}
});
}
}
let test = new Test();
test.submit();//undefined

通過this賦值給一個變數,然後再通過變數訪問。

這時箭頭函數的第一個優勢就出來了,因為箭頭函數中的this指向的是定義時的對象這時箭頭函數定義時的對象為 Test類的實例對象,也就是test對象,所以this能找到birth屬性。

...
success: (result)=> {
console.log(this.birth);//10
},
...

3.普通函數動態this指向優勢有哪些?

當然function函數這種動態的this指向有他的好處,就是可以自由的指向對象,比如js中call或者apply方法可以動態指定function中的this對象,如果沒有call或者apply方法,則是指向調用者的對象。

birth = 1994;
var obj = {
birth: 2000,
getAge: function () {
console.log(this.birth);
}
};
var obj2 = {
birth: 2018;
};
var a = obj.getAge;
a();//1994
a.call(obj);//2000
a.call(obj2);//2018

如果使用箭頭函數會這樣,看下面例子

birth = 1994;
var obj = {
birth: 2000,
getAge: ()=> {
console.log(this.birth);
}
};
var obj2 = {
birth: 2018;
};
var a = obj.getAge;
a();//1994
a.call(obj);//1994
a.call(obj2);//1994

在瀏覽器中定義箭頭函數的的調用對象為window,所以不用使用什麼方法,它的this對象指向的都是window對象,所以call或者apply對箭頭函數不起作用,因為箭頭函數一旦被調用者定義就不會改變。

3.箭頭函數中的this的使用場景

下面的例子時function和箭頭函數混合使用,先用function函數在確定this指向,然後查看下面箭頭函數this指向。

function foo() {
setTimeout(() => {
console.log(this.id);
}, 100);
}

id = 21;
foo.call({ id: 12 });//12

外層foo函數確認指向的是一個{ id:12}對象,然後它下面的箭頭函數作為setTimeout的回調函數,其定義時上下文的this對象,也就是foo函數的this,所以foo函數的this指向哪裡就是哪裡,如果把箭頭函數變為普通函數,那麼雖然定義時調用者為foo,但是實際100ms後真正使用這個函數的時window對象。如下~

function foo() {
setTimeout(function (){
console.log(this.id);
}, 100);
}
id = 21;
foo.call({ id: 12 });//21

所以在箭頭函數中this指向的這個特性非常在回調函數中的應用。

4.擴展-箭頭函數跟普通函數的區別都有哪些?

  1. 函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。當對箭頭函數使用call()和apply()方法時對函數內的this沒有影響。
  2. 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。
  3. 不可以使用arguments對象,該對象在函數體內不存在。如果要用,可以用 rest 參數代替。
  4. 不可以使用yield命令,因此箭頭函數不能用作 Generator 函數。
  5. 箭頭函數沒有原型屬性。

參考文檔:

ECMAScript 6入門


推薦閱讀:
查看原文 >>
相關文章