我必须升级一个用The Revealing Module Pattern编写的 1500 LoC JS 模块中的一个小型私有函数。
简化的任务如下所示:
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 意大利面怪物应用程序中可能会损坏什么。
更新
这是https://stackoverflow.com/a/65156282/7709003 上的答案。
首先,感谢TJ Crowder 的详细回答。
这通常称为monkeypatching
神圣真实。
你不能,除非你以某种方式公开那个私人。
对。我只是不想将方法直接公开到window
范围中。我认为解决方案可以更优雅。
看来您可以更改源代码
好吧,正如您已经注意到的那样,这令人困惑,但重点是,我自己还不确定。我必须保持默认的模块行为,但我希望我可以扩展一下模块,以帮助应用这个猴子补丁。
如果你想要完整的故事,有一个运行 WebApp 的 IoT 设备,它使用我们讨论的模块将微控制器寄存器状态转换为 DOM 视图。我们称之为registers_into_view
模块。
通过复制相同的模块,创建了一个网络服务器,它:
registers_into_view
模块创建模型视图(通常应在前端发生)通过扩展相同的 WebApp,创建了“云”Web 应用程序,它:
registers_into_view
模块,而是通常,我会重构整个架构:
registers_into_view
后端模块的副本registers_into_view
在前端重用现有模块但是使用堆栈的 A 公司拒绝这样做。现有模式为他们工作 6 年。事实上,他们的经理避免大的变动,因为创建这个软件的 B 公司为这项工作收取了很多钱。所以他们没有重构的动机。
嗯,我有。我在 C 公司工作,我们将销售相同的物联网设备。
我们还想使用现有的前端 WebApp。
为了满足这个需求,我扩展了我们现有的 IoT 服务器以支持这些设备及其前端。
所以我必须使用另一种语言和框架复制另一个服务器 API 合同。
现在,registers_into_view
我认为不是重新创建在后端服务器上运行的1500 LoC模块,而是将现有registers_into_view
模块导入“云”WebApp 并对其进行猴子补丁以从 JSON 而不是内存转储(我们的private_method
)中检索注册数据。
入侵应该尽可能少,以增加我的补丁被合并的机会。
现在我希望我的动机足够清楚。我发现每个人都不太感兴趣,因此尝试从上下文中清除编程任务。
我无法更改public_method
模块的 ,因为实际上它运行了大约 50 个其他私有方法,这些方法retreive_data_method
都只使用不同的请求调用并将结果放在 DOM 中的不同位置。
奇迹般有效。如此简单和优雅。我会根据我的需要稍微简化一下:
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()
与您的解决方案中的直接替换不同,我尝试将模块上下文保存在一些公开的变量中,并通过它找到私有方法。这没有用。
谢谢。
我必须升级一个用 The Revealing Module Pattern 编写的 1500 LoC JS 模块中的一个小型私有函数。
我认为您的意思是您必须在运行时从“模块”功能外部执行此操作。这通常称为“monkeypatching”。
你不能,除非你以某种方式公开那个私人。
我找到的唯一可行的解决方案是重写
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() { /* ... */ };
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句