JavaScript的本质允许完全重写其本机对象。我想知道这样做是否真的有危险!
以下是本机JavaScript对象的一些示例
Object
Function
Number
String
Boolean
Math
RegExp
Array
让我们假设我想对这些模型进行建模,以遵循您在Java(和其他一些OOP语言)中可能找到的类似模式,以便Object定义一组基本函数,并且每个其他对象继承它(这将具有由Java用户明确定义,其中所有东西都自然地来源于对象)
示例:
Object = null;
function Object() {
Object.prototype.equals = function(other) {
return this === other;
}
Object.prototype.toString = function() {
return "Object";
}
Object.equals = function(objA, objB) {
return objA === objB;
}
}
Boolean = null;
function Boolean() {
}
extend(Boolean, Object); // Assume extend is an inheritance mechanism
Foo = null;
function Foo() {
Foo.prototype.bar = function() {
return "Foo.bar";
}
}
extend(Foo, Object);
在这种情况下,Object和Boolean现在有了新的实现。在这方面,可能会发生什么?我可能会把事情进一步打破吗?
修改
我在某处读过像MooTools和Prototype这样的框架有类似的方法,这是正确的吗?
答案 0 :(得分:4)
猴子修补这样的内置类是一个有争议的话题。我个人不喜欢为2个原因做这件事:
内置类是全局范围。这意味着如果两个不同的模块尝试向全局类添加具有相同名称的方法,那么它们将发生冲突,从而导致细微的错误。更为巧妙的是,如果未来版本的浏览器决定使用相同的名称实现方法,那么您也遇到了麻烦。
在公共类的原型中添加内容可以破坏使用for-in循环的代码而不需要hasOwnProperty检查(JS新手经常对对象和数组执行此操作,因为for-in看起来像foreach环)。如果你不是100%确定你使用的代码是安全地使用for-in循环那么monkeypatching Object.prototype可能会导致问题。
也就是说,有一种情况我发现monkeypatching builtins是可以接受的,并且是在旧浏览器上添加新浏览器的功能(例如,数组的forEach方法)。在这种情况下,您可以避免与未来的浏览器版本发生冲突,并且不会让任何人感到意外。但即便如此,我仍然会建议使用来自第三方的垫片,而不是自己编码,因为通常很多棘手的角落案例很难做到。
答案 1 :(得分:1)
这里有一定程度的偏好,但我个人认为,这种事情有可能成为一个巨大的难以处理的混乱。
例如,您从两个项目A和B开始,每个项目都决定在String上实现各种非常有用的流畅方法。
项目A已决定String
需要isEmpty
函数,如果字符串为零长度或仅为空格,则返回true
。
项目B已决定String
需要isEmpty
函数,如果字符串为零长度,则返回true
,返回isEmptyOrWhitespace
函数返回true
如果一个字符串是零长度或只是空格。
现在你有一个项目想要使用项目A中的一些代码和项目B中的一些代码。它们都广泛使用了他们的自定义isEmpty
函数。你有没有机会成功加入这两个?可能不是。你可以说是集群安排。
请注意,这与C#中的扩展方法完全不同,您至少必须导入包含静态类的命名空间才能获得扩展方法,没有运行时冲突,并且可以合理地从A和B中消耗项目,只要你没有导入他们的扩展命名空间(希望他们有远见将他们的扩展类放在一个单独的命名空间中)。
答案 2 :(得分:0)
我知道JS中最糟糕的情况是undefined
。你可以定义它。
您可以执行undefined = 'blah';
等操作....此时,您无法再依赖if(x === undefined)
。这可能很容易破坏代码中的其他地方(或者,当然,在您可能正在使用的第三方库中)。
这完全是疯狂的,但绝对显示了任意覆盖内置对象的绝对危险。
另请参阅:http://wtfjs.com/2010/02/15/undefined-is-mutable
要获得更加明智的例子,请选择Sahi browser testing tool。此工具允许您为浏览器编写自动脚本以测试您的站点。 (类似于Selenium)。这样做的一个问题是,如果您的网站使用alert()
或confirm()
,则脚本会在等待用户输入时停止运行。 Sahi通过使用自己的存根函数覆盖这些函数来解决这个问题。
答案 3 :(得分:0)
我避免覆盖固有对象的默认行为。它几次让我感到厌烦,而其他人我很好。您可以查看一个示例库,Sugar.js。它是一个很棒的库,有些人喜欢,但我通常只是因为它扩展了现有JavScript对象的行为,例如你正在做的事情。
然而,我认为你会发现这纯粹是意见和风格。