当场没写出来,面试官劝我别紧张,说这道题很基础,可是面试后自己在这也没写出来...
综合了一下大家的回答,感觉👇这个代码能不费力看懂~
1
Yumwey 2022-06-20 18:24:36 +08:00
看看 koa 的源码吧,这是很基础的 compose 了,实现方式很多,递归,迭代都可以
|
2
Biwood 2022-06-20 18:24:56 +08:00 via Android
感觉还好,思路:遍历,使用.bind()把后一个 fn 作为参数绑定到前一个 fn 上,即前一个 fn 参数中的 next ,遍历完后执行第一个 fn 即可。
猜测一下,expressjs 这类框架的中间件大概是类似原理吧。 |
3
Yumwey 2022-06-20 18:25:36 +08:00
|
4
Yumwey 2022-06-20 18:27:05 +08:00
也可以看看 redux 的实现,应该都挺常见的
|
5
Cbdy 2022-06-20 18:28:42 +08:00
一行代码的事情
export function compose(middlewares = (ctx, next) => (void 0)) { if (!middlewares || !middlewares.length) { return ctx => (void 0) } return ctx => middlewares[0](ctx, () => compose(middlewares.slice(1))(ctx)) } |
6
zhaomeicheng OP @Cbdy 是怎么做到一下子写出来的...看来这道题真的很基础,面试官没说错。
|
7
cyrbuzz 2022-06-20 18:42:19 +08:00
```
const compose = arr => { const run = lastArr => { if (lastArr.length === 0) { return; } const first = lastArr.shift(); first(() => run(arr)); }; return () => { run(arr); }; }; ``` 这样?运行结果是 ok 的,没做检查。 |
8
rabbbit 2022-06-20 18:56:16 +08:00
哎,想了十几分钟才有思路。现场肯定是写不出来了
function compose(arr) { const next = (fn = (() => {}), arr) => { fn(next.bind(this, arr[0], arr.slice(1))); }; return next.bind(this, arr[0], arr.slice(1)); } |
9
zhaomeicheng OP @cyrbuzz 对的,我刚刚自己又尝试了一下,写出来的与你思路一致... 看样子我是能做出来的...唉
|
10
cyrbuzz 2022-06-20 19:01:07 +08:00
|
11
TWorldIsNButThis 2022-06-20 19:06:45 +08:00 via iPhone
swr 的 middleware 就是这个结构
其他前端框架应该有不少类似的 |
12
des 2022-06-20 19:18:46 +08:00 via iPhone
忘了在哪里看到的了
arr.reverse().reduce((a, b) => () => b(a), Function.prototype)() |
13
rabbbit 2022-06-20 19:20:00 +08:00
哪里有类似 Leetcode 能刷这种题的题库吗?
|
14
Cbdy 2022-06-20 19:46:26 +08:00
如果觉得 JS 版本太难懂,可以看一下 Java 版本
https://github.com/cbdyzj/nanometer/blob/main/common/src/main/java/nano/support/Onion.java |
15
enchilada2020 2022-06-20 19:52:20 +08:00
```JS
const compose = ([f, ...arr]) => arr.length ? () => f(compose(arr)) : () => f?.(() => { }); ``` |
16
enchilada2020 2022-06-20 20:02:27 +08:00 1
没想到还有更简单的
```JS const compose = arr => arr.reduceRight((acc, cur) => () => cur(acc), () => { }); ``` |
17
tyx1703 2022-06-20 20:03:36 +08:00
想了十几分钟,代码倒是简单,思路很重要。估计我面试时也会紧张写不出来。
// 递归 function compose(middlewares) { if (middlewares.length === 0) { return () => {} } const middleware = middlewares.shift() return middleware.bind(null, compose(middlewares)) } // 迭代 function compose(middlewares) { let res; for (let i = middlewares.length - 1; i >= 0; i--) { if (!res) { res = middlewares[i].bind(null, () => {}) } else { res = middlewares[i].bind(null, res) } } return res; } |
18
dinjufen 2022-06-20 20:10:45 +08:00 via Android 3
|
19
sweetcola 2022-06-20 20:25:21 +08:00 4
综合上面回答的缩减版()
const compose = ([fn, ...fns]) => () => fn?.(compose(fns)) |
20
isbase 2022-06-20 21:21:58 +08:00
|
21
enchilada2020 2022-06-20 21:32:14 +08:00 via Android
@sweetcola 很极致 我喜欢
|
22
skies457 2022-06-20 21:52:37 +08:00
const compose = ((self, [fn, ...fns]) => () => fn?.(self(self, fns)))((self, [fn, ...fns]) => () => fn?.(self(self, fns)), arr)
还可以升级一下难度,只使用匿名函数(逃 |
23
skies457 2022-06-20 21:53:33 +08:00
const compose = arr => ((self, [fn, ...fns]) => () => fn?.(self(self, fns)))((self, [fn, ...fns]) => () => fn?.(self(self, fns)), arr)
少了 arr => ( |
24
dasbn 2022-06-20 22:46:07 +08:00
|
25
molvqingtai 2022-06-20 23:09:44 +08:00 via Android
洋葱圈模型还挺实用的,koa 就不用说了,比如前端使用的 redux 、vue-router 内部实现都是使用洋葱模型
我也这种模式封装了一个 http 客户端: https://github.com/molvqingtai/resreq |
26
chezs66 2022-06-20 23:13:47 +08:00
这就是栈呀。。。无非是自己写一个栈,或用 js 自带的栈
|
27
zyxyz123 2022-06-20 23:17:32 +08:00
const compose = (arr) => {
if (arr.length === 0) { return () => {} } return () => { arr[0](compose(arr.slice(1))) } } |
28
otakustay 2022-06-20 23:29:13 +08:00
const compose = handlers => handlers.reduceRight((out, v) => () => v(out), () => {});
手写倒有点难,电脑敲再简单调试一下下可以 |
29
Agassiz 2022-06-21 00:54:36 +08:00 via iPhone
洋葱模型
|
30
walpurgis 2022-06-21 01:40:31 +08:00
middlewares = [fn1,fn2,fn3]
compose 展开后是这样 fn = () => fn1(() => fn2(() => fn3(() => {}))); 暴力拼接法 function compose(arr) { const fn = arr.pop(); let composed = () => fn(() => {}); while (arr.length > 0) { const fn = arr.pop(); composed = ((next) => () => fn(next))(composed) }; return composed; } |
31
wanacry 2022-06-21 03:19:59 +08:00 via iPhone
这是啥玩意 我也会 js 但是为啥我都看不懂
|
33
banmuyutian 2022-06-21 09:47:03 +08:00
才疏学浅,请教下这个洋葱模型是不是类似于 Spring Boot 的堆栈?
|
34
danhua 2022-06-21 10:14:32 +08:00
为啥我第一眼感觉可以用异步来解决,不过异步比上面大佬们给出的方法要麻烦多了。
|
35
dtdths1 2022-06-21 10:56:54 +08:00
洋葱圈模型
|
36
ryougifujino 2022-06-21 12:43:51 +08:00
function compose(arr) {
let next = function () { } arr.reverse().forEach(fn => { const nextFn = next next = function () { fn(nextFn) } }) return next } |
37
ryougifujino 2022-06-21 12:48:27 +08:00
@ryougifujino #36 个人感觉我这个思路很简单,确实就是洋葱圈模型。next 顾名思义,就是下一次要执行的函数,所以核心思路就是把下一次的函数放到 next 中去就行了。
|
38
zhaomeicheng OP @ryougifujino 说真的,我看不懂😂
|
39
Lenic 2022-06-25 12:33:39 +08:00
建议看下 Array.prototype.reduce 方法
|
40
channg 2022-08-03 17:56:54 +08:00
|
41
houchangxiaowang 2023-04-22 11:52:47 +08:00
图片挂了,重新帖下?
|