-
Notifications
You must be signed in to change notification settings - Fork 2k
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
Changes from 5 commits
182864f
7ebc01e
ab72e88
8f09a83
8afd673
f258519
6ec38c6
60a0f12
e80800a
4b26ff5
38f85ea
1dc7fcd
544aac6
be210dd
e60fa63
8874a07
519dfa4
04bd011
3dfa55e
8eaf5b4
818cdf8
e3b41c4
4d171dd
30f204b
a50cb57
ffedbac
d7b075a
801d151
cb8cf91
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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' }; | ||
``` | ||
```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 | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
||
|
@@ -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; | ||
} | ||
``` | ||
|
||
|
@@ -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 | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 good catch! There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ); | ||
``` | ||
|
||
|
@@ -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. | ||
|
||
|
@@ -420,6 +402,18 @@ const myStr = "You're amazing just the way you are."; | |
const component = <div className="post"></div>; | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we need to add in guides for template literals? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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'
// } There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I personally feel we should avoid advocating their usage. That is... unless we ever adopt Relay 😉 (reference) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 }`; | ||
``` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. w00t! I ❤️ this! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Thanks! As I was changing the original in "Multi-line Statements" to use |
||
|
||
## 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. | ||
|
@@ -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' ); | ||
|
@@ -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'; | ||
|
@@ -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 | ||
|
There was a problem hiding this comment.
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 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I rearranged all of the examples to show "Bad" first.