为了账号安全,请及时绑定邮箱和手机立即绑定

改变闭包中函数的行为

改变闭包中函数的行为

拉莫斯之舞 2023-10-14 18:28:48
我必须升级 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 意大利面怪物应用程序中可能会出现什么问题。
查看完整描述

2 回答

?
HUH函数

TA贡献1836条经验 获得超4个赞

这通常称为猴子补丁

神圣真实。

你不能,除非你以某种方式暴露这个隐私。

正确的。我只是不想将该方法直接公开到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 behavior

module.public_method()


// patching

module.replace_private_method(function() {console.log("I've been monkey patched")});

// new behavior

module.public_method()

我没有像您的解决方案中那样直接替换,而是尝试将模块上下文保存在某个公开的变量中,并通过它找到私有方法。这没有用。


谢谢。


查看完整回答
反对 回复 2023-10-14
?
守着一只汪

TA贡献1872条经验 获得超3个赞

我必须升级 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 change

module.__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 change

module.__privates__.private_method = function() { /* ... */ };


查看完整回答
反对 回复 2023-10-14
  • 2 回答
  • 0 关注
  • 94 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信