【每日一题NO.89】我为什么不推荐你扩展JS的内置对象

前端印记

共 2242字,需浏览 5分钟

 ·

2021-11-24 10:24


内置对象的定义

内置对象与宿主无关,是独立于宿主环境的 ECMAScript 实现提供的对象。他们在 ECMAScript 程序开始执行前就存在,本身就是实例化内置对象,开发者无需再去实例化。
内置对象是原生对象的子集,包含:GlobalMath
ECMAScript5 中增添了 JSON 这个存在于全局的内置对象

宿主对象的定义

由 ECMAScript 实现的宿主环境提供的对象,包含两大类,一个是宿主提供,一个是自定义类对象
所有非本地对象都属于宿主对象
对于嵌入到网页中的 JS 来说,其宿主对象就是浏览器提供的对象,浏览器对象有很多,如 WindowDocument 等,所有的 DOM 和 BOM 对象都属于宿主对象。

为什么扩展 JS 内置对象不是好的做法?

可靠性

当项目只有你一个人时,你想怎么改就怎么改,因为你很清楚它们。
当你的项目不止一个人时,就很容易出现冲突,并且你会为了解决这个冲突而花费更多的时间。

举个真实的例子: JavaScript 红皮书的作者在雅虎工作时因为同事更改了一个 YAHOO.util.Event.stopEvent()方法,结果导致他花了几天的时间去解决这个问题,当好不容易解决了,更多的 bug 出现了,因为还有很多地方也都依赖于这个方法。所以他说了一句话: Don't modify objects you don't own

兼容性

不单单是上面的问题,你可能会更关心你的项目是可以在将来使用,吸取一下 Prototype 的教训。
Prototype 最著名的失误在于当时实现document.getElementsByClassName直接使用了 Document.prototype 并返回了一个数组的实例。

大概的一个前因后果就是 Prototype 写了一个很好的方法,后来大家都觉得好,所以 JS 在原生的方法上也加了这个。但是不幸的是,Prototype 返回的是 Array,JS 返回的是 NodeList。那在原生方法没出现之前这句代码document.getElementsByClassName('myclass')可以好好的执行,但是原生方法出来后,NodeList 没有 each 方法,所以 Prototype 就悲剧了…

如果所有人都改呢?这样的话问题的严重性就不用说了。

扩展问题

扩展 JavaScript 内置(原生)对象意味着将属性或方法添加到其 prototype 中。

虽然听起来很不错,但事实上这样做很危险。

想象一下,你的代码使用了一些库,它们通过添加相同的 contains 方法来扩展 Array.prototype,如果这两个方法的行为不相同,那么这些实现将会相互覆盖,你的代码将不能正常运行。

扩展内置对象的唯一使用场景是创建 polyfill,本质上为老版本浏览器缺失的方法提供自己的实现,该方法是由 JavaScript 规范定义的。回到 2005 年,Prototype.js 框架是最先提出扩展 Javascript 原生对象的内置 prototype 属性的框架之一。他们的想法是我们可以通过向 prototype 属性添加额外的方法来扩展现有的功能。

如果你对近十年 Javascript 编程做一个简单的调查,比如使用 google 简单搜索下,你会发现对于这个想法有很多反对意见。它们都是有原因的。

大多数开发者会告诉你:“不要扩展原生对象” 或者 “只在 polyfill 的时候扩展原生对象”。后者意味着只有当扩展的功能已经被列入规范然后你只是为了能在旧的环境中使用这些功能的时候才能对元素对象进行扩展。

未来约束

比如假设有有个非常流行的库,是通过扩展内置对象来实现,然后现实中的很多网站都使用了这个库。如果发生这样一件事,突然一天 TC39 协会通过某种方式强制避免扩展官方规范,那么这些依赖于扩展的网站都将崩溃。

这就是扩展原生对象的危险所在:开发者为了实现一些的功能然后扩展原生对象,然后就拍拍屁股把这些烂摊子留给了 TC39 成员们。因为开发者错误的决定 Javascript 的维护者只能选择其他机制。

像类似的这种情况,有个大家都差不多知道的问题,typeof null === "object",这个 bug 为什么一直没有修复?这是因为太多的代码都依赖于这段代码,如果修复了这个 bug,那么结果可想而知。

所以还是要记住:扩展原生对象是一件危险并且充满冒险的事情,即使是 polyfill,我们需要的也是更多不破坏规范的 polyfill,要良好,稳固,性能优秀但是和标准兼容的 polyfill


立足行业规范,考虑多人协同。
所以,扩展原生对象主要对库的设计者也提出了更严格的要求,不仅要面对当下,实现功能。也要展望未来,确保插件遵循规范,可扩展、可维护。保证库的鲁棒性。


所有《每日一题》的 知识大纲索引脑图 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以点击文末的 “阅读原文” 快速跳转


用一个十年拉扯自己长大,
那时未来已达,星辰开花。

浏览 73
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报