属性和方法名称的下划线前缀仅仅是一种约定吗?

Posted

技术标签:

【中文标题】属性和方法名称的下划线前缀仅仅是一种约定吗?【英文标题】:Is the underscore prefix for property and method names merely a convention? 【发布时间】:2011-05-27 22:22:50 【问题描述】:

javascript 中的下划线前缀是否只是一种约定,例如 Python 中的私有类方法是?

来自 2.7 Python 文档:

“私有”实例变量 只能从内部访问 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;
    ...

只有约定?还是下划线前缀后面有更多内容?


我承认我的问题与 this question 非常相似,但它并没有让人更清楚 JavaScript 中下划线前缀的重要性。

【问题讨论】:

另见***.com/questions/17359885/… 【参考方案1】:

欢迎来到 2019 年!

似乎proposal 扩展类语法以允许# 前缀变量是私有的。 Chrome 74 ships 支持。

_ 前缀变量名按照惯例被认为是私有的,但仍然是公共的。

这种语法试图既简洁又直观,尽管它与其他编程语言有很大不同。

为什么在所有 Unicode 代码点中选择了印记 #?

@ 是最初的最爱,但后来被装饰者采用。 TC39 曾考虑交换装饰器和私有状态标志,但委员会决定遵从转译器用户的现有用法。 _ 会导致与现有 JavaScript 代码的兼容性问题,这使得 _ 长期出现在标识符或(公共)属性名称的开头。

该提案于 2017 年 7 月进入第 3 阶段。从那时起,就各种替代方案进行了广泛的思考和长时间的讨论。 最后,这一思考过程和持续的社区参与导致对该存储库中的提案重新达成共识。基于该共识,该提案的实施正在向前推进。

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

【讨论】:

【参考方案2】:

JavaScript 实际上确实支持封装,通过涉及hiding members in closures (Crockford) 的方法。也就是说,它有时很麻烦,并且下划线约定是一个很好的约定,可用于一些私有的东西,但实际上您需要隐藏。

【讨论】:

赞成澄清如何实现闭包,反对说下划线是好的惯例。所以我不会投票:) 在闭包中隐藏成员有时会妨碍可测试性。看看这篇文章:adequatelygood.com/2010/7/Writing-Testable-JavaScript @Jason - 只是好奇,为什么你认为下划线是一个不好的约定? @TamasPap - 有几个原因,但只是我的选择:1)迫使 JS 成为其他语言风格的拐杖 2)如果它可以访问,它将被使用。下划线会乱扔乱码并使外部代码复杂化。 3) 让新的 JS 程序员感到困惑。 即使使用闭包,技术上仍然可以访问所谓的“私有”变量。 _convention 至少让开发人员知道这样做需要自己承担风险(或类似的事情)。【参考方案3】:

“只有约定?还是下划线前缀后面有更多内容?”

除了隐私约定之外,我还想帮助人们意识到下划线前缀也用于依赖于独立参数的参数,特别是在 URI 锚映射中。从属键始终指向映射。

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

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

浏览器搜索字段上的 URI 锚点更改为:

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

这是用于根据哈希更改驱动应用程序状态的约定。

【讨论】:

【参考方案4】:

import/export 现在正在使用 ES6 完成这项工作。如果我的大部分函数都是导出的,我仍然倾向于在未导出的函数前面加上 _

如果您只导出一个类(如在 Angular 项目中),则根本不需要它。

export class MyOpenClass

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

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



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

【讨论】:

我不认为 import/export 以任何方式提供对私有类方法的支持。我的意思是,它确实支持类级别的类似功能,但它不提供隐藏包含的方法。 (即所有包含的方法总是公开的) 你导出类,内部函数调用外部函数。这些函数是私有的。【参考方案5】:

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

【讨论】:

【参考方案6】:

这只是一个约定。 Javascript 语言对以下划线字符开头的标识符没有任何特殊含义。

也就是说,对于不支持开箱即用 encapsulation 的语言来说,这是一个非常有用的约定。虽然没有办法防止有人滥用您的类的实现,但至少它确实阐明了您的意图,并首先记录了诸如错误之类的行为。

【讨论】:

是的。即使该语言不“支持”它,它也是一个非常方便的约定。 严重问题。 jsfiddle.net/VmFSR 如您所见,创建值的名称只能通过使用 _ 创建的新值前缀来访问,我很想知道发生了什么!?为什么不是this.name @Muhammad Umer,我不确定我是否理解您的评论。 console.log(someone._name = "Jean Dupont");console.log(someone.name); 一样有效,它既分配又评估属性后面带下划线前缀的成员。 As you can see,下划线不能保证封装:) 默认情况下,Visual Studio 会尽力帮助您尊重这一点。 javascript IntelliSense 引擎在使用“this”变量时从对象内部向您显示“私有”属性。但是,当从外部调用时,它会隐藏所有带下划线的属性。 @Karuhanga 他早在 2010 年就回答了这个问题——当然,10 年后情况发生了变化

以上是关于属性和方法名称的下划线前缀仅仅是一种约定吗?的主要内容,如果未能解决你的问题,请参考以下文章

python中名称修饰与描述符

Objective C综合变量下划线前缀不起作用

Python-4

面向对象——封装

面向对象三大特性之封装与多态

封装