曾经看到过修改prototype这种做法不好,本人实际使用感觉非常方便,有什么不好的?
例如:
要在所有的react组件或vue组件中,做数字的格式化,直接如下:
String.prototype.toNum = Number.prototype.toNum = function(s) {
}
就能在所有组件中的任何字段,方便的调用toNum方法了,无需每个组件引入方法
曾经看到过修改prototype这种做法不好,本人实际使用感觉非常方便,有什么不好的?
例如:
要在所有的react组件或vue组件中,做数字的格式化,直接如下:
String.prototype.toNum = Number.prototype.toNum = function(s) {
}
就能在所有组件中的任何字段,方便的调用toNum方法了,无需每个组件引入方法
在JavaScript中修改内置对象的原型(如String.prototype
、Number.prototype
等)确实可能带来一些风险,尽管在某些场景下它看起来非常方便。以下是修改原型时可能遇到的一些风险和问题:
someString.toNum()
这样的方法可能会感到困惑,因为他们可能不知道toNum
是哪里来的。toNum
方法(例如,添加新的参数或行为),那么你可能会发现很难在不破坏现有代码的情况下进行这样的扩展。对于你的例子,即希望在所有的React或Vue组件中方便地格式化数字,更好的做法可能是创建一个工具函数或模块,并在需要的地方导入它。例如:
// numFormatter.js
export function formatNumber(num) {
// 你的格式化代码
}
// 在你的组件中
import { formatNumber } from './numFormatter';
// 使用
const formattedNum = formatNumber(someNumber);
这种方法更加清晰、可预测,并且更容易测试和扩展。
自己用当然感觉不错,但是如果需要跟人合作就很麻烦了。现代化软件开发几乎不可能一个人完成,都要大量使用第三方开源仓库。如果每个人都往原型上挂东西,每个人写法都不一样,A 跟 B 冲突,一单用了 A,全部跟 B 相关的都不能用,那就是一场灾难。
现实世界也是如此,每个看起来不太合理的规定背后,可能都是你没遇到过的惨痛经历。
不错的问题啊。其实问题本身前面基本都回答干净了,我就不罗嗦啦,换一个角度聊两句凑个热闹。
在我看来这是个哲学问题,从历史的角度来说,软件这个体系还很年轻,整个发展历程其实和人类史具有极大的相似性,例如函数式编程很像原始社会,面向对象有点像奴隶社会,微服务架构就相当于封建社会了,而去中心化之后就有点现代社会的感觉了。
而prototype来说,更像是早期东西大多不好用,那就改一改,用的顺手就好,后来就不满意了,我为什么要把A改成B(我把xxx优化了一下),我自己直接生产B不就好了(这是我做的),然后就产生了“轮子”问题,然后就催生了“不要重复做轮子”等观点。
所以这其实就是软件在开发思想上的迭代更新而已,正确看待就好。
再举个例子,现代人也会感叹某些老物件比现代的还精巧好用,其实高级语言为主的开发中不也偶尔混入C,汇编等语言提升效率么。
其实大部分的JavaScript库都在大量的使用。比如Vue
。
平常自己用也很好。非要说风险,那就是多人合作时,需要注意一些风险点。
不要去覆盖已有的函数,自定义的尽量把名字取的奇怪一点,避免未知的情况下覆盖了已有的函数,可以考虑加一些前缀。
// 假设这是一个已有的库代码
function doSomethingWithArray(arr) {
for (let item of arr) {
// 基于原始的 Array.prototype.includes 方法进行操作
if (!arr.includes(item * 2)) {
// 执行一些操作
}
}
}
// 其他地方修改了 Array.prototype.includes 方法
Array.prototype.includes = function (value, fromIndex) {
// 自定义的实现,与原始行为不同
return false;
};
let myArray = [1, 2, 3];
doSomethingWithArray(myArray);
// 由于原型修改,这里的行为可能不再符合预期
别人阅读你代码时候,很陌生的函数''.toNum()
,会疑惑干什么用的。尽可能可能把注释写写全问题也不大。告诉人家这个是哪儿定义的做什么用的。
某些情况下,会影响JavaScript引擎优化策略,导致性能下降。特别是添加prototype特别多或者数据量特别大的情况下。
总结一下:
在避开以上问题的情况下,适量使用原型扩展,还是很香的
性能问题:
修改prototype可能会影响性能。当访问一个对象的属性时,JavaScript会沿着原型链查找。频繁地修改prototype可能会导致缓存失效,从而影响性能。
调试困难:
修改prototype可能会使追踪错误变得复杂,因为问题可能源于任意一个使用该构造函数创建的对象。
原型污染:
攻击者可能利用能够修改对象原型的漏洞来注入恶意代码或者篡改对象的行为。如果允许外部输入影响prototype,则可能会影响到所有从该构造函数实例化的对象,甚至可能影响到全局的Object.prototype,从而影响整个应用中的所有对象。
8 回答5.9k 阅读✓ 已解决
9 回答9.3k 阅读
6 回答4.9k 阅读✓ 已解决
5 回答3.6k 阅读✓ 已解决
3 回答10.4k 阅读✓ 已解决
4 回答7.9k 阅读✓ 已解决
7 回答9.9k 阅读
知道为什么JS的String、Array的
包含
方法不像其他语言那样叫,而是叫a.contains()
a.includes()
吗?因为曾经有个大聪明(MooTools
),在String.prototype
上挂了个非标准的contains()
,结果标准委员会只好改名为includes
,避免破坏那些旧的contains()
的行为,导致使用MooTools
的网站崩溃。知道为什么JS的
groupBy
长这个样子:而不是这个样子:
因为当年有个大聪明(
Sugar
),在Array.prototype
上挂了个非标准的groupBy()
,结果标准委员会只好改为静态方法Object.groupBy
,避免破坏那些旧的groupBy()
的行为,导致使用Sugar
的网站崩溃。类似的还有
Array.prototype.flat
和Array.prototype.flatten
之争,不一一列举了。标准委员会每当要给JS添加新功能时,都会考虑这些新功能会不会破坏当前环境的兼容性。如果影响较大,他们就会考虑做出让步。
但是,如果你没那么大影响力,他们就不会考虑对你的兼容性影响。你就要自己承担将来遇到breaking changes的风险了。