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
Merged
Changes from 5 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
182864f
Docs: Update var to let/const in JS guidelines
aduth Oct 31, 2016
7ebc01e
Docs: Remove inline comments on parameter list
aduth Oct 31, 2016
ab72e88
Docs: Add caveat about typeof object
aduth Oct 31, 2016
8f09a83
Docs: Update truthy link to ES2015 specification
aduth Oct 31, 2016
8afd673
Docs: Add explanation for boolean return value function naming
aduth Oct 31, 2016
f258519
Docs: Fix object key spacing per guidelines
aduth Oct 31, 2016
6ec38c6
Docs: Add link for Yoda conditions reference
aduth Oct 31, 2016
60a0f12
Docs: Improve React component best practices
aduth Oct 31, 2016
e80800a
Docs: Remove ES6 usage examples
aduth Oct 31, 2016
4b26ff5
Docs: Add reference links for ESLint configuration
aduth Oct 31, 2016
38f85ea
Docs: Add note about EditorConfig
aduth Oct 31, 2016
1dc7fcd
Docs: Remove jQuery clarification
aduth Oct 31, 2016
544aac6
Docs: Add early return example
aduth Oct 31, 2016
be210dd
Docs: Add else return example
aduth Oct 31, 2016
e60fa63
Docs: Remove complex ternary example
aduth Oct 31, 2016
8874a07
Docs: Add window typeof check tip for SSR
aduth Oct 31, 2016
519dfa4
Docs: Add caveat about object "in" operator
aduth Oct 31, 2016
04bd011
Docs: Improve clarity of `else` `return` guideline
aduth Oct 31, 2016
3dfa55e
Docs: Use tabs consistently in JS docs
aduth Oct 31, 2016
8eaf5b4
Docs: Promote discussing lint rules
aduth Oct 31, 2016
818cdf8
Docs: Use consistent capitalization in comments
aduth Oct 31, 2016
e3b41c4
Docs: Deeply tabbed -> deep indentation
aduth Oct 31, 2016
4d171dd
Docs: Remove jQuery specific spacing example
aduth Oct 31, 2016
30f204b
Docs: Use arrow functions where applicable
aduth Nov 1, 2016
a50cb57
Docs: Add link for automatic semicolon insertion
aduth Nov 1, 2016
ffedbac
Docs: Improve examples naming, logic, ordering
aduth Nov 1, 2016
d7b075a
Docs: Emphasize ES5+ Array methods for iteration
aduth Nov 1, 2016
801d151
Docs: Include template literal example
aduth Nov 1, 2016
cb8cf91
Docs: Fix closing code block syntax
aduth Nov 7, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 91 additions & 76 deletions docs/coding-guidelines/javascript.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ We specify an [EditorConfig](http://editorconfig.org/) configuration and encoura
Object declarations can be made on a single line if they are short (remember the line length guidelines). When an object declaration is too long to fit on one line, there must be one property per line. Property names only need to be quoted if they are reserved words or contain special characters:

```js
let map;

// Preferred
map = {
ready: 9,
when: 4,
'you are': 15
};

// Acceptable for small objects
map = { ready: 9, when: 4, 'you are': 15 };

// Bad
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
// Acceptable for small objects
const labels = { 'google-plus': 'Google Plus' };
```
```js
// Good
const labels = {
facebook: 'Facebook',
twitter: 'Twitter',
'google-plus': 'Google Plus'
};
```

## Arrays and Function Calls
Expand All @@ -70,17 +70,9 @@ foo( {
b: 'beta'
} );

foo( data, function() {
foo( data, () => {
// Do stuff
} );

foo( function() {
// Do stuff
}.bind( this ) );

foo( function() {
// Do stuff
}, options );
```

## Examples of Good Spacing
Expand Down Expand Up @@ -114,7 +106,7 @@ try {

## Semicolons

Use them. Never rely on Automatic Semicolon Insertion (ASI).
Use them. Never rely on [Automatic Semicolon Insertion (ASI)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Automatic_semicolon_insertion).

## Indentation and Line Breaks

Expand All @@ -126,37 +118,29 @@ Try to return early from a function to avoid functions with deep indentation aki

```js
// Bad
function getSiteTitle( site ) {
if ( site ) {
if ( site.name ) {
if ( site.name.length <= 50 ) {
return site.name;
} else {
return site.name.slice( 0, 50 ) + '…';
}
function isFreshData( data ) {
let isFresh;
if ( data ) {
if ( data.timestamp > Date.now() - ( 20 * 60 * 1000 ) ) {
isFresh = true;
} else {
return site.slug;
isFresh = false;
}
} else {
return null;
isFresh = false;
}
}

return isFresh;
}
```
```js
// Good
function getSiteTitle( site ) {
if ( ! site ) {
return null;
}

if ( ! site.name ) {
return site.slug;
}

if ( site.name.length > 50 ) {
return site.name.slice( 0, 50 ) + '…';
function isFreshData( data ) {
if ( data && data.timestamp > Date.now() - ( 20 * 60 * 1000 ) ) {
return true;
}

return site.name;
return false;
}
```

Expand All @@ -165,11 +149,9 @@ function getSiteTitle( site ) {
`if`, `else`, `for`, `while`, and `try` blocks should always use braces, and always go on multiple lines. The opening brace should be on the same line as the function definition, the conditional, or the loop. The closing brace should be on the line directly following the last statement of the block.

```js
let a, b, c;

if ( myFunction() ) {
if ( isLarge() ) {
// Expressions
} else if ( ( a && b ) || c ) {
} else if ( isMedium() ) {
// Expressions
} else {
// Expressions
Expand Down Expand Up @@ -203,14 +185,13 @@ function getStatusLabel() {
When a statement is too long to fit on one line, line breaks must occur after an operator.

```js
let html;

// Bad
html = '<p>The sum of ' + a + ' and ' + b + ' plus ' + c
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

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

Expand Down Expand Up @@ -261,12 +242,13 @@ Note that because parts of our application are [rendered on the server](https://
Variable and function names should be full words, using camel case with a lowercase first letter. This also applies to abbreviations and acronyms.

```js
// Good
let userIdToDelete, siteUrl;

// Bad
let userIDToDelete, siteURL;
```
```js
// Good
let userIdToDelete, siteUrl;
```

Names should be descriptive, but not excessively so. Exceptions are allowed for iterators, such as the use of `i` to represent the index in a loop.

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

Choose a reason for hiding this comment

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

do we need to add in guides for template literals?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

do we need to add in guides for template literals?

Added in 801d151


[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)


```js
// Before
const sumLabel = 'The sum of ' + a + ' and ' + b + ' plus ' + c +
' is ' + ( a + b + c );
```
```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.


## Switch Statements

Switch statements can be useful when there are a large number of cases – especially when multiple cases can be handled by the same block (using fall-through), or the default case can be leveraged.
Expand Down Expand Up @@ -456,7 +450,8 @@ You can prefix a variable with verb only for boolean values when it makes code e
```js
// Bad
const play = false;

```
```js
// Good
const name = 'John';
const blueCar = new Car( 'blue' );
Expand All @@ -472,7 +467,8 @@ The first word of a function name should be a verb (not a noun) to avoid confusi
function name() {
return 'John';
}

```
```js
// Good
function getName() {
return 'John';
Expand Down Expand Up @@ -532,28 +528,47 @@ Since we require strict equality checks, we are not going to enforce [Yoda condi

## Iteration

The functional programming inspired methods that were added in ECMA5 improve readability. Use them throughout.
Starting in [ECMAScript 5](https://en.wikipedia.org/wiki/ECMAScript#5th_Edition), JavaScript includes many methods and patterns inspired by functional programming.

We encourage you to make use of these methods in favor of traditional `for` and `while` loops:

- [`Array#map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
- [`Array#filter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
- [`Array#reduce`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)
- [`Array#some`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)
- [`Array#every`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)

While ES2015 and beyond include many more Array prototype methods, these cannot be used due to complications with polyfills. Instead, you are encouraged to use their [Lodash](https://lodash.com/) equivalents, among many other Lodash methods:

- [`_.find`](https://lodash.com/docs/#find) ([`Array#find`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find))
- [`_.findIndex`](https://lodash.com/docs/#findIndex) ([`Array#findIndex`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex))
- [`_.includes`](https://lodash.com/docs/#includes) ([`Array#includes`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes))

Introduced in ES2015, [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) provide a shorter syntax for function expressions while preserving the parent scope's `this` context. Arrow functions are especially well-suited for iteration method callbacks.

__Examples:__

Creating an array of React elements from an array of post objects:

```js
const posts = postList.map( function( post ) {
return <Post post={ post } key={ post.global_ID } />;
} );
posts.map( ( post ) => (
<Post post={ post } key={ post.global_ID } />
) );
```

When iterating over a large collection using a for loop, it is recommended to store the loop’s max value as a variable rather than re-computing the maximum every time:
Check whether every post in an array of post objects is published:

```js
// Good & efficient:
// getItemCount() gets called once
for ( let i = 0, max = getItemCount(); i < max; i++ ) {
// Do stuff
}
posts.every( ( post ) => post.status === 'publish' );
```

// Bad & potentially inefficient:
// getItemCount() gets called every time
for ( let i = 0; i < getItemCount(); i++ ) {
// Do stuff
}
Group an array of post objects by status:

```js
posts.reduce( ( memo, post ) => {
memo[ post.status ] = ( memo[ post.status ] || [] ).concat( post );
return memo;
}, {} );
```

### React components
Expand Down