下划线前缀在属性和方法名称中仅是一种惯例吗?

289

在 JavaScript 中,下划线前缀只是一种约定,就像在 Python 中私有类方法一样吗?

来自 Python 2.7 文档:

Python 中不存在除了对象内部无法访问的“私有”实例变量。但是,大多数 Python 代码都遵循以下约定:以下划线为前缀的名称(例如 _spam)应被视为 API 的非公共部分(无论它是函数、方法还是数据成员)。

这个约定适用于 JavaScript 吗?

比如这段 JavaScript 代码:

function AltTabPopup() {
    this._init();
}

AltTabPopup.prototype = {
    _init : function() {
        ...
    }
}

此外,还使用了下划线前缀变量。

    ...
    this._currentApp = 0;
    this._currentWindow = -1;
    this._thumbnailTimeoutId = 0;
    this._motionTimeoutId = 0;
    ...

只是惯例吗?还是下划线前缀背后还有更多东西?


我承认我的问题与这个问题非常相似,但它并没有让人更加了解JavaScript中下划线前缀的重要性。


请参见https://dev59.com/pmQm5IYBdhLWcg3wwxPF。 - Jon Onstott
6个回答

290

这只是一种约定俗成的做法。Javascript语言并未赋予以下划线字符开头的标识符任何特殊意义。

话虽如此,对于一门不支持原生封装的语言来说,这是一种相当有用的惯例。虽然无法阻止他人滥用你类的实现,至少它能够澄清你的意图,并将此类行为记录为错误


6
好的,即使语言本身不“支持”,这仍然是一个非常方便的约定。 - Juho Vepsäläinen
1
@Muhammad Umer,我不确定我理解你的评论。console.log(someone._name = "Jean Dupont");console.log(someone.name);一样有效,它们都分配并评估属性后面的下划线前缀成员。正如您可以看到的那样,在下划线中没有保证的封装 :) 请参见 - Frédéric Hamidi
哇,我完全错过了仅仅分配一个值就足以声明某些东西的部分。我的错。谢谢你指出我快疯了。:D - Muhammad Umer
5
默认情况下,Visual Studio会尝试帮助您遵守这一点。当使用“this”变量时,Javascript智能感知引擎会向您显示对象内部的“private”属性。但是,当从外部调用时,它会隐藏所有带下划线的属性。 - foxontherock
1
@Karuhanga他在2010年就回答了这个问题-当然,10年后情况已经发生了变化。 - Kenny Meyer
显示剩余2条评论

113
JavaScript实际上支持封装,通过一种方法在闭包中隐藏成员(Crockford)。尽管如此,有时候这种方法很麻烦,使用下划线约定来处理那些有点私有但实际上不需要隐藏的内容是一个相当好的约定。

27
对于解释如何实现闭包的,点赞;对于说下划线是良好约定的,点踩;因此我不会投票:) - Jason
6
@Jason - 只是好奇,为什么您认为下划线是一种不好的约定? - Tamás Pap
6
@TamasPap - 有几个原因,但这只是我的看法:1)将JS强制转换为其他语言的风格的支架;2)如果它是可访问的,它将被使用。下划线可能会使外部代码混乱不堪。3)对新的JS程序员来说很容易混淆。 - Jason
11
即使关闭了,仍然技术上可以访问所谓的“私有”变量。至少该约定提醒开发人员自担风险(或类似的事情)。 - Kabir Sarin
3
像这样的黑客攻击,例如 https://gist.github.com/sarink/7394867 ,总是有可能发生。当然,在一个框架中你可以采取防范措施,但是如果有人能看到源代码,他们肯定能够想出一种方法。 - Kabir Sarin
显示剩余8条评论

70

欢迎来到2019年!

看起来,一项允许使用#前缀变量作为私有变量的提案已被接受。Chrome 74 已支持此功能。

_前缀命名的变量名称通常被认为是私有的,但仍然是公共的。

这种语法既简洁又直观,尽管它与其他编程语言非常不同。

在所有Unicode代码点中,为什么选择#符号?

  • @一开始很受欢迎,但被装饰器占用了。TC39考虑交换装饰器和私有状态标识符,但委员会决定遵循转码器用户的现有用法。
  • _会导致与现有JavaScript代码的兼容性问题,因为_长时间以来一直允许在标识符或(公共)属性名称的开头使用_。

此提案于2017年7月达到第3阶段。自那时以来,对各种替代方案进行了广泛的思考和长时间的讨论。最终,这个思考过程和持续的社区参与,在该存储库中重新达成了关于该提案的共识。基于这个共识,实现正在推进该提案。

请参见https://caniuse.com/#feat=mdn-javascript_classes_private_class_fields


16

JSDoc 3允许您使用@access private(以前是@private标签)为函数添加注释,这也有助于向其他开发人员广播您的意图- http://usejsdoc.org/tags-access.html


12

仅仅是约定吗?或者下划线前缀背后还有更多的意义?

除了隐私约定之外,我还想帮助提高人们对下划线前缀在URI锚点映射中用于依赖于独立参数的参数的意识。依赖键总是指向一个映射。

例如(来自https://github.com/mmikowski/urianchor):

$.uriAnchor.setAnchor({
  page   : 'profile',
  _page  : {
    uname   : 'wendy',
    online  : 'today'
  }
});

浏览器搜索栏中的 URI 锚点已更改为:

\#!page=profile:uname,wendy|online,today

这是一种根据哈希变化驱动应用程序状态的约定。


8
import/export现在已经成为了ES6中的标准操作。如果大部分函数都被导出,我还是倾向于在未被导出的函数前加上前缀_
如果只导出一个类(如在angular项目中),那么根本不需要这样做。
export class MyOpenClass{

    open(){
         doStuff()
         this._privateStuff()
         return close();
    }

    _privateStuff() { /* _ only as a convention */} 

}

function close(){ /*... this is really private... */ }

我认为导入/导出不以任何方式支持私有类方法。我的意思是,它确实支持类级别的类似功能,但它不提供隐藏包含的方法。(即所有包含的方法始终是公共的) - bvdb
1
你可以导出这个类,以及函数外部调用内部函数。这些函数是私有的。 - Nicolas Zozol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接