Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs: Update JavaScript guidelines to reflect current best practices #9037

Merged
merged 29 commits into from
Nov 9, 2016

Conversation

aduth
Copy link
Contributor

@aduth aduth commented Oct 31, 2016

This pull request seeks to update our JavaScript guidelines to reflect consistent best practices throughout.

Much of this should be non-controversial:

  • Updating var to let/const
  • Updating React.createClass to React.Component
  • Accounting for server-side rendering in window global usage
  • Adding links to external resources
  • Document common gotchas and lint-enforced guidelines such as 'valueOf' in object, early function returns, typeof object === 'object

Other changes may warrant debate:

  • Discourage undesirable behaviors such as disabling ESLint rules and complex ternary expressions by removing them from the guidelines
  • Defer to external resources for ES2015 usage, as our own examples are likely to fall out of date quickly, and we should encourage using all ES2015 features, not just those we document

@aduth aduth added Documentation [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Oct 31, 2016
@matticbot
Copy link
Contributor


## Iteration

The functional programming inspired methods that were added in ECMA5 improve readability. Use them throughout.

```js
var posts = postList.map( function( post ) {
const posts = postList.map( function( post ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use an arrow function here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe use an arrow function here?

Done in d7b075a

when: 4, 'you are': 15 };

map = { ready: 9,
when: 4, 'you are': 15 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two ideas:

  • use const-first thinking as an example instead
  • not name this map in order to prevent confusion from Array.prototype.map

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

two ideas:

Yeah, I went back-and-forth a few times with the const. I agree we should promote it, but I didn't want to confuse by having const for the same variable name multiple times in the same example block. I might see to using different variable names, or just creating separate code blocks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of these are good vs bad, so I think it's reasonable to assume when reading that we're comparing two things that don't exist together

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use const-first thinking as an example instead
not name this map in order to prevent confusion from Array.prototype.map

Improved examples, including separate code blocks with const instead of let, in ffedbac.

}

// Unlike jQuery, WordPress prefers a space after the ! negation operator.
// This is also done to conform to our PHP standards.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for removing this!

$( '.container' ).val( array[ i ] );
for ( let i = 0; i < 100; i++ ) {
object[ array[ i ] ] = someFn( i );
$( '.container' ).val( array[ i ] );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my. I hope to never find this actual code in a review

Copy link
Contributor Author

@aduth aduth Oct 31, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh my. I hope to never find this actual code in a review

Removed this specific line in 4d171dd . This could also be improved to a reduce or forEach, but I think the point is to demonstrate for spacing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I got the point of it, but was just a bit irked that we used such a nasty example. we could have used a much simpler loop body

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I got the point of it, but was just a bit irked that we used such a nasty example. we could have used a much simpler loop body

I'm going to leave this as-is, but d7b075a includes an emphasis on ES5+ Array methods for iteration.

@@ -125,54 +123,105 @@ Indentation and line breaks add readability to complex statements.

Tabs should be used for indentation.

Try to return early from a function to avoid deeply tabbed functions akin to ["Callback Hell"](http://callbackhell.com/).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe deep indentation would be more appropriate than tabbed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe deep indentation would be more appropriate than tabbed

Updated in e3b41c4.

@dmsnell
Copy link
Member

dmsnell commented Oct 31, 2016

thanks for taking the time to update these @aduth. overall I think it would be great to place more emphasis on const and less on for loops. I'm worried that even in places where we're not using those as good examples, their presence will encourage their use. we have plenty of examples of for loops in there and appropriate questions on their style, but do we need more indicating when array operations would be better suited? we could add in a few good/bad examples of using loops vs. map, filter, forEach.

@aduth
Copy link
Contributor Author

aduth commented Oct 31, 2016

@dmsnell : We have a short section "Iteration" that we could expand to cover more use-cases of map, filter, forEach, reduce, and perhaps pointers to a few more Lodash methods.

@aduth
Copy link
Contributor Author

aduth commented Nov 1, 2016

Improved "Iteration" section with an emphasis on ES5+ Array methods in d7b075a.

map = { ready: 9,
when: 4, 'you are': 15 };
const labels = { facebook: 'Facebook',
twitter: 'Twitter', 'google-plus': 'Google Plus' };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, I almost made a comment like, "what are you thinking?" and then I noticed this is the "bad" example 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

haha, I almost made a comment like, "what are you thinking?" and then I noticed this is the "bad" example 😉

Yeah, I rearranged all of the examples to show "Bad" first.

```js
// After
const sumLabel = `The sum of ${ a } and ${ b } plus ${ c } is ${ a + b + c }`;
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w00t! I ❤️ this!

Copy link
Contributor Author

@aduth aduth Nov 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

w00t! I ❤️ this!

Thanks! As I was changing the original in "Multi-line Statements" to use const, I felt depressed that we'd concatenate a string like that, so I thought it was a nice progression to see how the same example can be improved further using template literals.

@@ -420,6 +402,18 @@ const myStr = "You're amazing just the way you are.";
const component = <div className="post"></div>;
```

[ES2015 template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) are available as an alternative to string concatenation when including variables in a string.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is this obscure usage of tag functions with template literals. Do we want to acknowledge them and make some styling decisions?

function tag(strings, ...values) {
  return {
    blog: values[0],
    user: values[1]
  }
}

const s = tag`Make blog ${ 'catbacon' } with user ${ 'Porkat' }`;

// s === {
//   blog: 'cat bacon',
//   user: 'Porkat'
// }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is this obscure usage of tag functions with template literals.

I personally feel we should avoid advocating their usage. That is... unless we ever adopt Relay 😉 (reference)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't agree more (about not using them)


- [Rest](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters) at MDN
- Use [stateless function components or the `React.Component` class](https://facebook.github.io/react/docs/components-and-props.html#functional-and-class-components) instead of `React.createClass`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we do about mixins? Are we encouraged to replace them by HoC when we think It's appropriate?
For example, LinkedStateMixin and dirtyLinkedState can be replaced nicely with a HoC, I think.

What's our current approach with that? Maybe we could add a note about mixins on this doc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely want to avoid all mixins, and LinkedStateMixin has been on the black list for a long time. We have various ways to remove them: higher order components for sure, but also for some things local component state or proper use of our Redux state/query components

## Strings

Use single-quotes for string literals:

```js
var myStr = 'strings should be contained in single quotes';
const myStr = 'strings should be contained in single quotes';
```

When a string contains single quotes, they need to be escaped with a backslash (\\):

Double quotes can be used in cases where there is a single quote in the string or in JSX attributes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we not have a style preference one way or another for that? I personally think the latter is much more readable.

'The Cat\'s Hat'

vs

"The Cat's Hat"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally agree with your preference. If there's enough in agreement, I'd be okay with removing the allowed double-quote exception.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm confused here... I read @gwwar's comment as preferring "The Cat's Hat"...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@blowery : Oh, you're right, I misread it. In which case then... I disagree 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely prefer "The Cat's Hat" over 'The Cat\'s Hat' and I also prefer (backtick)The Cat's Hat(backtick) over it too.

Why escape things when we don't have too? Just gets in the way

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm with @gwwar

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why escape things when we don't have too?

Consistency for one. Also, what happens when the string is updated and no longer needs escaping? Do we change the " back to '? This seems easy to overlook and is unnecessary effort.

Copy link
Member

@dmsnell dmsnell Nov 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency seems subjective here. What are we being consistent about: quote types or escaping quotes?

If we're consistent about quote types then we're basically requiring that we never use double-quotes, except for with JSX.

If we're consistent about escaping, we're saying never escape quotes (except maybe the back-tick inside of template literals).

In this case I'm willing to sacrifice a small consistency for the sake of providing normal-reading sentences.

@gwwar
Copy link
Contributor

gwwar commented Nov 2, 2016

👍 Thanks for updating this @aduth !

// Bad
const sumLabel = 'The sum of ' + a + ' and ' + b + ' plus ' + c
+ ' is ' + ( a + b + c );
```js
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a heads up, looks like a couple of extraneous '```js's got in - causes the output to look like:

// Bad
const sumLabel = 'The sum of ' + a + ' and ' + b + ' plus ' + c
    + ' is ' + ( a + b + c );
```js
```js
// Good
const sumLabel = 'The sum of ' + a + ' and ' + b + ' plus ' + c +
    ' is ' + ( a + b + c );

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 good catch!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch indeed, fixed in cb8cf91

@gwwar
Copy link
Contributor

gwwar commented Nov 8, 2016

Unless folks have found any blockers, I think this is a great improvement, and we should :shipit:

@aduth
Copy link
Contributor Author

aduth commented Nov 9, 2016

Unless folks have found any blockers, I think this is a great improvement, and we should :shipit:

I'm sure there's still room for improvement, but we can always revisit it in a future pull request. Hopefully nothing changed here was too controversial.

Seems a few ideas to consider for a future pull request are:

  • Double-quoted strings when containing apostrophe
  • Multi-line conditions indentation
  • Reevaluate == null strict equality exception (my own desire)

In the meantime, I'm going to squash and merge this one.

@aduth aduth added [Status] Ready to Merge and removed [Status] Needs Review The PR is ready for review. This also triggers e2e canary tests and wp-desktop tests automatically. labels Nov 9, 2016
@aduth aduth merged commit 1de954c into master Nov 9, 2016
@aduth aduth deleted the update/js-docs branch November 9, 2016 15:03
bisko pushed a commit that referenced this pull request Nov 16, 2016
…9037)

