如果我想向 Function 对象添加通用命名的属性,我是否应该担心与未来可能发生的 ECMAScript 更改发生冲突?

Nor*_*mal 4 javascript

我正在尝试创建一个可以通过以下方式调用的函数:

log() 
log.error()
Run Code Online (Sandbox Code Playgroud)

根据我的理解,我们可以通过在函数本身上编写来创建这样一个函数:

log() 
log.error()
Run Code Online (Sandbox Code Playgroud)

不建议这样写函数吗?因为 JavaScript 将来可能会发布一个可能包含“错误”属性的更新,所以我的“错误”属性将覆盖该 JavaScript 功能。


下面是一个示例,说明人们可能会在何处使用此模式以及它为何有用。

例如,Jquery:

function log(){

}
log.error = () => {}
Run Code Online (Sandbox Code Playgroud)

$( "#button-container button" ).on( "click", function( event ) {} )
Run Code Online (Sandbox Code Playgroud)

您在许多流行的图书馆中都会看到这种模式。

$一个对象,

它是可调用的,所以你可以调用它$('css query selector here') ,它是可链接的,所以你可以调用它的其他方法$().on(),并且它有方法$.ajax()

sta*_*all 5

这种想法本意是好的,但没有规模化。举个例子

  • Object.prototype。如果我们在任何地方都应用这种思路,那么我们就必须将其应用到Object.prototype. 默认情况下,每个对象都有Object.prototype其原型链,除非您Object.create(null, {...}). 人们是否每天都担心他们在对象上设置的每个具有通用名称的属性有一天会覆盖未来的标准属性?从来没听说过。

  • 或者一些不那么戏剧化但更现实的东西:静态属性。JS 中的类基本上是构造函数和原型的语法糖。当您使用静态属性时,您是在函数对象上定义某些内容 - 类的构造函数。每当您这样做时,您是否还会担心名称冲突?(可能不会)

您担心自己的选择与未来对标准的更改发生冲突,但您也应该考虑相反的方式:ECMAScript 标准委员会不想做出破坏网络的更改(网络上所有使用JavaScript 在其存在的过去多少年里一直在做出命名决定)。去谷歌搜索“JavaScript smoosh”并阅读有关array.prototype.flatten惨败的信息。

TC39ECMA-International 的 ECMAScript 标准委员会)中的一个常见短语/规则是“不要破坏网络”

如果JavaScript 的更改保留了现有网站的当前行为,则该更改被视为“网络兼容”。如果它改变了现有网站的行为,则被视为“破坏网络”。

这里的定义有点模糊和经验主义——构建一个在 JavaScript 的任何特定更改或添加下都会崩溃的网站总是有可能的,关键是损坏的网站有多常见。如果太多网站崩溃,那么网络浏览器将拒绝发布更改。

“不要破坏网络”是TC39和所有网络标准机构的共同目标:我们的目标是在发展语言的同时保持网络兼容性。即使更改对开发人员来说很方便,但如果我们在此过程中伤害了很多用户,那就不值得了!

例子

我们努力添加一种方法Array.prototype.contains。然而,这破坏了许多网站(参考)。因此,该方法被命名为Array.prototype.includes

最后,你做什么的决定取决于你。因为“不要破坏网络”,我认为总的来说,当你选择做这样的事情时,你可以更轻松地休息。

我不会将这种思维方式概括为:标准类的原型。如果您只是向原型链最子端的对象实例Function(例如 的实例)添加属性,而不是向标准类的原型(例如实例的Function原型)添加属性,那么这没什么大不了的,但是当谈到向标准原型添加东西时,我个人不会一直这样做——只有当我看到它会显着提高我的代码库的很大一部分的可读性时。

自卫技术

  • 遵循 ECMAScript 标准的提案,看看是否计划或正在讨论此类引发冲突的更改,

  • 进行运行时检查以查看Function原型链error中是否包含属性,如果是,则在控制台中发出警告消息(但不要抛出异常 - 那么你的网站将崩溃!只需记录警告并继续前进) 。并制定一个计划,以便将来发生此类冲突时该怎么办。

  • 选择一种替代设计,在您的理想和您认为可能避免未来问题的设计之间做出妥协,例如选择不太可能与 ECMAScript 标准的新增内容发生冲突的命名方案。

  • 如果您实际上只关心防御而不关心人体工程学,您可以采取不同的方法,例如根本不使用属性方法函数(而是使用像“ function myError(func) {/*...*/}”这样的自由函数),或者使用符号代替。

杂项

由于您对 JQuery 正在做什么表示好奇,根据您在问题帖子中的内容:typeof $Gives "function"Object.getPrototypeOf($).constructor === Functionistruetypeof $.ajaxGives "function"。据我所知,您所提出的建议是目前实现您在问题帖子中所描述的目标的唯一方法。

任何时候你想要摆弄一些与改变对象或对象类相对于“较低级别”JS 行为的行为方式有关的东西时,你的第一直觉应该是看看是否有一个标准Symbol属性,例如如Symbol.toPrimitive、 、Symbol.iterator、 和Symbol.species。我不知道存在这样一个符号属性,可以将任何对象转换为可调用对象。即,要使某些东西可调用,您当前在其原型链中一个。Function

回答您在评论中提出的后续问题:

我们可以考虑使用类来实现相同的目的吗?或者您认为它们对于这种情况毫无用处?

这听起来像是您提出了更具体的 Symbol 属性案例(在类(在其原型中)而不是任何对象上)。因此,推而广之,答案是否定的,我认为它们在这里没有用。