From the main section:
Use a leading underscore
_when naming private properties.
From the React section:
Do not use underscore prefix for internal methods of a React component.
These two seem a bit inconsistent.
A bit. The thing about React components is that all methods should be private so we just save ourselves the typing and don't insert the leading underscore.
What do you mean? If I have e.g. a ref to an element, I can call methods on it just fine. I've seen this used to implement e.g. getValue methods on things like complex, uncontrolled input components for forms.
If you consider this an anti-pattern, it could be useful to add a clarifying comment to say to not do this. There are definitely problems with this pattern - it's not container-safe, for one.
I am also interested in an explanation to that. Not all methods are "internal" if we consider that we may call a component's method from the parent.
Example:
parent.jsx
_handleOpenChildDialog() {
this.refs.childComponent.showDialog();
}
childComponent.jsx
showDialog() {
this.refs.myDialog.show();
}
So, what is our general take on this? Do we still write all methods without any underscore?
:+1: Facebook, for example, use this pattern extensively: https://github.com/facebook/relay/blob/v0.4.0/src/container/RelayRootContainer.js
@taion what about v0.14
What about it?
Personally, I think this pattern is horrifically bad. Things that are not private are not private - sticking an underscore in front of them doesn't make them private, it just lulls you into the false belief that you can make a breaking change without bumping a major version.
It's not private if it's accessible in the runtime. If it's private - ie, via closure or WeakMap - no convention or unit tests are needed. If it's not, it's public, and you _must_ test it, and whether you document it or not, _it is public_ and breaking it counts as a breaking change. At that point, using a leading underscore is just fluff, so why would you need a convention for it?
@ljharb
That's totally fine - I personally don't mind writing my code either way (as long as I can point to a style guide and say that I'm following it).
The issue is that this style guide appears to be inconsistent between https://github.com/airbnb/javascript#22.4 and https://github.com/airbnb/javascript/tree/master/react#methods. I don't see how it's possible to follow both.
React is a special subset - you could follow both by using _-prefixes in non-React code. I don't see a logical disconnect there.
Okay. If that's intentional, I can live with it.
JS doesn't have privacy. Underscore-prefixed things are just as public as anything else.
We should probably just remove this section from the main style guide or rephrase it. Same with the React style guide. This whole attachment to "private" is what makes the rules seem confusing.
Either way would be fine – it's the inconsistency that's galling.
Though I'd note again that FB do in fact use leading underscores, real privacy or no 😛
I'm late to this discussion, but if I drop the underscore, how will my colleagues know to avoid _private_ methods that will change at any point. The absence of underscore indicates any change will be documented with a semver version bump.
PS: I export my _private_ methods so they may be unit tested.
If it's private, it should not be directly tested, nor accessible - only the public API should be tested. If it's exposed, for any reason, it's simply not private anymore, and changing it in a breaking way is simply a semver-major change.
In principle, yes, but underscores are a widely understood indicator that a variable is "private". You don't walk through a door with a sock on the handle, even if it's unlocked.
Since there are no private properties in ES6 classes, how can I indicate "private" methods and properties? This is especially problematic with getters/setters in ES6, since it needs to be stored in a separate variable.
// Simplified example
export class User {
get address() { return this._address; }
set address(address) { this._address = address; }
}
@nicbou "widely understood indicator" isn't the same thing as "language definition". The answer to your question is that there _are_ no private methods and properties, so you _can't_ indicate them - you can't actually _have_ them. This is a limitation slash feature of JS, and the proper solution is usually to architect around it rather than to work around it. (In that example, I'd use this.address and this.getAddress() and this.setAddress() - our style guide explicitly discourages using ES5 getters and setters)
@ljharb
This might just be a limitation in understanding. Suppose I have this.setAddress() and this.getAddress(), and this.setAddress() performs additional validation (or something).
I think the concern is that if I store the address field on this.address, it's relatively easy for consumers of this object to accidentally do this.address = foo rather than this.setAddress(foo).
While I can e.g. use a WeakMap to get an actual private field, maybe I don't need that, and just want a code-completion-friendly way to suggest to users that they should use this.setAddress() instead of this.address.
How do you deal with such cases? Doc comments are fine, but they have the downside that you can't see them unless you actually look at the code, while property names are visible given just an object.
More briefly, taking your example, how do you indicate that users should _probably_ do obj.setAddress(foo) rather than obj.address = foo?
@taion Given that a WeakMap-per-field is the only way to get true per-instance privacy in JS, then I'd think just _not_ defining "this.address" should work fine with IDE code completion - if not, they should be looking at the docs or the "class" implementation to see what options are available. setAddress is a function property on the object just like address would be.
I'm saying something a little different, I think – having this.setAddress() and this._address communicates that this.setAddress() is the preferred API, and that it would be unusual to set this._address directly, versus the WeakMap approach that gives actual privacy but is just more code.
The goal isn't to prevent determined users from accessing e.g. this._address; more to suggest that they prefer this.setAddress().
@taion if your goal is helpful suggestions, then name it this.private_address_do_not_use, or SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED. Naming it _address is just obscure.
I don't think it's especially obscure, though – it's a common convention, not just in e.g. Facebook code but also from earlier versions of this guide: https://github.com/airbnb/javascript/tree/eslint-config-airbnb-v1.0.0#22.4.
@ljharb, I agree that this is not a language definition, but I disagree that we should architect around it when it's clearly a language limitation. Calling a private method SECRET_METHOD_DO_NOT_USE_OR_YOU_WILL_BE_FIRED does not make the variable any more private, nor does it prevent people from using them.
In this scenario, there is no perfect solution, only different hacks for JavaScript's limitations, so arbitrarily preventing one commonly used workaround seems odd to me.
As for underscore-prefixed variables being obscure, I completely disagree. If it were, you wouldn't set a rule for it, Mozilla wouldn't mention it, and we would not be having this discussion.
I think it's problematic _because_ it's a common convention - because many people see it and think "private", and it is anything but. Accessible === public === changes to it affect semver. If you are personally confident that you, and all future contributors, will respect this responsibility on a given project, then you are of course most welcome to disable linter rules and/or ignore sections of the guide - but I think it's important that the default, and the general advice, errs on the side of safety and not false promises of privacy.
👍 on underscores, while stuff may not actually be private that seems irrelevant, the point of privacy in programming is isolation, encapsulation for _humans_. Insofar as the convention is understood by humans then who cares whether its "really" private or not. The value of the underscore is in signally to users of your code what is and is not the public API. In my experience despite the potential for folks not following the convention, almost no one uses an underscored method or property thinking thats its something that is part of the supported public api of the package
@jquense node core has, and npm broke them accidentally. This is a HUGE problem in practice.
This is a HUGE problem in practice.
well agree to disagree then on that. In practice it's not been a huge problem for anything i've been involved in. Signally is helpful, and imo better than the alternative of accidentally signally that everything is part of your public api, when you support in browsers that don't have a WeakMap, or the Object model doesn't easily allow closures for instance properties.
@ljharb, that's great, but since there is no readily accessible solution to have private methods/properties in ES6 classes, what would you suggest? Without a usable alternative to underscore-prefixed classes, there really isn't a reason to dismiss them.
It seems to me that this rule takes the standard from "best practices" to "high-minded ideals". So far, all of the proposed changes have been about improving coding practices, rather than forcing awkward workarounds for language limitations.
In addition, I forgot to mention JSDocs, which has a @private attribute as an extra layer of don't-do-this-you-dunce for whoever might want to access private attributes.
As a compromise, couldn't we raise an error for accessing _variables from another class?
My suggestion continues to be, design things so you don't _need_ privacy. Comments, JSDoc or otherwise, aren't enforcement any more than an underscore.
In JS, it is a bad coding practice to design something that runs into language limitations - in other words, you need to work within the constraints of the language.
This rule will not be changing. You're welcome to fork the guide, and/or overwrite linter rules, to fit what makes sense for your project.
Yes, and I am asking "how?", because there clearly isn't a way to do so with ES6 classes.
This isn't the proper venue for it, but if you ping me in the gitter channel, I'll be happy to show you how it's easily possible for your example.
Sure thing! Let's talk about it Monday. The weekend is too short for arguments about code ;)
@ljharb Your points are very valid and I guess the Javascript Ecosystem has a history of "working around the language" read CoffeeScript, TypeScript, React & JSX etc.
But you must agree the leading (and trailing) underscore is pretty much prevalent in the Industry.
And there are some more heavyweights too apart from the above.
Your point about working with the language is well taken. I personally feel though that this style guide could be better off taking a solid consistent stand throughout.
Eg with https://github.com/airbnb/javascript#accessors--no-getters-setters It seems we are encouraging people to go "around" the language and use custom setters like setProperty which is _code convention based_ and not language based (getters & setters).
I may be wrong but this seems to be the same issue as the leading underscore/
@gaurav21r I believe Polymer is from Google, so your three examples are really just two. Also, someone being a "heavyweight" is a poor reason to make any choice.
This style guide does take a solid and consistent stand: do not use ES5 getters/setters; and do not use underscores at all (trailing or leading), nor anything else that attempts to convey soft "privacy". The reasons are hopefully communicated well. If they can be improved, PRs are most welcome!
Just because a language provides functionality does not mean it is the best or most appropriate choice to achieve a goal, nor that using an alternative is "going around the language". You can do lots of things with eval or with, but that doesn't mean it's ever a good idea.
Most helpful comment
In principle, yes, but underscores are a widely understood indicator that a variable is "private". You don't walk through a door with a sock on the handle, even if it's unlocked.
Since there are no private properties in ES6 classes, how can I indicate "private" methods and properties? This is especially problematic with getters/setters in ES6, since it needs to be stored in a separate variable.