改变闭包中函数的行为

我必须升级 1500 LoC JS 模块中的一个小型私有函数,该函数是用The Revealing Module Pattern编写的。

简化的任务如下所示:

var module = module || function (){


    function init(){

        // you can upgrade init anyhow to be able to replace private_method later in runtime


        (function not_important(){console.log("I do some other stuff")})()

    }


    function public_method(){

        private_method()

    }


    function private_method(){

        console.log("original private method")

    }


    // you can add any methods here, too


    return {

        init: init,

        public_method: public_method,

        // and expose them

    }

}()



/** this is another script on the page, that uses original module */

module.public_method()  // returns "original private method"


/** the third script, that runs only in some environment, and requires the module to work a bit different */

// do any JS magic here to replace private_method

// or call module.init(method_to_replace_private_method)

module.public_method()  // I want to see some other behavior from private_method here

我研究过
访问闭包捕获的变量
如何从闭包中获取对象?
已经有问题了,这看起来与我的任务最相关,但没有成功。

我发现的唯一可行的解决方案是重写 function private_method(){}this.private_method = function(){...}其绑定到窗口的内容,这样我就可以在运行时更改它。

但我不会走这条路,因为该方法不再是私有的,而且我无法预测用旧式 ECMAScript 5 编写的旧 100000 LoC 意大利面怪物应用程序中可能会出现什么问题。



拉莫斯之舞
浏览 95回答 2
2回答

HUH函数

这通常称为猴子补丁神圣真实。你不能,除非你以某种方式暴露这个隐私。正确的。我只是不想将该方法直接公开到window作用域中。我认为解决方案可以更优雅。看来你可以更改源代码好吧,正如您已经注意到的那样,这很令人困惑,但重点是,我自己也不确定。我必须保留默认的模块行为,但我希望我可以稍微扩展该模块,以帮助应用这个猴子补丁。如果您想了解完整的故事,有一个运行 WebApp 的 IoT 设备,它使用我们讨论的模块将微控制器寄存器状态转换为 DOM 视图。我们称之为registers_into_view模块。通过复制相同的模块,创建了一个 Web 服务器,其中:接收微控制器内存转储模块的副本registers_into_view创建一个模型视图(通常应该发生在前端)向视图发送 JSON 形式通过扩展相同的 Web 应用程序,创建了“云”Web 应用程序,其中:否定现有registers_into_view模块,而是接收后端传来的View数据直接将其应用到 DOM 中通常情况下,我会重构整个架构:registers_into_view删除后端模块的副本registers_into_view在前端重用现有模块但使用该堆栈的 A 公司拒绝这样做。现有架构对他们来说可以使用 6 年。事实上,他们的经理避免进行重大变革,因为创建该软件的 B 公司收取了大量费用。所以他们没有重构的动力。嗯,我有。我在 C 公司工作,我们将销售相同的物联网设备。我们还想使用现有的前端 Web 应用程序。为了满足这一需求,我扩展了现有的物联网服务器以支持这些设备及其前端。所以我必须使用另一种语言和框架复制另一个服务器 API 合约。现在,我不想重新创建registers_into_view在后端服务器上运行的 1500 LoC 模块,而是将现有registers_into_view模块导入“云”Web 应用程序并对其进行猴子修补以从 JSON 而不是内存转储(我们的private_method)中检索寄存器数据。入侵应该尽可能少,以提高我的补丁被合并的机会。现在我希望我的动机足够明确。我发现它对每个人来说并不是那么有趣,因此尝试从上下文中清除编程任务。让我们来看看解决方案。你的解决方案2我无法更改public_method模块的 ,因为实际上它运行大约 50 个其他私有方法,这些方法都retreive_data_method仅使用不同的请求进行调用,并将结果放在 DOM 中的不同位置。解决方案1奇迹般有效。如此简单和优雅。我将根据我的需要稍微简化一下:var module = module || function (){    function public_method(){private_method()}    function private_method(){console.log("original private method")}    return {        public_method: public_method,                // this function allows to update the private_method in runtime        replace_private_method: function(fn) {                // Function declarations effectively create variables;                // you can simply write to them:                private_method = fn;        }    }}()// original behaviormodule.public_method()// patchingmodule.replace_private_method(function() {console.log("I've been monkey patched")});// new behaviormodule.public_method()我没有像您的解决方案中那样直接替换,而是尝试将模块上下文保存在某个公开的变量中,并通过它找到私有方法。这没有用。谢谢。

守着一只汪

我必须升级 1500 LoC JS 模块中的一个小型私有函数,该函数是用“揭示模块模式”编写的。我认为你的意思是你必须在运行时从“模块”函数之外执行此操作。这通常称为“猴子修补”。你不能,除非你以某种方式暴露这个隐私。我发现的唯一可行的解决方案是重写function private_method(){}将this.private_method = function(){...}其绑定到窗口的内容,这样我就可以在运行时更改它。如果你能做到这一点,那么看来你可以更改源代码。但如果你可以改变源代码,那么你可以这样做(见***评论):var module = module || function (){    function init(){        (function not_important(){console.log("I do some other stuff")})()    }    function public_method(){        private_method()    }    function private_method(){        console.log("original private method")    }    return {        init: init,        public_method: public_method,        // *** Provide yourself functions to get `private_method` (and any        // others you may want) and update it        __privates__: {            private_method: {                get: function() {                    return private_method;                },                set: function(fn) {                    // *** Function declarations effectively create variables;                    // you can write to them:                    private_method = fn;                }            }        }    }}()// *** Where you want to make your changemodule.__privates__.private_method.set(function() { /* ... */ });您可以通过将所有私有方法放在您通过其调用的对象上来概括(并且可以说是简化),但这意味着要么使用与预期不同的方式调用它们,要么使这些调用更加尴尬this:var module = module || function (){    /*** An object with the private functions you need to do this for    var privates = {};    function init(){        (function not_important(){console.log("I do some other stuff")})()    }    function public_method(){        // *** Calling it via that object, which has an effect on `this`        privates.private_method()        // *** If you want `this` to be the same as it would have been        // with the raw call above (the global object or `undefined` if        // you're in strict mode), you can use the comma trick:        // (0,privates.private_method)()    }    privates.private_method = function private_method(){        console.log("original private method")    };    return {        init: init,        public_method: public_method,        // *** Expose that object with the private functions        __privates__: privates    }}()// *** Where you want to make your changemodule.__privates__.private_method = function() { /* ... */ };
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript