41条对违反抽象原则行为的讨论之后,下面聊一聊终极违例。由于对象共享原型,因此每一个对象都可以增加、删除或修改原型的属性。这个有争议的实践通常称为猴子补丁。
猴子补丁示例
猴子补丁的吸引力在于其强大。数组缺少一个有用的方法吗?你自己就可以增加它。
Array.prototype.split=function(i){
return [this.slice(0,i),this.slice(i)];
}
很完美,现在可以在任意的数组上调用这个方法了。但当多个库以不兼容的方式给同一个原型打猴子补丁时,另外的库使用同一个方法名给Array.prototype打猴子补丁。
Array.prototype.split=function(){
var i=Math.floor(this.length);
return [this.slice(0,i),this.slice(i)];
}
问题
现在,任一对数组split方法的使用都大约有50%的机会被破坏,这取决于它们期望这两个方法哪一个被调用。
至少,任一修改其享原型的程序库都应当清晰地记录其修改。这至少能给使用者在关于不同库之间潜在的冲突提供足够的警告。但是,两个以冲突的方式给原型打猴子补丁的程序库不能在同一个程序中使用。
替代的方法
一种替代的方法是,如果库仅仅是将给原型打猴子补丁作为一种便利,那么可以将这些修改置于一个函数中,用户可以选择调用或忽略。
function addArrayMethods(){
Array.prototype.split=funciton(i){
return [this.slice(0,i),this.slice(i)]
}
}
这种方法只有在程序库提供了addArrayMethods函数时才能工作,而实际上并不依赖于Array.prototype.split函数。
ployfill
尽管猴子补丁很危险,但有一种告别可靠而且有价值的使用场景:ployfill。js程序和库经常部署在多个平台,这些平台实现了多少个标准API可能是有区别的。缺失的方法的行为是广泛支持的标准所定义的,而且许多程序和库可能依赖这些方法。由于它们的行为是标准化的,因此实现这些方法并不会造成与库之间不兼容性的类似的风险。事实上,多个库都可以给同一个标准方法提供实现(假设是被正确实现),因为它们都实现了相同的标准API。
你可以通过使用带有测试条件的守护猴子补丁来安全地弥补这些平台的差距。
if(typeof Array.prototype.map!=="function"){
Array.prototype.map=function(f,thisArg){
var res=[];
for(var i=0,n=this.length;i < n;i++){
res[i]=f.call(thisArg,this[i],i);
}
return res;
}
}
提示
-
避免使用轻率的猴子补丁
-
记录程序库所执行的所有猴子补丁
-
考虑通过将修改置于一个导出函数中,使猴子补丁成为可选的
-
使用猴子补丁为缺失的标准API提供polyfills