JavaScript:扩展Array.prototype有什么危险?
Google JavaScript风格指南build议不要扩展Array.prototype
。 但是,我使用Array.prototype.filter = Array.prototype.filter || function(...) {...}
Array.prototype.filter = Array.prototype.filter || function(...) {...}
作为在不存在的浏览器中使用它(以及类似的方法)的一种方式。 MDN实际上提供了类似的例子 。
我知道关于Object.prototype
问题,但Array
不是一个哈希表。
在扩展Array.prototype
,可能会出现什么问题,让Google反对呢?
大多数人都错过了这一点。 像Array.prototype.filter
这样的填充或填充标准function,以便它在旧版浏览器中工作是一个好主意,在我看来。 不要听仇恨。 Mozilla甚至向你展示了如何在MDN上做到这一点。 通常,不扩展Array.prototype
或其他原生原型的build议可能归结为以下之一:
-
for..in
可能无法正常工作 - 其他人也可能想要使用相同的函数名称来扩展Array
- 即使使用垫片,它也可能无法在每个浏览器中正常工作。
这是我的回应:
- 通常你不需要在Array上使用
for..in
。 如果你这样做,你可以使用hasOwnProperty
来确保它是合法的。 - 只有当你知道你是唯一一个正在执行的, 或者是像
Array.prototype.filter
这样的标准东西的时候,才能扩展本地的。 - 这是烦人的,有我。 旧IE浏览器有时会遇到添加这种function的问题。 你只需要看看它是否在个案的基础上工作。 对我来说,我的问题是添加
Object.keys
到IE7。 它在某些情况下似乎停止工作。 你的旅费可能会改变。
看看这些参考资料:
- http://perfectionkills.com/extending-native-builtins/
- http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
- https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter
- https://github.com/kriskowal/es5-shim
祝你好运!
我会给你从尼古拉斯扎卡斯的优秀文章的重点句子, 维护JavaScript:不要修改你不属于自己的对象 :
- 可靠性 :“简单的解释是企业软件产品需要一个一致可靠的执行环境来维护。”
- 不兼容的实现 :“修改你不拥有的对象的另一个危险是命名冲突和不兼容实现的可能性。
- 如果每个人都这样做呢? :“简单地说,如果你的团队中的每个人都修改了他们不拥有的对象,你很快就会碰到命名冲突,不兼容的实现和维护噩梦。
基本上,不要这样做。 即使你的项目永远不会被别人使用,而且你永远不会input第三方代码,也不要这样做。 你会build立一个可怕的习惯,当你开始尝试和别人打好关系的时候,这个习惯很难打破。
在你自己的应用程序代码中扩展Array.prototype
是安全的(除非你在数组中使用for .. in
在这种情况下,你需要付出代价,并有乐趣重构它们)。
在你打算别人使用的库中扩展本地主机对象并不酷。 你无权腐败你自己的图书馆里的其他人的环境。
可以在lib.extendNatives()
类的可选方法之后执行此操作,或者将[].filter
作为要求执行。
扩展本地和主机对象
有些人使用for ... in
循环遍历数组。 如果你添加一个方法到原型,循环也会尝试迭代该键。 当然,你不应该使用它,但有些人无论如何。
原型做到这一点。 这是邪恶的。 以下片段演示如何这样做可以产生意想不到的结果:
<script language="javascript" src="https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js"></script> <script language="javascript"> a = ["not", "only", "four", "elements"]; for (var i in a) document.writeln(a[i]); </script>
结果:
not only four elements function each(iterator, context) { var index = 0; . . .
和约5000个字符以上。
你可以用poser
库轻松地创build一些沙箱。
看看https://github.com/bevacqua/poser
var Array2 = require('poser').Array(); // <- Array Array2.prototype.eat = function () { var r = this[0]; delete this[0]; console.log('YU NO .shift()?'); return r; }; var a = new Array2(3, 5, 7); console.log(Object.keys(Array2.prototype), Object.keys(Array.prototype))
作为Jamund Ferguson的回答的一个现代更新:
通常,不扩展Array.prototype或其他原生原型的build议可能归结为以下之一:
- for..in可能无法正常工作
- 其他人也可能想要使用相同的函数名称来扩展Array
- 即使使用垫片,它也可能无法在每个浏览器中正常工作。
现在可以通过使用符号添加您的方法在ES6中缓解点1和2。
它使一个稍微笨拙的调用结构,但增加了一个没有迭代,不能轻易重复的属性。
// Any string works but a namespace may make library code easier to debug. var myMethod = Symbol('MyNamespace::myMethod'); Array.prototype[ myMethod ] = function(){ /* ... */ }; var arr = []; // slightly clumsier call syntax arr[myMethod](); // Also works for objects Object.prototype[ myMethod ] = function(){ /* ... */ };
优点:
- For..in按预期工作,符号不会迭代。
- 作为符号的方法名称的冲突没有局限于范围,并努力检索。
缺点:
- 只适用于现代环境
- 语法稍微笨重
扩展原型是一个只能使用一次的技巧。 你这样做,你使用一个图书馆也是(不兼容的方式)和繁荣 !
您正在重写的函数可能会被内部JavaScript调用使用,并可能导致意外的结果。 这是准则的原因之一
例如,我重写了数组的indexOf函数,并使用[]来搞乱访问数组。
我相信这个问题值得ES6最新的答案。
ES5
首先,正如很多人所说的那样。 将本地原型扩展到填充或填充新标准或修复错误是标准操作,而不是有害的。 例如,如果浏览器不支持.filter方法if (!Array.prototype.filter)
您可以自由添加此function。 事实上,这个语言就是为了pipe理向后兼容性而devise的。
现在,你可能会认为,因为JavaScript对象使用原型inheritance,所以扩展一个像Array.prototype
这样的本地对象而不会产生干扰应该很容易,但是直到ES6,这是不可行的。
与对象不同,例如,您必须依赖和修改Array.prototype
来添加自己的自定义方法。 正如其他人所指出的那样, 这是不好的,因为它污染了Global的命名空间,可能以一种意想不到的方式干扰其他代码,存在潜在的安全问题,是一个基本的罪过。
在ES5中,你可以尝试黑客攻击,但是这些实现并没有真正实用。 更多深入的信息,我build议你看看这个非常丰富的职位: http : //perfectionkills.com/how-ecmascript-5-still-does-not-allow-to-subclass-an-array/
您可以将一个方法添加到一个数组,甚至是一个数组构造函数,但是遇到的问题试图使用依赖length属性的本地数组方法。 最糟糕的是,这些方法将返回一个本地的Array.prototype
而不是你shiny的新的子类数组,即: subClassArray.slice(0) instanceof subClassArray === false
。
ES6
但是,现在使用ES6,可以使用class
和extends Array
来inheritance内置class
,从而克服所有这些问题。 它保持Array.prototype
完整,创build一个新的子类,它inheritance的数组方法将是相同的子类! https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/
看下面的小提琴演示: https : //jsfiddle.net/dmq8o0q4/1/