ES6 允許使用“箭頭”(=>)定義函數。
一.箭頭函數
1.基本定義
-
① 簡單寫法,一個變量一個表達式返回單值
var f = v => v; // 等同於 var f = function (v) { return v; };
-
② 空參數
var f = () => 5; // 等同於 var f = function () { return 5 };
-
③ 多參數
var sum = (num1, num2) => num1 + num2; // 等同於 var sum = function(num1, num2) { return num1 + num2; };
-
④ 函數體中多條語句,使用
return
語句返回並使用大括號將函數體包圍起來。var sum = (num1, num2) => { num1+=1; return num1 + num2; }
-
⑤ 函數結構體也可以不返回
//一 單行不返回使用void關鍵字標識 let fn = () => void doesNotReturn(); // 二 多行語句不使用return var sum = (num1, num2) => { num1+=1; num1 + num2; }
-
⑥ 如果箭頭函數直接返回一個對象,由於大括號被解釋爲代碼塊,必須在對象外面加上括號,否則會報錯。
// 報錯 let getTempItem = id => { id: id, name: "Temp" }; // 不報錯 let getTempItem = id => ({ id: id, name: "Temp" });
2. 箭頭函數與變量解構結合使用
-
箭頭函數可以與變量解構結合使用。
const full = ({ first, last }) => first + ' ' + last; // 等同於 function full(person) { return person.first + ' ' + person.last; }
3. 箭頭函數的使用範例
-
箭頭函數使得表達更加簡潔。
const isEven = n => n % 2 === 0; const square = n => n * n;
-
簡化回調函數
// 正常函數寫法 [1,2,3].map(function (x) { return x * x; }); // 箭頭函數寫法 [1,2,3].map(x => x * x);
// 正常函數寫法 var result = values.sort(function (a, b) { return a - b; }); // 箭頭函數寫法 var result = values.sort((a, b) => a - b);
-
rest 參數與箭頭函數結合的例子
const numbers = (...nums) => nums; numbers(1, 2, 3, 4, 5) // [1,2,3,4,5] const headAndTail = (head, ...tail) => [head, tail]; headAndTail(1, 2, 3, 4, 5) // [1,[2,3,4,5]]
二.箭頭函數使用注意點
1.對this指向的影響
-
this對象表示當前對象運行的上下文環境,普通函數有自己的作用域,其定義的this指向就是本身的運行環境,若在對象中運行此函數就是指向此對象,若在全局中運行就指向全局對象;而對象不形成作用域,若對象屬性直接定義this或者使用箭頭函數定義(非普通函數),則指向上級運行環境(大多是頂級對象window)
const cat = { thisDefine: this,//object window lives: 9, jumps: () => { this.lives--; alert(this.lives)//NaN }, jumps2: function () { this.lives--; alert(this.lives)//8 } } alert(cat.thisDefine)//object window cat.jumps() alert(cat.lives)//9 cat.jumps2() alert(cat.lives)//8
-
箭頭函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象。
//一 call方法使對象具有調用foo和foo2函數的能力 //二 setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到 100 毫秒後。如果是普通函數,執行時this應該指向全局對象window,這時應該輸出21。但是,箭頭函數導致this總是指向函數定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。 //三 箭頭函數可以讓setTimeout裏面的this,綁定定義時所在的作用域,而不是指向運行時所在的作用域。 var id = 21; function foo() { setTimeout(() => { console.log('id:', this.id); }, 100); } function foo2() { setTimeout(function(){ console.log('id:', this.id); }, 100); } foo2.call({ id: 49 }); //id: 21 foo.call({ id: 42 }); // id: 42
2.箭頭函數不可以當作構造函數
- 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤。
3.箭頭函數沒有自己的this,指向外層對象的this
-
箭頭函數中
this
實質:this
指向的固定化,並不是因爲箭頭函數內部有綁定this的機制,實際原因是箭頭函數根本沒有自己的this,導致內部的this就是外層代碼塊的this。正是因爲它沒有this,所以也就不能用作構造函數。var handler = { id: '123456', init: function() { document.addEventListener('click', event => this.doSomething(event.type), false); }, doSomething: function(type) { console.log('Handling ' + type + ' for ' + this.id); } };
-
範例
//下面代碼之中,只有一個this,就是函數foo的this,所以t1、t2、t3都輸出同樣的結果。因爲所有的內層函數都是箭頭函數,都沒有自己的this,它們的this其實都是最外層foo函數的this。 function foo() { return () => { return () => { return () => { console.log('id:', this.id); }; }; }; } var f = foo.call({id: 1}); var t1 = f.call({id: 2})()(); // id: 1 var t2 = f().call({id: 3})(); // id: 1 var t3 = f()().call({id: 4}); // id: 1
4.箭頭函數除了this
,以下三個變量在箭頭函數之中也是不存在的,指向外層函數的對應變量:arguments
、super
、new.target
。
-
箭頭函數內部的變量arguments,其實是函數foo的arguments變量。
function foo() { setTimeout(() => { console.log('args:', arguments); }, 100); } foo(2, 4, 6, 8) // args: [2, 4, 6, 8]
-
由於箭頭函數沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。
//箭頭函數沒有自己的this,所以bind方法無效,內部的this指向外部的this。 (function() { return [ (() => this.x).bind({ x: 'inner' })() ]; }).call({ x: 'outer' }); // ['outer']
三.不適宜使用箭頭函數場景
1.由於箭頭函數使得this從“動態”變成“靜態”,實質是內部沒有this指向,拷貝上級對象this指向
-
第一個場合是定義對象的方法,且該方法內部包括
this
。//因爲對象不構成單獨的作用域,導致jumps箭頭函數定義時的作用域就是全局作用域this指向全局對象。jumps2定義在普通函數中,形成了作用域,this指向cat對象 const cat = { lives: 9, jumps: () => { this.lives--; alert(this.lives)//NaN }, jumps2: function () { this.lives--; alert(this.lives)//8 } } cat.jumps() alert(cat.lives)//9 cat.jumps2() alert(cat.lives)//8
-
第二個場合是需要動態this的時候,也不應使用箭頭函數。
//下面代碼運行時,點擊按鈕會報錯,因爲button的監聽函數是一個箭頭函數,導致裏面的this就是全局對象。如果改成普通函數,this就會動態指向被點擊的按鈕對象。 var button = document.getElementById('press'); button.addEventListener('click', () => { this.classList.toggle('on'); });