箭頭函數是ES6標准中新增的一種函數,在詳細的討論箭頭函數之前,我們先來看看函數的四種定義方式
函數的四種定義方式
1、函數聲明的方式(常用)
function sum (num1,num2) { return num1 + num2; }
注意函數定義最后沒有加分號。必須有名字,會函數提升,在預解析階段就已經創建,聲明前后都可以調用。
2、函數表達式的方式
let sum = function(num1,num2) { return num1 + num2; };
一種變量賦值,函表達式可以沒有名字(匿名函數),沒有函數提升。函數表達式創建方式和函數聲明幾乎是等價的,這里代碼定義了一個變量sum並將其初始化為一個函數,這個函數可以通過sum來引用。注意這里函數末尾是有分號的,與任何變量初始化語句一樣。
3、箭頭函數的方式
//多個參數需要括號 let sum = (num1,num2) => { return num1 + num2; }; //以下兩種寫法都有效,只有一個參數可以不寫括號 let double = (x) => { return 2 * x }; let double = x => { return 2 * x }; //沒有參數需要括號 let getRandom = () => { return Math.random() };
注意:箭頭函數在參數和箭頭之間不能換行,但是,可以通過在 ‘=>’ 之后換行,或者用 ‘( )’、'{ }'來實現換行
任何可以使用函數表達式的地方,都可以使用箭頭函數。箭頭函數語法簡單,非常適合嵌入函數的場景。
let ints = [1,2,3]; console.log(ints.map(function (i){ return i + 1 }));//[2,3,4] console.log(ints.map(i => {return i + 1 }));//[2,3,4] //map()方法定義在JavaScript的Array中,它返回一個新的數組,數組中的元素為原始數組調用函數處理后的值。
4、構造函數的方式(不推薦)
let sum = new Function("num1","num2","return num1 + num2");
不推薦使用這種語法定義,因為這段代碼會被解釋兩次:第一次是把它作為常規ES代碼,第二次是解釋傳給構造函數的字符串。這顯然會影響性能。不過,把函數想象為對象,把函數名想象為指針這很重要。而上面的語法很好地詮釋了這個概念。
箭頭函數中的this
箭頭函數不綁定this, 它會捕獲其所在(即定義的位置)上下文的this值, 作為自己的this值,即箭頭函數內部的this
是詞法作用域,由上下文確定
在箭頭函數出現之前,每一個新函數根據它是被如何調用的來定義這個函數的this值:
- 如果該函數是一個構造函數,this指針指向一個新的對象
- 在嚴格模式下的函數調用下,this指向undefined
- 如果該函數是一個對象的方法,則它的this指針指向這個對象
- 等等
由於 箭頭函數沒有自己的this指針,通過 call()
或 apply()
方法調用一個函數時,只能傳遞參數
var adder = { base : 1, add : function(a) { var f = v => v + this.base; return f(a); }, addThruCall: function(a) { var f = v => v + this.base; var b = { base : 2 }; return f.call(b, a); } }; console.log(adder.add(1)); // 輸出 2 console.log(adder.addThruCall(1)); // 仍然輸出 2
接下來我們來看一個箭頭函數應用的例子:我們想每隔一秒輸出this.age的值加1
function Person() { // Person() 構造函數定義 `this`作為它自己的實例. this.age = 0; setInterval(function add() { // 在非嚴格模式, add()函數定義 `this`作為全局對象, // 與在 Person()構造函數中定義的 `this`並不相同. this.age++; console.log(this.age); }, 1000); } var p = new Person();
但是你會發現,每隔一秒都會有一個NaN
打印出來,而不是累加的數字。到底哪里錯了呢? 實際上setInterval
里面的this
綁定到全局對象window,而window.age未定義,那么怎么解決這一問題呢?
在ECMAScript 3/5中,通過將this
值分配給封閉的變量,可以解決this
問題。
function Person() { var that = this; that.age = 0; setInterval(function add() { that.age++;// 回調引用的是`that`變量, 其值是預期的對象. console.log(that.age); }, 1000); } var p = new Person();
使用箭頭函數解決,箭頭函數不會創建自己的this,它只會從自己的作用域鏈的上一層繼承this
。因此,在下面的代碼中,傳遞給setInterval
的函數內的this
與封閉函數中的this
值相同:
function Person() { this.age = 0; setInterval(() => { this.age++; console.log(this.age); }, 1000); } var p = new Person();
箭頭函數不綁定arguments
箭頭函數不綁定arguments對象。
function foo() { console.log(arguments[0]); } foo(5);//5 let bar = () => { console.log(arguments[0]); } bar(5)//arguments is not defined
雖然箭頭函數沒有arguments對象。但可以在包裝函數中把它提供給箭頭函數:
function foo() { let bar = () => { console.log(arguments[0]);//5 } bar() } foo(5);
箭頭函數不能用作構造函數
和new一起使用會拋出異常
var Foo = () => {}; var foo = new Foo(); // TypeError: Foo is not a constructor
箭頭函數沒有prototype
屬性
var Foo = () => {}; console.log(Foo.prototype); // undefined
返回對象字面量
記住用params => {object:literal}
這種簡單的語法返回對象字面量是行不通的。
var func = () => { foo: 1 }; // Calling func() returns undefined! var func = () => ({foo: 1}); //所以,記得用圓括號把對象字面量包起來: var func = () => { foo: function() {} }; // SyntaxError: function statement requires a name
箭頭函數也不能使用super和new.target