這篇文章咱們來了解一下ES6中的箭頭函數。首先會介紹一下箭頭函數的基本語法,由於基本語法比較好理解,咱們用示例作簡單介紹便可。以後,咱們重點來討論一下箭頭函數與普通函數之間的區別。也歡迎你們隨時指正、探討!謝謝!git
本文已同步至個人我的主頁。歡迎訪問查看更多內容!謝謝你們的關注和支持!github
ES6中容許使用箭頭=>
來定義箭頭函數,具體語法,咱們來看一個簡單的例子:web
// 箭頭函數
let fun = (name) => {
// 函數體
return `Hello ${name} !`;
};
// 等同於
let fun = function (name) {
// 函數體
return `Hello ${name} !`;
};
複製代碼
能夠看出,定義箭頭函在數語法上要比普通函數簡潔得多。箭頭函數省去了function
關鍵字,採用箭頭=>
來定義函數。函數的參數放在=>
前面的括號中,函數體跟在=>
後的花括號中。bash
關於箭頭函數的參數:app
① 若是箭頭函數沒有參數,直接寫一個空括號便可。函數
② 若是箭頭函數的參數只有一個,也能夠省去包裹參數的括號。ui
③ 若是箭頭函數有多個參數,將參數依次用逗號(,)分隔,包裹在括號中便可。this
// 沒有參數
let fun1 = () => {
console.log(111);
};
// 只有一個參數,能夠省去參數括號
let fun2 = name => {
console.log(`Hello ${name} !`)
};
// 有多個參數
let fun3 = (val1, val2, val3) => {
return [val1, val2, val3];
};
複製代碼
關於箭頭函數的函數體:spa
① 若是箭頭函數的函數體只有一句代碼,就是簡單返回某個變量或者返回一個簡單的JS表達式,能夠省去函數體的大括號{ }。prototype
let f = val => val;
// 等同於
let f = function (val) { return val };
let sum = (num1, num2) => num1 + num2;
// 等同於
let sum = function(num1, num2) {
return num1 + num2;
};
複製代碼
② 若是箭頭函數的函數體只有一句代碼,就是返回一個對象,能夠像下面這樣寫:
// 用小括號包裹要返回的對象,不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
// 但毫不能這樣寫,會報錯。
// 由於對象的大括號會被解釋爲函數體的大括號
let getTempItem = id => { id: id, name: "Temp" };
複製代碼
③ 若是箭頭函數的函數體只有一條語句而且不須要返回值(最多見是調用一個函數),能夠給這條語句前面加一個void
關鍵字
let fn = () => void doesNotReturn();
複製代碼
箭頭函數最多見的用處就是簡化回調函數。
// 例子一
// 正常函數寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函數寫法
[1,2,3].map(x => x * x);
// 例子二
// 正常函數寫法
var result = [2, 5, 1, 4, 3].sort(function (a, b) {
return a - b;
});
// 箭頭函數寫法
var result = [2, 5, 1, 4, 3].sort((a, b) => a - b);
複製代碼
從上面的基本語法示例中能夠看出,箭頭函數的定義要比普通函數定義簡潔、清晰得多,很快捷。
咱們先來看看MDN上對箭頭函數this
的解釋。
箭頭函數不會建立本身的
this
,因此它沒有本身的this
,它只會從本身的做用域鏈的上一層繼承this
。
箭頭函數沒有本身的this
,它會捕獲本身在定義時(注意,是定義時,不是調用時)所處的外層執行環境的this
,並繼承這個this
值。因此,箭頭函數中this
的指向在它被定義的時候就已經肯定了,以後永遠不會改變。
來看個例子:
var id = 'Global';
function fun1() {
// setTimeout中使用普通函數
setTimeout(function(){
console.log(this.id);
}, 2000);
}
function fun2() {
// setTimeout中使用箭頭函數
setTimeout(() => {
console.log(this.id);
}, 2000)
}
fun1.call({id: 'Obj'}); // 'Global'
fun2.call({id: 'Obj'}); // 'Obj'
複製代碼
上面這個例子,函數fun1
中的setTimeout
中使用普通函數,2秒後函數執行時,這時函數實際上是在全局做用域執行的,因此this
指向Window
對象,this.id
就指向全局變量id
,因此輸出'Global'
。 可是函數fun2
中的setTimeout
中使用的是箭頭函數,這個箭頭函數的this
在定義時就肯定了,它繼承了它外層fun2
的執行環境中的this
,而fun2
調用時this
被call
方法改變到了對象{id: 'Obj'}
中,因此輸出'Obj'
。
再來看另外一個例子:
var id = 'GLOBAL';
var obj = {
id: 'OBJ',
a: function(){
console.log(this.id);
},
b: () => {
console.log(this.id);
}
};
obj.a(); // 'OBJ'
obj.b(); // 'GLOBAL'
複製代碼
上面這個例子,對象obj
的方法a
使用普通函數定義的,普通函數做爲對象的方法調用時,this
指向它所屬的對象。因此,this.id
就是obj.id
,因此輸出'OBJ'
。 可是方法b
是使用箭頭函數定義的,箭頭函數中的this
實際是繼承的它定義時所處的全局執行環境中的this
,因此指向Window
對象,因此輸出'GLOBAL'
。(這裏要注意,定義對象的大括號{}
是沒法造成一個單獨的執行環境的,它依舊是處於全局執行環境中!!)
上面的例子,就徹底能夠說明箭頭函數繼承而來的this
指向永遠不變。對象obj
的方法b
是使用箭頭函數定義的,這個函數中的this
就永遠指向它定義時所處的全局執行環境中的this
,即使這個函數是做爲對象obj
的方法調用,this
依舊指向Window
對象。
.call()
/.apply()
/.bind()
方法能夠用來動態修改函數執行時this
的指向,但因爲箭頭函數的this
定義時就已經肯定且永遠不會改變。因此使用這些方法永遠也改變不了箭頭函數this
的指向,雖然這麼作代碼不會報錯。
var id = 'Global';
// 箭頭函數定義在全局做用域
let fun1 = () => {
console.log(this.id)
};
fun1(); // 'Global'
// this的指向不會改變,永遠指向Window對象
fun1.call({id: 'Obj'}); // 'Global'
fun1.apply({id: 'Obj'}); // 'Global'
fun1.bind({id: 'Obj'})(); // 'Global'
複製代碼
咱們先了解一下構造函數的new都作了些什麼?簡單來講,分爲四步: ① JS內部首先會先生成一個對象; ② 再把函數中的this指向該對象; ③ 而後執行構造函數中的語句; ④ 最終返回該對象實例。
可是!!由於箭頭函數沒有本身的this
,它的this
實際上是繼承了外層執行環境中的this
,且this
指向永遠不會隨在哪裏調用、被誰調用而改變,因此箭頭函數不能做爲構造函數使用,或者說構造函數不能定義成箭頭函數,不然用new
調用時會報錯!
let Fun = (name, age) => {
this.name = name;
this.age = age;
};
// 報錯
let p = new Fun('cao', 24);
複製代碼
箭頭函數沒有本身的arguments
對象。在箭頭函數中訪問arguments
實際上得到的是外層局部(函數)執行環境中的值。
// 例子一
let fun = (val) => {
console.log(val); // 111
// 下面一行會報錯
// Uncaught ReferenceError: arguments is not defined
// 由於外層全局環境沒有arguments對象
console.log(arguments);
};
fun(111);
// 例子二
function outer(val1, val2) {
let argOut = arguments;
console.log(argOut); // ①
let fun = () => {
let argIn = arguments;
console.log(argIn); // ②
console.log(argOut === argIn); // ③
};
fun();
}
outer(111, 222);
複製代碼
上面例子二,①②③處的輸出結果以下:
outer
內部的箭頭函數
fun
中的
arguments
對象,實際上是沿做用域鏈向上訪問的外層
outer
函數的
arguments
對象。
能夠在箭頭函數中使用rest參數代替arguments對象,來訪問箭頭函數的參數列表!!
let sayHi = () => {
console.log('Hello World !')
};
console.log(sayHi.prototype); // undefined
複製代碼
這一點等到講解Generator函數的時候咱們再深刻討論。