mostkia
V2EX  ›  JavaScript

js 代码,函数中如何进行追加写入新代码

By mostkia at 2020 年 8 月 6 日 · 5577 次点击

这里有一个很基本的函数:

function box(val){
	console.log('假设这是函数原有代码');
	val();  //这是回调函数
}

现在控制台运行它的结果是这样的:

box(function(){console.log('回调函数 01')});  //返回 [假设这是函数原有代码] + [回调函数 01 ] 
box(function(){console.log('回调函数 02')});  //返回 [假设这是函数原有代码] + [回调函数 02 ] 

很正常,上一次的结果被这次的参数替代掉了

现在要达到的需求:

box(function(){console.log('回调函数 01')});  //返回 [假设这是函数原有代码] + [回调函数 01 ] 
box(function(){console.log('回调函数 02')});  //返回 [假设这是函数原有代码] + [回调函数 01 ] + [回调函数 02 ] 

说简单点就是:如何在一个函数内追加新的代码片段,更新这个 box 函数,还不能执行它(它也是回调函数,有条件才能激活的) 请问该如何做到,如果有懂得 v 友,还希望不要吝啬赐教。

30 条回复  •  2020-08-07 16:51:22 +08:00
krixaar
   1
krixaar  
   2020 年 8 月 6 日   ❤️ 1
没看明白,你为啥不能传个存了一堆不同函数的数组进去一个个跑?
mostkia
   2
mostkia  
OP
   2020 年 8 月 6 日
@krixaar 因为传入函数是随机的,不是预置在内部的,否则就好处理了😂而且可能随时增加新的函数进去,怎么说呢,有点类似于这个吧 window.addEventListener,写入了一个回调,再次写入一个新的,不会覆盖上次的代码。
kop1989
   3
kop1989  
   2020 年 8 月 6 日   ❤️ 2
首先,这个需求不符合代码设计逻辑,典型的腐败代码。
理论上讲,一个 function,输入相同的参数,输出也应该是固定逻辑的。值不一定相同,但是逻辑处理要稳定。

其次,如果这个需求描述正确的话:
1 、box 是否能改?如果能改则可以传递 function 的数组解决。
2 、如果 box 不能改,则从回调函数入手,你可以把逻辑累加这个过程放在 box 外面维护。
yaphets666
   4
yaphets666  
   2020 年 8 月 6 日   ❤️ 2
function(...arg) {
arg.forEach(item=>{
item()
})
}
en20
   5
en20  
   2020 年 8 月 6 日 via iPhone   ❤️ 1
加个参数 eval 一下?
hoythan
   6
hoythan  
   2020 年 8 月 6 日   ❤️ 1
他的意思明显就是想要实现 php 的 filter 和 action 功能。
journey0ad
   7
journey0ad  
   2020 年 8 月 6 日 via iPhone   ❤️ 1
闭包+数组
ccraohng
   8
ccraohng  
   2020 年 8 月 6 日 via Android   ❤️ 1
看起来是要把以前传入的也保留?
顶层用一个数组,每次传入保存进去,返回所有数组的执行结果
unsized
   9
unsized  
   2020 年 8 月 6 日   ❤️ 1
闭包可解

const box = (() => {
const callbacks = [];
return (callback)=> {
if (callback instanceof Function) {
callbacks.push(callback);
return;
}
callbacks.forEach(callback => {
callback();
});
}
})();
ianva
   10
ianva  
   2020 年 8 月 6 日   ❤️ 3
``` javascript
const box = (() => {
const callbackList = [];
return callback => {
callbackList.push(callback);
callbackList.forEach(item => item());
};
})();
```
mostkia
   11
mostkia  
OP
   2020 年 8 月 6 日
@kop1989 是的,我也感觉不太符合正常的代码逻辑,不过具体项目只有我最清楚,关于这方面我会仔细考虑的,感谢你的意见。
@en20 @journey0ad @ccraohng @tyx1703 看来意见是出奇的一致,我朝这个方向研究一下,我一开始也感觉闭包可以做到,但以往都是传递保留数据,没试过函数,一下子不知道该从何入手。

感谢诸位出谋划策,不一一 @了,已发感谢~
cin
   12
cin  
   2020 年 8 月 6 日   ❤️ 1
var box = val => {
const fn0 = () => {
console.log('假设这是函数原有代码')
}
const fns = [fn0]
box = fn => {
fns.push(fn)
fns.forEach(fn => fn())
}
box(val)
}
azcvcza
   13
azcvcza  
   2020 年 8 月 6 日   ❤️ 1
要存东西,里面的状态要独立,且可更新,在 js 里就只能用闭包了
ianva
   14
ianva  
   2020 年 8 月 6 日
JavaScript 这个语言就闭包这一个特性能拿的出手吧,其他都是点缀,还是要多了解
mostkia
   15