* Docs: Update var to let/const in JS guidelines

* Docs: Remove inline comments on parameter list

Prefer JSDoc documentation

* Docs: Add caveat about typeof object

* Docs: Update truthy link to ES2015 specification

Slightly updated since ES5.1

* Docs: Add explanation for boolean return value function naming

* Docs: Fix object key spacing per guidelines

Exception removed in #5829

* Docs: Add link for Yoda conditions reference

* Docs: Improve React component best practices

* Docs: Remove ES6 usage examples

Instead defer to external resources

* Docs: Add reference links for ESLint configuration

Discourage disabling rules, because you’re probably wrong if you’re
disabling a rule

* Docs: Add note about EditorConfig

* Docs: Remove jQuery clarification

Specific to WordPress?

* Docs: Add early return example

* Docs: Add else return example

* Docs: Remove complex ternary example

Perhaps controversially, but I argue that complex ternaries are
difficult to read. While I did not include any guidelines _against_
using complex ternaries, I similarly don’t think it’s advisable that we
include recommendations for how to use them.

* Docs: Add window typeof check tip for SSR

* Docs: Add caveat about object "in" operator

* Docs: Improve clarity of `else` `return` guideline

* Docs: Use tabs consistently in JS docs

* Docs: Promote discussing lint rules

* Docs: Use consistent capitalization in comments

* Docs: Deeply tabbed -> deep indentation

* Docs: Remove jQuery specific spacing example

* Docs: Use arrow functions where applicable

* Docs: Add link for automatic semicolon insertion

* Docs: Improve examples naming, logic, ordering

* Docs: Emphasize ES5+ Array methods for iteration

* Docs: Include template literal example

* Docs: Fix closing code block syntax
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants