V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
mostkia
V2EX  ›  JavaScript

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

  •  
  •   mostkia · 2020-08-06 08:52:43 +08:00 · 4053 次点击
    这是一个创建于 1331 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

    感谢诸位出谋划策,不一一 @了,已发感谢~
    cin
        12
    cin  
       2020-08-06 09:29:55 +08:00   ❤️ 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-08-06 09:30:19 +08:00   ❤️ 1
    要存东西,里面的状态要独立,且可更新,在 js 里就只能用闭包了
    ianva
        14
    ianva  
       2020-08-06 09:31:17 +08:00
    JavaScript 这个语言就闭包这一个特性能拿的出手吧,其他都是点缀,还是要多了解
    mostkia
        15
    mostkia  
    OP
       2020-08-06 09:39:48 +08:00
    @ianva 好的,的确得多了解
    @cin 感谢,已经找到思路了
    @azcvcza 好的,目前已经解决这个问题。
    cyrbuzz
        16
    cyrbuzz  
       2020-08-06 09:55:48 +08:00
    在更进一步,数组外层再用一个对象(可以用 weakMap 不会造成垃圾回收的负担额)包裹。
    liberty1900
        17
    liberty1900  
       2020-08-06 10:02:33 +08:00   ❤️ 1
    这个就是简单的 memorization, 写过 react 或者尝试过函数式编程应该都知道
    都说在 JS 里函数是第一公民,其实意思是函数就和普通变量一样,但实际函数可以做的更多,实际上应该是特等公民
    wellsc
        18
    wellsc  
       2020-08-06 10:09:08 +08:00
    @ianva 还有原型链
    stillsilly
        19
    stillsilly  
       2020-08-06 10:09:15 +08:00   ❤️ 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-08-06 10:29:29 +08:00   ❤️ 1
    基本思路就是在这个函数能被调用的整个生命周期里,开辟一个存储空间来存每次传入的 callback,然后每次调用这个函数都从这个存储空间里读出之前所有传入过的 callback 一起调用。

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

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

    从代码可读性上,可以调整代码结构或增加注释来让看代码的人很快意识到这个函数的特殊性。
    比如可以创建一个 Class,实例化的对象的状态保存在对象内部,通过调用对象的方法来实现你的功能。
    ianva
        21
    ianva  
       2020-08-06 11:00:42 +08:00
    也可以做一个 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-08-06 11:01:36 +08:00   ❤️ 1
    const box1 = curryFactory((...args) => {
    __Array.from(args).forEach(item => item());
    })
    box1(() => {
    __console.log("1");
    });
    box1(() => {
    __console.log("2");
    });
    box1();
    ianva
        23
    ianva  
       2020-08-06 11:10:59 +08:00
    @wellsc 并不是一个好的特性,特别是当年 for in 的时候还会把原型方法遍历出来,必须 hasOwnProperty,所以大部分的库都不会选择轻易的通过原型方法做入侵
    wellsc
        24
    wellsc  
       2020-08-06 11:17:55 +08:00   ❤️ 1
    @ianva 但就是因为有了原型链,才可以让 js 有这么多范式的方言啊
    ianva
        25
    ianva  
       2020-08-06 11:22:25 +08:00
    @wellsc 有很多种方式可以实现 oo 特性,这并不是最好的选择
    mostkia
        26
    mostkia  
    OP
       2020-08-06 12:28:18 +08:00
    好的,感谢诸位,不一一 @了浪费大家时间了,有意义的回复均已感谢~
    catch
        27
    catch  
       2020-08-06 13:13:08 +08:00 via iPhone
    我看了你最近问的两个问题,标题都写的很奇怪,实则就都很基础的 js
    mostkia
        28
    mostkia  
    OP
       2020-08-06 22:26:31 +08:00
    @catch emm 。。怎么说呢,你说的的确有道理,比如像我之前的一个问题。其实吧,我提问的主要动机,主要是不知道一些偏门的需求自己这样闭门造车实现出来,代码是不是走偏了,发出来看看大家是怎么解决的,笨办法都是有的,但有最优解我肯定是想要了解一下的,看到大家都是这样做的,一般也就没差了。
    Exceptionluo
        29
    Exceptionluo  
       2020-08-07 09:48:15 +08:00   ❤️ 1
    function box(val) {
    console.log('假设这是函数原有代码');
    box.ar = box.ar || []
    box.ar.push(val)
    box.ar.forEach(element => {
    element()
    });
    }
    mostkia
        30
    mostkia  
    OP
       2020-08-07 16:51:22 +08:00
    @Exceptionluo 好的,有效,感谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5433 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 42ms · UTC 08:44 · PVG 16:44 · LAX 01:44 · JFK 04:44
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.