JavaScript:扩展 Array.prototype 有什么危险?

IT技术 javascript coding-style prototype
2021-02-09 04:06:39

Google JavaScript 风格指南建议不要扩展Array.prototype. 但是,我将其用作Array.prototype.filter = Array.prototype.filter || function(...) {...}在不存在的浏览器中使用它(和类似方法)的一种方式。MDN 实际上提供了类似的例子

我知道Object.prototype问题,但Array不是哈希表。

扩展时可能会出现哪些问题Array.prototype,导致 Google 不建议这样做?

6个回答

大多数人都忽略了这一点。在我看来,Polyfilling 或 shimming 标准功能Array.prototype.filter使其在旧浏览器中工作一个好主意。不要听那些仇恨者的话。Mozilla 甚至在 MDN 上向您展示了如何做到这一点。通常不扩展Array.prototype或其他原生原型的建议可能归结为以下之一:

  1. for..in 可能无法正常工作
  2. 其他人可能也想用相同的函数名扩展 Array
  3. 即使使用 shim,它也可能无法在每个浏览器中正常工作。

以下是我的回复:

  1. 您通常不需要for..in在 Array 上使用如果你这样做,你可以hasOwnProperty用来确保它是合法的。
  2. 仅当您知道自己是唯一一个这样做的人当它是标准的东西时才扩展本地人,例如Array.prototype.filter.
  3. 这很烦人,而且咬了我。旧 IE 有时会在添加此类功能时遇到问题。您只需要根据具体情况查看它是否有效。对我来说,我遇到的问题是添加Object.keys到 IE7。它似乎在某些情况下停止工作。你的旅费可能会改变。

查看这些参考资料:

祝你好运!

@mrdoob 这值得研究吗?
2021-03-16 04:06:39
“你不需要使用 for..in”——根本不要使用它。即使使用hasOwnProperty,您仍然会跳过length,这在大多数情况下没有意义。
2021-03-22 04:06:39
@NickWiggill,因为 .. in 不是为数组设计的,它是为类似数组的对象设计的(读取关联数组)。你永远不应该使用 for .. in 来表示数组。
2021-04-01 04:06:39
您可以避免使用扩展 Array.prototype 对象的枚举问题 Object.defineProperty()
2021-04-03 04:06:39
@Malvolio 不同意:您无法轻松了解/控制您的 3rd 方库中的内容。例如,for..inThree.js 的 SEA3D 加载器中断,我添加了Array.prototype. 事实上,three.js 的大部分内容都使用for..in. 真的,当心。这些不是必须找到的令人愉快的错误。
2021-04-08 04:06:39

我将从 Nicholas Zakas 的优秀文章可维护 JavaScript:不要修改您不拥有的对象中给出要点和关键句子

  • 可靠性:“简单的解释是,企业软件产品需要一致且可靠的执行环境才能进行维护。”
  • 不兼容的实现:“修改你不拥有的对象的另一个危险是命名冲突和不兼容的实现的可能性。”
  • 如果每个人都这样做了呢?:“简单地说:如果你团队中的每个人都修改了他们不拥有的对象,你很快就会遇到命名冲突、不兼容的实现和维护噩梦。”

基本上,不要这样做。即使您的项目永远不会被其他人使用,并且您永远不会导入第三方代码,也不要这样做。你会养成一个可怕的习惯,当你开始尝试与他人友好相处时,这个习惯可能很难打破。

@jsumners,如果 John 或 Jane 已经写了VeryUsefulObject但仍然缺少exactlyWhatINeed方法怎么办?
2021-03-31 04:06:39
如果团队同意,扩展宿主对象以使其符合标准并没有错。使用非标准属性扩展宿主对象是一个不同的游戏,我同意“不要这样做”
2021-04-07 04:06:39
通过在您自己的对象上实现该功能,您可以想出任何修改宿主对象的理由都可以轻松克服。宿主环境的文档不包括您的修改。无论“团队”同意什么,这都会引起混乱。那个新来的人不知道你所有的主机修改,但你告诉他可以更改主机对象呢?事情可能会很快破裂。
2021-04-08 04:06:39
事实中的这种推理建议反对 OOP
2021-04-09 04:06:39
“宿主环境的文档不包括您的修改。” - 什么文件?我们在这里主要讨论支持可能符合或可能符合标准的未知 Web 浏览器,并且只是添加缺少的功能。如果您正在扩展主机对象以使其符合标准(如 Raynos 所述),那么大概就像在问题中一样,您首先要测试该功能是否已经存在,并且仅在需要时添加您自己的版本。
2021-04-10 04:06:39

作为 Jamund Ferguson 答案的现代更新:

通常,不扩展 Array.prototype 或其他原生原型的建议可能归结为以下之一:

  1. 因为..in 可能无法正常工作
  2. 其他人可能也想用相同的函数名扩展 Array
  3. 即使使用 shim,它也可能无法在每个浏览器中正常工作。

现在可以在ES6 中通过使用Symbol添加您的方法来缓解第 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 按预期工作,符号不会迭代。
  • 没有方法名称冲突,因为符号是局部范围的,并且需要努力检索。

缺点:

@johnywhy 是的,但是如果您这样做(例如通过添加Array.prototype.f = Array.prototype[ myMethod ]),那么该方法可用于(并可能与其他库代码发生冲突)。
2021-03-30 04:06:39
arr[myMethod]() -- Cantcha 将其包装在一个更简单的调用中?
2021-04-02 04:06:39

扩展Array.prototype您自己的应用程序代码是安全的(除非您for .. in在数组上使用,在这种情况下您需要为此付费并享受重构它们的乐趣)。

在您打算其他人使用的库中扩展本机宿主对象并不酷。您无权破坏自己图书馆中其他人的环境。

做这一点的背后,如可选的方法lib.extendNatives()还是有[].filter作为的要求。

扩展本地对象和宿主对象

原型就是这样做的。这是邪恶的。以下代码段演示了这样做会如何产生意想不到的结果:

<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 个字符。

他们不是邪恶的,邪恶的部分是你的代码,如果你把它包装在 .hasOwnProperty 中,只会显示它自己的属性,没有冒犯
2021-03-21 04:06:39
为什么要使用 for ... in 而不是 forEach?不需要拥有自己的财产或弄乱作用域变量
2021-03-30 04:06:39
我认为使用 .hasOwnProperty 迭代数组在语法上并不优雅。
2021-04-02 04:06:39