mostkia  
OP
   2020 年 8 月 6 日
@ianva 好的,的确得多了解
@cin 感谢,已经找到思路了
@azcvcza 好的,目前已经解决这个问题。
cyrbuzz
   16
cyrbuzz  
PRO
   2020 年 8 月 6 日
在更进一步,数组外层再用一个对象(可以用 weakMap 不会造成垃圾回收的负担额)包裹。
35aZ4P8mT576683q
   17
35aZ4P8mT576683q  
   2020 年 8 月 6 日   ❤️ 1
这个就是简单的 memorization, 写过 react 或者尝试过函数式编程应该都知道
都说在 JS 里函数是第一公民,其实意思是函数就和普通变量一样,但实际函数可以做的更多,实际上应该是特等公民
wellsc
   18
wellsc  
   2020 年 8 月 6 日
@ianva 还有原型链
stillsilly
   19
stillsilly  
   2020 年 8 月 6 日   ❤️ 1
用装饰者模式

运行结果截图:
https://i.loli.net/2020/08/06/UlrdQPqua1sCiDK.png

代码:
function box(){
console.log('假设这是函数原有代码');
}
function 装饰 box(){
var _box = box
function 新加的函数(){
console.log('这是新加的函数')
}
box = function (){
_box()
新加的函数()
}

}
装饰 box()
box()
libook
   20
libook  
   2020 年 8 月 6 日   ❤️ 1
基本思路就是在这个函数能被调用的整个生命周期里,开辟一个存储空间来存每次传入的 callback,然后每次调用这个函数都从这个存储空间里读出之前所有传入过的 callback 一起调用。

上面楼层都给出了实际的代码。

有一个问题需要注意,就是你开辟的存储空间是有限的,在长时间高频调用的情况下,理论上有可能达到上限而抛出错误,而且因为每次执行的函数数量递增,执行需要的计算量和时长也会逐渐增加。

从代码可读性上,可以调整代码结构或增加注释来让看代码的人很快意识到这个函数的特殊性。
比如可以创建一个 Class,实例化的对象的状态保存在对象内部,通过调用对象的方法来实现你的功能。
ianva
   21
ianva  
   2020 年 8 月 6 日
也可以做一个 curry factory,对这一类问题做处理
const curryFactory = fn => {
__const list = [];
__return (...args) => {
____if (args.length) {
______console.log(args);
______list.push(...args);
____} else {
______return fn(...list);
____}
__};
};

全部源码,https://codesandbox.io/s/elated-cartwright-szp2w
ianva
   22
ianva  
   2020 年 8 月 6 日   ❤️ 1
const box1 = curryFactory((...args) => {
__Array.from(args).forEach(item => item());
})
box1(() => {
__console.log("1");
});
box1(() => {
__console.log("2");
});
box1();
ianva
   23
ianva  
   2020 年 8 月 6 日
@wellsc 并不是一个好的特性,特别是当年 for in 的时候还会把原型方法遍历出来,必须 hasOwnProperty,所以大部分的库都不会选择轻易的通过原型方法做入侵
wellsc
   24
wellsc  
   2020 年 8 月 6 日   ❤️ 1
@ianva 但就是因为有了原型链,才可以让 js 有这么多范式的方言啊
ianva
   25
ianva  
   2020 年 8 月 6 日
@wellsc 有很多种方式可以实现 oo 特性,这并不是最好的选择
mostkia
   26
mostkia  
OP
   2020 年 8 月 6 日
好的,感谢诸位,不一一 @了浪费大家时间了,有意义的回复均已感谢~
catch
   27
catch  
   2020 年 8 月 6 日 via iPhone
我看了你最近问的两个问题,标题都写的很奇怪,实则就都很基础的 js
mostkia
   28
mostkia  
OP
   2020 年 8 月 6 日
@catch emm 。。怎么说呢,你说的的确有道理,比如像我之前的一个问题。其实吧,我提问的主要动机,主要是不知道一些偏门的需求自己这样闭门造车实现出来,代码是不是走偏了,发出来看看大家是怎么解决的,笨办法都是有的,但有最优解我肯定是想要了解一下的,看到大家都是这样做的,一般也就没差了。
Exceptionluo
   29
Exceptionluo  
   2020 年 8 月 7 日   ❤️ 1
function box(val) {
console.log('假设这是函数原有代码');
box.ar = box.ar || []
box.ar.push(val)
box.ar.forEach(element => {
element()
});
}
mostkia
   30
mostkia  
OP
   2020 年 8 月 7 日
@Exceptionluo 好的,有效,感谢
推荐关注
› Meteor
› JSLint - a JavaScript code quality tool
› jsFiddle
› D3.js
› WebStorm
推荐书目
› JavaScript 权威指南第 5 版
› Closure: The Definitive Guide
© 2026 V2EX · 72ms · 3.9.8.5