如何在JavaScript中模拟let表达式?
How to simulate let expressions in JavaScript?
考虑以下 take
的实现:
const take = (n, [x, ...xs]) =>
n === 0 || x === undefined ?
[] : [x, ...take(n - 1, xs)];
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // []
如您所见,它不适用于带有 undefined
的数组,因为 x === undefined
不是测试数组是否为空的最佳方法。以下代码解决了这个问题:
const take = (n, xs) =>
n === 0 || xs.length === 0 ?
[] : [xs[0], ...take(n - 1, xs.slice(1))];
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // [undefined]
然而,写 xs[0]
和 xs.slice(1)
并不优雅。此外,如果您需要多次使用它们,这是有问题的。要么你必须复制代码并做不必要的额外工作,要么你必须创建一个块作用域,定义常量并使用 return 关键字。
最好的解决方案是使用 let expression。不幸的是,JavaScript 没有它们。那么,如何在JavaScript?
中模拟let表达式
在 Lisp 中,一个 let expression is just syntactic sugar for a left-left lambda (i.e. an immediately-invoked function expression)。例如,考虑:
(let ([x 1]
[y 2])
(+ x y))
; This is syntactic sugar for:
((lambda (x y)
(+ x y))
1 2)
在ES6中,我们可以使用arrow functions and default parameters创建一个类似let表达式的IIFE,如下所示:
const z = ((x = 1, y = 2) => x + y)();
console.log(z);
使用这个 hack,我们可以定义 take
如下:
const take = (n, xxs) =>
n === 0 || xxs.length === 0 ?
[] : (([x, ...xs] = xxs) => [x, ...take(n - 1, xs)])();
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // [undefined]
希望对您有所帮助。
不使用 IIFE,而是使用具有适当名称的普通函数来使事情更明确:
const _let = f =>
f();
const collateBy = f => xs =>
xs.reduce((m, x) =>
_let((r = f(x), ys = m.get(r) || []) =>
m.set(r, (ys.push(x), ys))), new Map());
const includes = t => s =>
s.includes(t);
xs = ["Dev", "Jeff", "Kalib", "Amy", "Gemma"];
const collation = collateBy(includes("e")) (xs);
console.log(collation.get(true));
console.log(collation.get(false));
考虑以下 take
的实现:
const take = (n, [x, ...xs]) =>
n === 0 || x === undefined ?
[] : [x, ...take(n - 1, xs)];
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // []
如您所见,它不适用于带有 undefined
的数组,因为 x === undefined
不是测试数组是否为空的最佳方法。以下代码解决了这个问题:
const take = (n, xs) =>
n === 0 || xs.length === 0 ?
[] : [xs[0], ...take(n - 1, xs.slice(1))];
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // [undefined]
然而,写 xs[0]
和 xs.slice(1)
并不优雅。此外,如果您需要多次使用它们,这是有问题的。要么你必须复制代码并做不必要的额外工作,要么你必须创建一个块作用域,定义常量并使用 return 关键字。
最好的解决方案是使用 let expression。不幸的是,JavaScript 没有它们。那么,如何在JavaScript?
中模拟let表达式在 Lisp 中,一个 let expression is just syntactic sugar for a left-left lambda (i.e. an immediately-invoked function expression)。例如,考虑:
(let ([x 1]
[y 2])
(+ x y))
; This is syntactic sugar for:
((lambda (x y)
(+ x y))
1 2)
在ES6中,我们可以使用arrow functions and default parameters创建一个类似let表达式的IIFE,如下所示:
const z = ((x = 1, y = 2) => x + y)();
console.log(z);
使用这个 hack,我们可以定义 take
如下:
const take = (n, xxs) =>
n === 0 || xxs.length === 0 ?
[] : (([x, ...xs] = xxs) => [x, ...take(n - 1, xs)])();
console.log(take(7, [1, 2, 3, 4, 5])); // [1, 2, 3, 4, 5]
console.log(take(3, [1, 2, 3, 4, 5])); // [1, 2, 3]
console.log(take(1, [undefined, 1])); // [undefined]
希望对您有所帮助。
不使用 IIFE,而是使用具有适当名称的普通函数来使事情更明确:
const _let = f =>
f();
const collateBy = f => xs =>
xs.reduce((m, x) =>
_let((r = f(x), ys = m.get(r) || []) =>
m.set(r, (ys.push(x), ys))), new Map());
const includes = t => s =>
s.includes(t);
xs = ["Dev", "Jeff", "Kalib", "Amy", "Gemma"];
const collation = collateBy(includes("e")) (xs);
console.log(collation.get(true));
console.log(collation.get(false));