1.函數(shù)參數(shù)的默認值
基本用法
ES6 之前,不能直接為函數(shù)的參數(shù)指定默認值,只能采用變通的方法。
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代碼檢查函數(shù)log的參數(shù)y有沒有賦值,如果沒有,則指定默認值為World。這種寫法的缺點在于,如果參數(shù)y賦值了,但是對應(yīng)的布爾值為false,則該賦值不起作用。就像上面代碼的最后一行,參數(shù)y等于空字符,結(jié)果被改為默認值。
為了避免這個問題,通常需要先判斷一下參數(shù)y是否被賦值,如果沒有,再等于默認值。
if (typeof y === 'undefined') {
y = 'World';
}
ES6 允許為函數(shù)的參數(shù)設(shè)置默認值,即直接寫在參數(shù)定義的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
可以看到,ES6 的寫法比 ES5 簡潔許多,而且非常自然。下面是另一個例子。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
除了簡潔,ES6 的寫法還有兩個好處:首先,閱讀代碼的人,可以立刻意識到哪些參數(shù)是可以省略的,不用查看函數(shù)體或文檔;其次,有利于將來的代碼優(yōu)化,即使未來的版本在對外接口中,徹底拿掉這個參數(shù),也不會導致以前的代碼無法運行。
參數(shù)變量是默認聲明的,所以不能用let或const再次聲明。
function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
上面代碼中,參數(shù)變量x是默認聲明的,在函數(shù)體中,不能用let或const再次聲明,否則會報錯。
使用參數(shù)默認值時,函數(shù)不能有同名參數(shù)。
// 不報錯
function foo(x, x, y) {
// ...
}
// 報錯
function foo(x, x, y = 1) {
// ...
}
// SyntaxError: Duplicate parameter name not allowed in this context
另外,一個容易忽略的地方是,參數(shù)默認值不是傳值的,而是每次都重新計算默認值表達式的值。也就是說,參數(shù)默認值是惰性求值的。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
上面代碼中,參數(shù)p的默認值是x + 1。這時,每次調(diào)用函數(shù)foo,都會重新計算x + 1,而不是默認p等于 100。
與解構(gòu)賦值默認值結(jié)合使用
參數(shù)默認值可以與解構(gòu)賦值的默認值,結(jié)合起來使用。
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
上面代碼只使用了對象的解構(gòu)賦值默認值,沒有使用函數(shù)參數(shù)的默認值。只有當函數(shù)foo的參數(shù)是一個對象時,變量x和y才會通過解構(gòu)賦值生成。如果函數(shù)foo調(diào)用時沒提供參數(shù),變量x和y就不會生成,從而報錯。通過提供函數(shù)參數(shù)的默認值,就可以避免這種情況。
function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
上面代碼指定,如果沒有提供參數(shù),函數(shù)foo的參數(shù)默認為一個空對象。
下面是另一個解構(gòu)賦值默認值的例子。
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 報錯
上面代碼中,如果函數(shù)fetch的第二個參數(shù)是一個對象,就可以為它的三個屬性設(shè)置默認值。這種寫法不能省略第二個參數(shù),如果結(jié)合函數(shù)參數(shù)的默認值,就可以省略第二個參數(shù)。這時,就出現(xiàn)了雙重默認值。
function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
上面代碼中,函數(shù)fetch沒有第二個參數(shù)時,函數(shù)參數(shù)的默認值就會生效,然后才是解構(gòu)賦值的默認值生效,變量method才會取到默認值GET。
作為練習,請問下面兩種寫法有什么差別?
// 寫法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 寫法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
上面兩種寫法都對函數(shù)的參數(shù)設(shè)定了默認值,區(qū)別是寫法一函數(shù)參數(shù)的默認值是空對象,但是設(shè)置了對象解構(gòu)賦值的默認值;寫法二函數(shù)參數(shù)的默認值是一個有具體屬性的對象,但是沒有設(shè)置對象解構(gòu)賦值的默認值。
// 函數(shù)沒有參數(shù)的情況
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情況
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 無值的情況
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都無值的情況
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
參數(shù)默認值的位置
通常情況下,定義了默認值的參數(shù),應(yīng)該是函數(shù)的尾參數(shù)。因為這樣比較容易看出來,到底省略了哪些參數(shù)。如果非尾部的參數(shù)設(shè)置默認值,實際上這個參數(shù)是沒法省略的。
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 報錯
f(undefined, 1) // [1, 1]
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 報錯
f(1, undefined, 2) // [1, 5, 2]
上面代碼中,有默認值的參數(shù)都不是尾參數(shù)。這時,無法只省略該參數(shù),而不省略它后面的參數(shù),除非顯式輸入undefined。
如果傳入undefined,將觸發(fā)該參數(shù)等于默認值,null則沒有這個效果。
function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null)
// 5 null
上面代碼中,x參數(shù)對應(yīng)undefined,結(jié)果觸發(fā)了默認值,y參數(shù)等于null,就沒有觸發(fā)默認值。
函數(shù)的 length 屬性
指定了默認值以后,函數(shù)的length屬性,將返回沒有指定默認值的參數(shù)個數(shù)。也就是說,指定了默認值后,length屬性將失真。
(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
上面代碼中,length屬性的返回值,等于函數(shù)的參數(shù)個數(shù)減去指定了默認值的參數(shù)個數(shù)。比如,上面最后一個函數(shù),定義了 3 個參數(shù),其中有一個參數(shù)c指定了默認值,因此length屬性等于3減去1,最后得到2。
這是因為length屬性的含義是,該函數(shù)預期傳入的參數(shù)個數(shù)。某個參數(shù)指定默認值以后,預期傳入的參數(shù)個數(shù)就不包括這個參數(shù)了。同理,后文的 rest 參數(shù)也不會計入length屬性。
(function(...args) {}).length // 0
如果設(shè)置了默認值的參數(shù)不是尾參數(shù),那么length屬性也不再計入后面的參數(shù)了。
(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
###作用域
一旦設(shè)置了參數(shù)的默認值,函數(shù)進行聲明初始化時,參數(shù)會形成一個單獨的作用域(context)。等到初始化結(jié)束,這個作用域就會消失。這種語法行為,在不設(shè)置參數(shù)默認值時,是不會出現(xiàn)的。
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
上面代碼中,參數(shù)y的默認值等于變量x。調(diào)用函數(shù)f時,參數(shù)形成一個單獨的作用域。在這個作用域里面,默認值變量x指向第一個參數(shù)x,而不是全局變量x,所以輸出是2。
再看下面的例子。
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
上面代碼中,函數(shù)f調(diào)用時,參數(shù)y = x形成一個單獨的作用域。這個作用域里面,變量x本身沒有定義,所以指向外層的全局變量x。函數(shù)調(diào)用時,函數(shù)體內(nèi)部的局部變量x影響不到默認值變量x。
如果此時,全局變量x不存在,就會報錯。
function f(y = x) {
let x = 2;
console.log(y);
}
f() // ReferenceError: x is not defined
下面這樣寫,也會報錯。
var x = 1;
function foo(x = x) {
// ...
}
foo() // ReferenceError: x is not defined
上面代碼中,參數(shù)x = x形成一個單獨作用域。實際執(zhí)行的是let x = x,由于暫時性死區(qū)的原因,這行代碼會報錯”x 未定義“。
如果參數(shù)的默認值是一個函數(shù),該函數(shù)的作用域也遵守這個規(guī)則。請看下面的例子。
let foo = 'outer';
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar(); // outer
上面代碼中,函數(shù)bar的參數(shù)func的默認值是一個匿名函數(shù),返回值為變量foo。函數(shù)參數(shù)形成的單獨作用域里面,并沒有定義變量foo,所以foo指向外層的全局變量foo,因此輸出outer。
如果寫成下面這樣,就會報錯。
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar() // ReferenceError: foo is not defined
上面代碼中,匿名函數(shù)里面的foo指向函數(shù)外層,但是函數(shù)外層并沒有聲明變量foo,所以就報錯了。
下面是一個更復雜的例子。
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x // 1
上面代碼中,函數(shù)foo的參數(shù)形成一個單獨作用域。這個作用域里面,首先聲明了變量x,然后聲明了變量y,y的默認值是一個匿名函數(shù)。這個匿名函數(shù)內(nèi)部的變量x,指向同一個作用域的第一個參數(shù)x。函數(shù)foo內(nèi)部又聲明了一個內(nèi)部變量x,該變量與第一個參數(shù)x由于不是同一個作用域,所以不是同一個變量,因此執(zhí)行y后,內(nèi)部變量x和外部全局變量x的值都沒變。
如果將var x = 3的var去除,函數(shù)foo的內(nèi)部變量x就指向第一個參數(shù)x,與匿名函數(shù)內(nèi)部的x是一致的,所以最后輸出的就是2,而外層的全局變量x依然不受影響。
var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
應(yīng)用
利用參數(shù)默認值,可以指定某一個參數(shù)不得省略,如果省略就拋出一個錯誤。
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
上面代碼的foo函數(shù),如果調(diào)用的時候沒有參數(shù),就會調(diào)用默認值throwIfMissing函數(shù),從而拋出一個錯誤。
從上面代碼還可以看到,參數(shù)mustBeProvided的默認值等于throwIfMissing函數(shù)的運行結(jié)果(注意函數(shù)名throwIfMissing之后有一對圓括號),這表明參數(shù)的默認值不是在定義時執(zhí)行,而是在運行時執(zhí)行。如果參數(shù)已經(jīng)賦值,默認值中的函數(shù)就不會運行。
另外,可以將參數(shù)默認值設(shè)為undefined,表明這個參數(shù)是可以省略的。
function foo(optional = undefined) { ··· }
rest 參數(shù)
ES6 引入 rest 參數(shù)(形式為...變量名),用于獲取函數(shù)的多余參數(shù),這樣就不需要使用arguments對象了。rest 參數(shù)搭配的變量是一個數(shù)組,該變量將多余的參數(shù)放入數(shù)組中。
function add(...values) {
let sum = 0;
for (var val of values) {
sum += val;
}
return sum;
}
add(2, 5, 3) // 10
上面代碼的add函數(shù)是一個求和函數(shù),利用 rest 參數(shù),可以向該函數(shù)傳入任意數(shù)目的參數(shù)。
下面是一個 rest 參數(shù)代替arguments變量的例子。
// arguments變量的寫法
function sortNumbers() {
return Array.prototype.slice.call(arguments).sort();
}
// rest參數(shù)的寫法
const sortNumbers = (...numbers) => numbers.sort();
上面代碼的兩種寫法,比較后可以發(fā)現(xiàn),rest 參數(shù)的寫法更自然也更簡潔。
arguments對象不是數(shù)組,而是一個類似數(shù)組的對象。所以為了使用數(shù)組的方法,必須使用Array.prototype.slice.call先將其轉(zhuǎn)為數(shù)組。rest 參數(shù)就不存在這個問題,它就是一個真正的數(shù)組,數(shù)組特有的方法都可以使用。下面是一個利用 rest 參數(shù)改寫數(shù)組push方法的例子。
function push(array, ...items) {
items.forEach(function(item) {
array.push(item);
console.log(item);
});
}
var a = [];
push(a, 1, 2, 3)
注意,rest 參數(shù)之后不能再有其他參數(shù)(即只能是最后一個參數(shù)),否則會報錯。
// 報錯
function f(a, ...b, c) {
// ...
}
函數(shù)的length屬性,不包括 rest 參數(shù)。
(function(a) {}).length // 1
(function(...a) {}).length // 0
(function(a, ...b) {}).length // 1
箭頭函數(shù)
基本用法
ES6 允許使用“箭頭”(=>)定義函數(shù)。
var f = v => v;
// 等同于
var f = function (v) {
return v;
};
如果箭頭函數(shù)不需要參數(shù)或需要多個參數(shù),就使用一個圓括號代表參數(shù)部分。
var f = () => 5;
// 等同于
var f = function () { return 5 };
var sum = (num1, num2) => num1 + num2;
// 等同于
var sum = function(num1, num2) {
return num1 + num2;
};
如果箭頭函數(shù)的代碼塊部分多于一條語句,就要使用大括號將它們括起來,并且使用return語句返回。
var sum = (num1, num2) => { return num1 + num2; }
由于大括號被解釋為代碼塊,所以如果箭頭函數(shù)直接返回一個對象,必須在對象外面加上括號,否則會報錯。
// 報錯
let getTempItem = id => { id: id, name: "Temp" };
// 不報錯
let getTempItem = id => ({ id: id, name: "Temp" });
下面是一種特殊情況,雖然可以運行,但會得到錯誤的結(jié)果。
let foo = () => { a: 1 };
foo() // undefined
上面代碼中,原始意圖是返回一個對象{ a: 1 },但是由于引擎認為大括號是代碼塊,所以執(zhí)行了一行語句a: 1。這時,a可以被解釋為語句的標簽,因此實際執(zhí)行的語句是1;,然后函數(shù)就結(jié)束了,沒有返回值。
如果箭頭函數(shù)只有一行語句,且不需要返回值,可以采用下面的寫法,就不用寫大括號了。
let fn = () => void doesNotReturn();
箭頭函數(shù)可以與變量解構(gòu)結(jié)合使用。
const full = ({ first, last }) => first + ' ' + last;
// 等同于
function full(person) {
return person.first + ' ' + person.last;
}
箭頭函數(shù)使得表達更加簡潔。
const isEven = n => n % 2 === 0;
const square = n => n * n;
上面代碼只用了兩行,就定義了兩個簡單的工具函數(shù)。如果不用箭頭函數(shù),可能就要占用多行,而且還不如現(xiàn)在這樣寫醒目。
箭頭函數(shù)的一個用處是簡化回調(diào)函數(shù)。
// 正常函數(shù)寫法
[1,2,3].map(function (x) {
return x * x;
});
// 箭頭函數(shù)寫法
[1,2,3].map(x => x * x);
另一個例子是
// 正常函數(shù)寫法
var result = values.sort(function (a, b) {
return a - b;
});
// 箭頭函數(shù)寫法
var result = values.sort((a, b) => a - b);
下面是 rest 參數(shù)與箭頭函數(shù)結(jié)合的例子。
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]]
使用注意點
箭頭函數(shù)有幾個使用注意點。
(1)函數(shù)體內(nèi)的
this對象,就是定義時所在的對象,而不是使用時所在的對象。
(2)不可以當作構(gòu)造函數(shù),也就是說,不可以使用
new命令,否則會拋出一個錯誤。
(3)不可以使用
arguments對象,該對象在函數(shù)體內(nèi)不存在。如果要用,可以用 rest 參數(shù)代替。
(4)不可以使用
yield命令,因此箭頭函數(shù)不能用作 Generator 函數(shù)。
上面四點中,第一點尤其值得注意。this對象的指向是可變的,但是在箭頭函數(shù)中,它是固定的。
function foo() {
setTimeout(() => {
console.log('id:', this.id);
}, 100);
}
var id = 21;
foo.call({ id: 42 });
// id: 42
上面代碼中,setTimeout的參數(shù)是一個箭頭函數(shù),這個箭頭函數(shù)的定義生效是在foo函數(shù)生成時,而它的真正執(zhí)行要等到 100 毫秒后。如果是普通函數(shù),執(zhí)行時this應(yīng)該指向全局對象window,這時應(yīng)該輸出21。但是,箭頭函數(shù)導致this總是指向函數(shù)定義生效時所在的對象(本例是{id: 42}),所以輸出的是42。
箭頭函數(shù)可以讓setTimeout里面的this,綁定定義時所在的作用域,而不是指向運行時所在的作用域。下面是另一個例子。
function Timer() {
this.s1 = 0;
this.s2 = 0;
// 箭頭函數(shù)
setInterval(() => this.s1++, 1000);
// 普通函數(shù)
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函數(shù)內(nèi)部設(shè)置了兩個定時器,分別使用了箭頭函數(shù)和普通函數(shù)。前者的this綁定定義時所在的作用域(即Timer函數(shù)),后者的this指向運行時所在的作用域(即全局對象)。所以,3100 毫秒之后,timer.s1被更新了 3 次,而timer.s2一次都沒更新。
箭頭函數(shù)可以讓this指向固定化,這種特性很有利于封裝回調(diào)函數(shù)。下面是一個例子,DOM 事件的回調(diào)函數(shù)封裝在一個對象里面。
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方法中,使用了箭頭函數(shù),這導致這個箭頭函數(shù)里面的this,總是指向handler對象。否則,回調(diào)函數(shù)運行時,this.doSomething這一行會報錯,因為此時this指向document對象。
this指向的固定化,并不是因為箭頭函數(shù)內(nèi)部有綁定this的機制,實際原因是箭頭函數(shù)根本沒有自己的this,導致內(nèi)部的this就是外層代碼塊的this。正是因為它沒有this,所以也就不能用作構(gòu)造函數(shù)。
所以,箭頭函數(shù)轉(zhuǎn)成 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);
}
上面代碼中,轉(zhuǎn)換后的 ES5 版本清楚地說明了,箭頭函數(shù)里面根本沒有自己的this,而是引用外層的this。
請問下面的代碼之中有幾個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
上面代碼之中,只有一個this,就是函數(shù)foo的this,所以t1、t2、t3都輸出同樣的結(jié)果。因為所有的內(nèi)層函數(shù)都是箭頭函數(shù),都沒有自己的this,它們的this其實都是最外層foo函數(shù)的this。
除了this,以下三個變量在箭頭函數(shù)之中也是不存在的,指向外層函數(shù)的對應(yīng)變量:
arguments、super、new.target。
function foo() {
setTimeout(() => {
console.log('args:', arguments);
}, 100);
}
foo(2, 4, 6, 8)
// args: [2, 4, 6, 8]
上面代碼中,箭頭函數(shù)內(nèi)部的變量arguments,其實是函數(shù)foo的arguments變量。
另外,由于箭頭函數(shù)沒有自己的this,所以當然也就不能用call()、apply()、bind()這些方法去改變this的指向。
(function() {
return [
(() => this.x).bind({ x: 'inner' })()
];
}).call({ x: 'outer' });
// ['outer']
上面代碼中,箭頭函數(shù)沒有自己的this,所以bind方法無效,內(nèi)部的this指向外部的this。
長期以來,JavaScript 語言的this對象一直是一個令人頭痛的問題,在對象方法中使用this,必須非常小心。箭頭函數(shù)”綁定”this,很大程度上解決了這個困擾。
不適用場合
由于箭頭函數(shù)使得this從“動態(tài)”變成“靜態(tài)”,下面兩個場合不應(yīng)該使用箭頭函數(shù)。
第一個場合是定義函數(shù)的方法,且該方法內(nèi)部包括this。
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面代碼中,cat.jumps()方法是一個箭頭函數(shù),這是錯誤的。調(diào)用cat.jumps()時,如果是普通函數(shù),該方法內(nèi)部的this指向cat;如果寫成上面那樣的箭頭函數(shù),使得this指向全局對象,因此不會得到預期結(jié)果。
第二個場合是需要動態(tài)this的時候,也不應(yīng)使用箭頭函數(shù)。
var button = document.getElementById('press');
button.addEventListener('click', () => {
this.classList.toggle('on');
});
上面代碼運行時,點擊按鈕會報錯,因為button的監(jiān)聽函數(shù)是一個箭頭函數(shù),導致里面的this就是全局對象。如果改成普通函數(shù),this就會動態(tài)指向被點擊的按鈕對象。
另外,如果函數(shù)體很復雜,有許多行,或者函數(shù)內(nèi)部有大量的讀寫操作,不單純是為了計算值,這時也不應(yīng)該使用箭頭函數(shù),而是要使用普通函數(shù),這樣可以提高代碼可讀性。
嵌套的箭頭函數(shù)
箭頭函數(shù)內(nèi)部,還可以再使用箭頭函數(shù)。下面是一個 ES5 語法的多重嵌套函數(shù)。
function insert(value) {
return {into: function (array) {
return {after: function (afterValue) {
array.splice(array.indexOf(afterValue) + 1, 0, value);
return array;
}};
}};
}
insert(2).into([1, 3]).after(1); //[1, 2, 3]
上面這個函數(shù),可以使用箭頭函數(shù)改寫。
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]
下面是一個部署管道機制(pipeline)的例子,即前一個函數(shù)的輸出是后一個函數(shù)的輸入。
const pipeline = (...funcs) =>
val => funcs.reduce((a, b) => b(a), val);
const plus1 = a => a + 1;
const mult2 = a => a * 2;
const addThenMult = pipeline(plus1, mult2);
addThenMult(5)
// 12
如果覺得上面的寫法可讀性比較差,也可以采用下面的寫法。
const plus1 = a => a + 1;
const mult2 = a => a * 2;
mult2(plus1(5))
// 12
箭頭函數(shù)還有一個功能,就是可以很方便地改寫 λ 演算。
// λ演算的寫法
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)));
上面兩種寫法,幾乎是一一對應(yīng)的。由于 λ 演算對于計算機科學非常重要,這使得我們可以用 ES6 作為替代工具,探索計算機科學。