javascript “猴子补丁”真的有那么糟糕吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5741877/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is "monkey patching" really that bad?
提问by maerics
Some languages like Ruby and JavaScript have open classes which allow you to modify interfaces of even core classes like numbers, strings, arrays, etc. Obviously doing so could confuse others who are familiar with the API but is there a good reason to avoid it otherwise, assuming that you are adding to the interface and not changing existing behavior?
某些语言(如 Ruby 和 JavaScript)具有开放类,允许您修改甚至核心类(如数字、字符串、数组等)的接口。显然,这样做会使熟悉 API 的其他人感到困惑,但是否有充分的理由避免使用它,假设您要添加到界面而不改变现有行为?
For example, it might be nice to add a an Array.mapimplementation to web browsers which don't implement ECMAScript 5th edition (and if you don't need all of jQuery). Or your Ruby arrays might benefit from a "sum" convenience method which uses "inject". As long as the changes are isolated to your systems (e.g. not part of a software package you release for distribution) is there a good reason not to take advantage of this language feature?
例如,将Array.map实现添加到未实现 ECMAScript 第 5 版的 Web 浏览器(如果您不需要全部 jQuery)可能会很好。或者您的 Ruby 数组可能会受益于使用“注入”的“总和”便利方法。只要更改与您的系统隔离(例如,不是您为分发而发布的软件包的一部分),是否有充分的理由不利用此语言功能?
采纳答案by Michael Kohl
Wikipedia has a short summary of the pitfalls of monkey-patching:
维基百科对猴子补丁的陷阱有一个简短的总结:
http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls
http://en.wikipedia.org/wiki/Monkey_patch#Pitfalls
There's a time and place for everything, also for monkey-patching. Experienced developers have many techniques up their sleeves and learn when to use them. It's seldom a technique per se that's "evil", just inconsiderate use of it.
任何事情都有时间和地点,也有猴子补丁。经验丰富的开发人员掌握了许多技巧,并了解何时使用它们。很少有技术本身是“邪恶的”,只是不体贴地使用它。
回答by JUST MY correct OPINION
Monkey-patching, like many tools in the programming toolbox, can be used both for good and for evil. The question is where, on balance, such tools tend to be most used. In my experience with Ruby the balance weighs heavily on the "evil" side.
Monkey-patching 与编程工具箱中的许多工具一样,可用于善恶。问题是,总的来说,这些工具最常用于何处。在我使用 Ruby 的经验中,平衡在“邪恶”方面占很大比重。
So what's an "evil" use of monkey-patching? Well, monkey-patching in general leaves you wide open to major, potentially undiagnosable clashes. I have a class A
. I have some kind of monkey-patching module MB
that patches A
to include method1
, method2
and method3
. I have another monkey-patching module MC
that also patches A
to include a method2
, method3
and method4
. Now I'm in a bind. I call instance_of_A.method2
: whose method gets called? The answer to that can depend on a lot of factors:
那么猴子补丁的“邪恶”用途是什么?嗯,一般来说,猴子补丁会让你对主要的、可能无法诊断的冲突持开放态度。我有一堂课A
。我有某种猴子修补模块MB
,可以修补A
包含method1
,method2
和method3
. 我有另一个猴子修补模块MC
,它也修补A
以包含method2
,method3
和method4
。现在我陷入了困境。我打电话instance_of_A.method2
:谁的方法被调用?答案可能取决于很多因素:
- In which order did I bring in the patching modules?
- Are the patches applied right off or in some kind of conditional circumstance?
- AAAAAAARGH! THE SPIDERS ARE EATING MY EYEBALLS OUT FROM THE INSIDE!
- 我是按什么顺序引入接线模块的?
- 补丁是立即应用还是在某种有条件的情况下应用?
- 啊啊啊啊啊!蜘蛛正在从内部吃掉我的眼球!
OK, so #3 is perhaps a tad over-melodramatic....
好吧,所以#3 可能有点过于戏剧化了....
Anyway, that's the problem with monkey-patching: horrible clashing problems. Given the highly-dynamic nature of the languages that typically support it you're already faced with a lot of potential "spooky action at a distance" problems; monkey-patching just adds to these.
无论如何,这就是猴子补丁的问题:可怕的冲突问题。鉴于通常支持它的语言的高度动态特性,您已经面临许多潜在的“远距离动作”问题;猴子补丁只是增加了这些。
Having monkey-patching available is nice if you're a responsible developer. Unfortunately, IME, what tends to happen is that someone sees monkey-patching and says, "Sweet! I'll just monkey-patch this in instead of checking to see if other mechanisms might not be more appropriate." This is a situation roughly analogous to Lisp code bases created by people who reach for macros before they think of just doing it as a function.
如果您是一个负责任的开发人员,那么可以使用猴子补丁是很好的。不幸的是,在 IME 中,经常发生的事情是有人看到猴子补丁并说:“太棒了!我只会用猴子补丁而不是检查其他机制是否更合适。” 这种情况大致类似于人们创建的 Lisp 代码库,这些人在考虑将宏作为一个函数来使用之前就已经使用了宏。
回答by Raynos
As long as the changes are isolated to your systems (e.g. not part of a software package you release for distribution) is there a good reason not to take advantage of this language feature?
只要更改与您的系统隔离(例如,不是您为分发而发布的软件包的一部分),是否有充分的理由不利用此语言功能?
As a lone developer on an isolated problem there are no issues with extending or altering native objects. Also on larger projects this is a team choice that should be made.
作为孤立问题的单独开发人员,扩展或更改本机对象没有任何问题。同样在较大的项目中,这是应该做出的团队选择。
Personally I dislike having native objects in javascript altered but it's a common practice and it's a valid choice to make. If your going to write a library or code that is meant to be used by other's I would heavily avoid it.
我个人不喜欢改变 javascript 中的原生对象,但这是一种常见的做法,也是一个有效的选择。如果您要编写供其他人使用的库或代码,我会极力避免它。
It is however a valid design choice to allow the user to set a config flag which states please overwrite native objects with your convenience methods because there's so convenient.
然而,允许用户设置一个配置标志是一个有效的设计选择,该标志声明请使用您的便利方法覆盖本机对象,因为它非常方便。
To illustrate a JavaScript specific pitfall.
为了说明 JavaScript 特定的陷阱。
Array.protoype.map = function map() { ... };
var a = [2];
for (var k in a) {
console.log(a[k]);
}
// 2, function map() { ... }
This issue can be avoided by using ES5 which allows you to inject non-enumerable properties into an object.
这个问题可以通过使用 ES5 来避免,它允许你将不可枚举的属性注入到一个对象中。
This is mainly a high level design choice and everyone needs to be aware / agreeing on this.
这主要是一个高层次的设计选择,每个人都需要意识到/同意这一点。
回答by Dafydd Rees
It's perfectly reasonable to use "monkey patching" to correct a specific, known problem where the alternative would be to wait for a patch to fix it. That means temporarily taking on responsibility for fixing something until there's a "proper", formally released fix that you can deploy.
使用“猴子补丁”来纠正特定的已知问题是完全合理的,替代方法是等待补丁修复它。这意味着暂时承担修复某些问题的责任,直到出现可以部署的“适当的”、正式发布的修复程序。
A considered opinion by Gilad Bracha on Monkey Patching: http://gbracha.blogspot.com/2008/03/monkey-patching.html
Gilad Bracha 关于猴子补丁的经过深思熟虑的意见:http: //gbracha.blogspot.com/2008/03/monkey-patching.html
回答by Chris Oei
The conditions your describe -- adding (not changing) existing behavior, and not releasing your code to the outside world -- seem relatively safe. Problems could come up, however, if the next version of Ruby or JavaScript or Rails changes their API. For example, what if some future version of jQuery checks to see if Array.map is already defined, and assumes it's the EMCA5Script version of map when in actuality it's your monkey-patch?
您描述的条件——添加(不改变)现有行为,并且不向外界发布您的代码——似乎相对安全。但是,如果下一版本的 Ruby、JavaScript 或 Rails 更改其 API,则可能会出现问题。例如,如果某个未来版本的 jQuery 检查是否已经定义了 Array.map,并假设它是 map 的 EMCA5Script 版本,而实际上它是您的猴子补丁呢?
Similarly, what if you define "sum" in Ruby, and one day you decide you want to use that ruby code in Rails or add the Active Support gem to your project. Active Support also defines a sum method (on Enumerable), so there's a clash.
类似地,如果您在 Ruby 中定义“sum”,并且有一天您决定要在 Rails 中使用该 ruby 代码或将 Active Support gem 添加到您的项目中,该怎么办?Active Support 还定义了一个 sum 方法(在 Enumerable 上),因此存在冲突。
回答by CertainPerformance
With regards to Javascript:
关于 Javascript:
is there a good reason to avoid it otherwise, assuming that you are adding to the interface and not changing existing behavior?
假设您正在添加到界面而不改变现有行为,是否有充分的理由避免它?
Yes. Worst-case, even if you don't alter existingbehavior, you could damage the futuresyntax of the language.
是的。最坏的情况是,即使您不改变现有行为,也可能会破坏该语言的未来语法。
This is exactly what happened with Array.prototype.flatten
and Array.prototype.contains
. In short, the specification was written up for those methods, their proposals got to stage 3, and then browsers started shipping it. But, in both cases, it was found that there were ancient libraries which patched the built-in Array
object with their own methods with the same name as the new methods, and had different behavior; as a result, websites broke, the browsers had to back out of their implementations of the new methods, and the specification had to be edited. (The methods were renamed.)
这正是Array.prototype.flatten
和发生的情况Array.prototype.contains
。简而言之,规范是为这些方法编写的,他们的提案进入了第 3 阶段,然后浏览器开始发布它。但是,在这两种情况下,都发现有一些古老的库Array
使用与新方法同名的自己的方法修补内置对象,并具有不同的行为;结果,网站崩溃了,浏览器不得不退出新方法的实现,并且必须编辑规范。(这些方法已重命名。)
If you mutate a built-in object like Array
on your own browser, on your own computer, that's fine. (This is a very useful technique for userscripts.) If you mutate a built-in object on your public-facing site, that's less fine - it mayeventually result in problems like the above. If you happen to control a big site(like stackoverflow.com) and you mutate a built-in object, you can almost guarantee that browsers will refuse to implement new features/methods which break your site (because then users of that browser will not be able to use your site, and they will be more likely to migrate to a different browser). (see herefor an explanation of these sorts of interactions between the specification writers and browser makers)
如果您像Array
在您自己的浏览器上、在您自己的计算机上那样改变内置对象,那很好。(这对于用户脚本来说是一种非常有用的技术。)如果您在面向公众的站点上改变内置对象,那就不太好 -最终可能会导致上述问题。如果您碰巧控制了一个大站点(如 stackoverflow.com)并且您改变了一个内置对象,您几乎可以保证浏览器将拒绝实现破坏您站点的新功能/方法(因为该浏览器的用户不会能够使用您的网站,并且他们更有可能迁移到不同的浏览器)。(有关规范编写者和浏览器制造商之间的此类交互的解释,请参见此处)
All that said, with regards to the specific examplein your question:
综上所述,关于您问题中的具体示例:
For example, it might be nice to add a an Array.map implementation to web browsers which don't implement ECMAScript 5th edition
例如,将 Array.map 实现添加到未实现 ECMAScript 第 5 版的 Web 浏览器可能会很好
This is a very common and trustworthy technique, called a polyfill.
这是一种非常常见且值得信赖的技术,称为polyfill。
A polyfill is code that implements a feature on web browsers that do not support the feature. Most often, it refers to a JavaScript library that implements an HTML5 web standard, either an established standard (supported by some browsers) on older browsers, or a proposed standard (not supported by any browsers) on existing browsers
polyfill 是在不支持该功能的 Web 浏览器上实现该功能的代码。大多数情况下,它指的是实现 HTML5 Web 标准的 JavaScript 库,要么是旧浏览器上的既定标准(某些浏览器支持),要么是现有浏览器上的提议标准(任何浏览器都不支持)
For example, if you wrote a polyfill for Array.prototype.map
(or, to take a newer example, for Array.prototype.flatMap
) which was perfectly in linewith the official Stage 4 specification, and then ran code that defined Array.prototype.flatMap
on browsers which didn't have it already:
例如,如果你写的填充工具Array.prototype.map
(或者,采取了新的例子,对于Array.prototype.flatMap
)这是完全符合与官方的第4阶段规范,然后跑了定义的代码Array.prototype.flatMap
上没有它已经浏览器:
if (!Array.prototype.flatMap) {
Array.prototype.flatMap = function(...
// ...
}
}
If your implementation is correct, this is perfectly fine, and is very commonly done all over the web so that obsolete browsers can understand newer methods. polyfill.iois a common service for this sort of thing.
如果您的实现是正确的,这完全没问题,并且在整个网络上非常普遍,以便过时的浏览器可以理解更新的方法。polyfill.io是此类事情的常见服务。