參考: http://es6.ruanyifeng.com/?search=%E7%AE%AD%E5%A4%B4&x=0&y=0#docs/function
箭頭函數的基本用法
let f1 = v => v;
let f2 = () => 5;
let f3 = (num1, num2) => num1 + num2;
let f4 = (num) => {
let n = 2;
return num + n;
}
使用示例
[1,2,3].map(x => x * x);
var result = values.sort((a, b) => a - b);
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]]
注意事項
- 函數體內的
this
對象,就是定義時所在的對象(函數體內沒有自己的this
對象,利用作用域查找到最近的this
對象),而不是使用時所在的對象。 - 不可以當作構造函數,也就是說,不可以使用new命令,否則會拋出一個錯誤(沒有
this
對象)。 - 不可以使用
arguments
對象,該對象在函數體內不存在。如果要用,可以用rest
參數代替。 - 不可以使用
yield
命令,因此箭頭函數不能用作Generator
函數。
關于第一點的一個說明
箭頭函數內并沒有自己的
this
對象,在箭頭函數內使用的this
對象時,會通過作用域鏈查找到最近的this
對象
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
上面代碼中,setTimeout
的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo
函數生成時,而它的真正執行要等到100
毫秒后。如果是普通函數,執行時this
應該指向全局對象window
,這時應該輸出21
。但是,箭頭函數導致this
總是指向函數定義生效時所在的對象(本例是{id: 42}
),所以輸出的是42
。
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭頭函數
setInterval(() => this.s1++, 1000);
// 普通函數
setInterval(function () {
this.s2++;
}, 1000);
}
var timer = new Timer();
setTimeout(() => console.log('s1: ', timer.s1), 3100);
setTimeout(() => console.log('s2: ', timer.s2), 3100);
// s1: 3
// s2: 0
上面代碼中,Timer
函數內部設置了兩個定時器,分別使用了箭頭函數和普通函數。前者的this
綁定定義時所在的作用域(即Timer
函數),后者的this
指向運行時所在的作用域(即全局對象)。所以,3100
毫秒之后,timer.s1
被更新了3次,而timer.s2
一次都沒更新。
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);
}
};
上面代碼的init
方法中,使用了箭頭函數,這導致這個箭頭函數里面的this
,總是指向handler
對象。否則,回調函數運行時,this.doSomething
這一行會報錯,因為此時this
指向document
對象。
this
指向的固定化,并不是因為箭頭函數內部有綁定this
的機制,實際原因是箭頭函數根本沒有自己的this
,導致在定義箭頭函數時內部的this
就是外層代碼塊的this
。正是因為它沒有this
,所以也就不能用作構造函數。也就不能用call()、apply()、bind()這些方法去改變this的指向。
所以,在ES6箭頭函數轉成ES5如下所示:
// ES6
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
// ES5
function foo() {
var _this = this;
setTimeout(function () {
console.log('id:', _this.id);
}, 100);
}
長期以來,JavaScript 語言的this對象一直是一個令人頭痛的問題,在對象方法中使用this,必須非常小心。箭頭函數”綁定”this,很大程度上解決了這個困擾。
嵌套箭頭函數
let insert = (value) => ({into: (array) => ({after: (afterValue) => {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}})});
insert(2).into([1, 3]).after(1); //[1, 2, 3]
// λ演算的寫法
fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v)))
// ES6的寫法
var fix = f => (x => f(v => x(x)(v)))
(x => f(v => x(x)(v)));
ES7綁定this
箭頭函數可以綁定this對象,大大減少了顯式綁定this對象的寫法(call、apply、bind)。但是,箭頭函數并不適用于所有場合,所以ES7提出了“函數綁定”(function bind)運算符,用來取代call、apply、bind調用。雖然該語法還是ES7的一個提案,但是Babel轉碼器已經支持。函數綁定運算符是并排的兩個冒號(::),雙冒號左邊是一個對象,右邊是一個函數。該運算符會自動將左邊的對象,作為上下文環境(即this對象),綁定到右邊的函數上面。
foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}
如果雙冒號左邊為空,右邊是一個對象的方法,則等于將該方法綁定在該對象上面。
var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);
由于雙冒號運算符返回的還是原對象,因此可以采用鏈式寫法。
// 例一
import { map, takeWhile, forEach } from "iterlib";
getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));
// 例二
let { find, html } = jake;
document.querySelectorAll("div.myClass")
::find("p")
::html("hahaha");