Javascript 在闭包中更改函数的行为
我必须升级一个1500 LoC JS模块中的一个小私有函数,用 简化的任务如下所示:Javascript 在闭包中更改函数的行为,javascript,closures,refactoring,private-methods,Javascript,Closures,Refactoring,Private Methods,我必须升级一个1500 LoC JS模块中的一个小私有函数,用 简化的任务如下所示: var模块=模块| |函数(){ 函数init(){ //您可以随时升级init,以便稍后在运行时替换private_方法 (函数not_important(){console.log(“我做一些其他事情”)})() } 函数公共_方法(){ 私有_方法() } 函数私有_方法(){ log(“原始私有方法”) } //您也可以在此处添加任何方法 返回{ init:init, 公共法:公共法, //并揭露他们
var模块=模块| |函数(){
函数init(){
//您可以随时升级init,以便稍后在运行时替换private_方法
(函数not_important(){console.log(“我做一些其他事情”)})()
}
函数公共_方法(){
私有_方法()
}
函数私有_方法(){
log(“原始私有方法”)
}
//您也可以在此处添加任何方法
返回{
init:init,
公共法:公共法,
//并揭露他们
}
}()
/**这是页面上的另一个脚本,它使用原始模块*/
module.public\u method()//返回“原始私有方法”
/**第三个脚本,只在某些环境中运行,要求模块的工作方式稍有不同*/
//在这里做任何JS魔术来代替私有方法吗
//或者调用module.init(方法\u到\u替换\u私有\u方法)
module.public\u method()//我想在这里看到private\u方法的一些其他行为
我学习过已经有问题了,看起来与我的任务最相关,但没有成功 我找到的唯一可行的解决办法是重写
function private_method(){}
到this.private_method=function(){…}
,它将它绑定到窗口,因此我可以在运行时更改它
但我不会这样做,因为这个方法不再是私有的,我无法预测旧的100000 LoC意大利面怪物应用程序(用老式ECMAScript 5编写)中会出现什么问题
更新这是答案上的答案 首先,感谢你的详细回答,T.J.克劳德 这通常被称为monkeypatching 真是太好了 你不能,除非你以某种方式揭露那个秘密 对。我只是不想将该方法直接暴露在
窗口中。我认为解决方案可以更优雅一些
看起来您可以更改源代码
正如你们已经注意到的,这让人困惑,但问题是,我自己还不确定。
我必须保持默认的模块行为,但我希望我可以扩展模块一点,以帮助应用这个猴子补丁
如果你想了解全部情况,有一个IoT设备运行WebApp,它使用我们讨论的模块将微控制器寄存器状态转换为DOM视图。我们将其称为注册到\u视图中模块
通过复制同一模块,创建了一个web服务器,该服务器:
- 接收微控制器内存转储
的复制将_注册到_视图中
模块创建一个模型视图(通常应发生在前端)
- 将视图发送为JSON格式
通过扩展相同的web应用程序,创建了“云”web应用程序,该应用程序:
- 将现有的
寄存器注册到\u视图
模块中,而不是
- 从后端接收视图数据
- 将其直接应用到DOM中
通常,我会重构整个体系结构:
- 将
寄存器的副本删除到后端的\u视图
模块中
- 将现有的
寄存器重新使用到前端的\u视图
模块中
但使用该堆栈的A公司拒绝这样做。现有的模式为他们工作了6年。事实上,他们的管理者避免大的改变,因为开发这个软件的B公司收取了大量的费用。所以他们没有重构的动机
嗯,我有。
我在C公司工作,我们将销售相同的物联网设备。
我们还希望使用现有的前端WebApp。
为此,我扩展了现有的物联网服务器,以支持这些设备及其前端。
因此,我必须使用另一种语言和框架复制另一个服务器API合同
现在,我想将已经存在的registers\u导入到\u view
模块中,并将其安装到“云”WebApp中,然后对其进行修补,从JSON而不是内存转储中检索寄存器数据(我们的私有\u方法
)
入侵应该尽可能少,以提高我的补丁被合并的可能性
现在我希望我的动机足够清楚。我发现它对每个人都不是那么有趣,所以我试图从上下文中清理编程任务
让我们来讨论解决方案。
你的解决方案2
我无法更改该模块的public\u方法
,因为实际上它运行约50个其他私有方法,这些方法都调用retretrieve\u data\u方法
,只需使用不同的请求,并将结果放在DOM中的不同位置
解决方案1
工作起来很有魅力。如此简单和优雅。我将根据自己的需要对其进行简化:
var模块=模块| |函数(){
函数public_method(){private_method()}
函数private_method(){console.log(“原始私有方法”)}
返回{
公共法:公共法,
//此函数允许在运行时更新private_方法
替换私有方法:函数(fn){
//函数声明有效地创建变量;
//你可以简单地写信给他们:
私有_方法=fn;
}
}
}()
//原始行为
module.public_方法()
//修补
module.replace_private_方法(function(){console.log(“我被猴子打过补丁”)});
//新行为
module.public_方法()
与您的解决方案中的直接替换不同,我尝试将模块上下文保存在某个公开变量中,并通过它查找私有方法。这没用
谢谢
我必须升级一个1500 LoC JS模块中的一个小私有函数,该模块是以显示模块模式编写的
我认为这很有趣
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() { /* ... */ });
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() { /* ... */ };