代码之家  ›  专栏  ›  技术社区  ›  Jack Guo

javascript:module调用文件中需要该模块的函数

  •  0
  • Jack Guo  · 技术社区  · 6 年前

    我第一次写js库。库旨在在特定时间执行文件中需要库的函数。类似角度执行用户实现的钩子,如 $onInit ,但在我的例子中,用户可以定义任意数量的函数以供我的库调用。我该如何实现?

    我想到的一个方法是定义 registerFunction(name, function) 方法,它将函数名映射到实现。但是用户可以给我一个名称数组,然后我自动为它们注册相应的函数吗?

    1 回复  |  直到 6 年前
        1
  •  1
  •   Tom    6 年前

    除非您有特定的要求,否则模块不需要知道它所提供的函数的名称。当您的模块调用这些函数时,它将通过对它们的直接引用而不是使用它们的名称来执行这些操作。

    例如:

    // my-module.js
    module.exports = function callMyFunctions( functionList ) {
        functionList.forEach( fn => fn() )
    }
    
    // main application
    
    const myFunc1 = () => console.log('Function 1 executing')
    const myFunc2 = () => console.log('Function 2 executing')
    
    const moduleThatInvokesMyFunctions = require('./my-module.js')
    
    // instruct the module to invoke my 2 cool functions
    moduleThatInvokesMyFunctions([ myFunc1, myFunc2 ])
    //> Function 1 executing
    //> Function 2 executing
    

    注意,调用者直接提供对模块的函数引用,然后模块使用这些引用——而不关心甚至不关心 知道 这些函数的名称。(是的,您可以通过检查函数引用来获取它们的名称,但是为什么要麻烦呢?)

    如果你想要一个更深入的答案或解释,它将有助于了解你的情况。您的库的目标环境是什么:浏览器?点头?电子?反应自然?


    库的目的是在特定时间执行文件中需要库的函数

    “在特定时间”向我暗示了一些松散的基于事件的东西。所以,根据你的目标平台,你可以使用真正的EventEmitter。在这种情况下,您应该为每个应该调用函数的时间创建唯一的名称,然后您的模块将导出一个单例发射器。然后,调用者将为他们关心的每个事件分配事件处理程序。对于呼叫者来说,可能是这样的:

    const lifecycleManager = require('./your-module.js')
    
    lifecycleManager.on( 'boot', myBootHandler )
    lifecycleManager.on( 'config-available', myConfigHandler )
    // etc.
    

    处理此问题的一种更为简单的方法是调用方提供函数字典:

    const orchestrateJobs = require('./your-module.js')
    
    orchestrateJobs({
        'boot': myBootHandler,
        'config-available': myConfigHandler
    })
    

    如果你不喜欢和EventEmitter一起工作,这可能很吸引人。但是走这条路需要考虑如何支持其他场景,比如调用方希望删除函数和延迟注册。


    演示如何使用的快速草图 apply 每个功能:

    // my-module.js
    module.exports = function callMyFunctions( functionList ) {
        functionList.forEach( fn => fn.apply( thisValue, arrayOfArguments ) )
    }
    

    注意,这个模块仍然不知道调用者为这些函数分配了什么名称。在这个范围内,每个例程都有一个绰号“fn”

    我感觉到你对执行的工作方式有一些误解,这让你相信程序的各个部分需要知道程序其他部分的名称。但这不是延续传递风格的工作方式。


    由于您是基于特定时间触发调用方函数,因此事件模型可能非常适合。下面是一个大概的情况:

    // caller
    
    const AlarmClock = require('./your-module.js')
    
    function doRoosterCall( exactTime ) {
        console.log('I am a rooster! Cock-a-doodle-doo!')
    }
    
    function soundCarHorn( exactTime ) {
        console.log('Honk! Honk!')
    }
    
    AlarmClock.on('sunrise', doRoosterCall)
    AlarmClock.on('leave-for-work', soundCarHorn)
    // etc
    

    要做到这一点,你可以做一些像。。。

    // your-module.js
    
    const EventEmitter = require('events')
    
    const singletonClock = new EventEmitter()
    
    function checkForEvents() {
        const currentTime = new Date()
    
        // check for sunrise, which we'll define as 6:00am +/- 10 seconds
        if(nowIs('6:00am', 10 * 1000)) {
            singletonClock.emit('sunrise', currentTime)
        }
    
        // check for "leave-for-work": 8:30am +/- 1 minute
        if(nowIs('8:30am', 60 * 1000)) {
            singletonClock.emit('leave-for-work', currentTime)
        }
    }
    
    setInterval( checkForEvents, 1000 )
    
    module.exports = singletonClock
    

    ( nowIs 是用来比较时间的手写体。在进行类似cron的工作时,您应该假设当时间值完全匹配时,您的heartbeat函数几乎永远不会被触发,因此您需要一些东西来提供“足够接近”的比较。我没有提供impl是因为(1)这里似乎是一个外围问题,(2)我确信Momentjs、date fns或其他一些包提供了一些很棒的东西,所以您不需要自己实现它。