Recently, I wanted to make a code specification for our company, so I translated the latest specification of Airbnb. It doesn’t help me to leave some out. There’s also code format that’s not translated, Prettier is recommended.
reference
- Use all of your references
const
; Avoid the use ofvar
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
Copy the code
- If you must reassign references, use
let
Instead ofvar
Why is that? Let is block-level scoped instead of function-scoped var
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
Copy the code
object
- Creates objects using literal syntax
// bad
const item = new Object(a);// good
const item = {};
Copy the code
- Use computed properties when creating an object with dynamic properties
Why is that? Allows you to define all attributes of an object in one place
function getKey(k) {
return `a key named ${k}`;
}
// bad
const obj = {
id: 5.name: "San Francisco"
};
obj[getKey("enabled")] = true;
// good
const obj = {
id: 5.name: "San Francisco",
[getKey("enabled")]: true
};
Copy the code
- Object methods that use shorthand
// bad
const atom = {
value: 1.addValue: function(value) {
returnatom.value + value; }};// good
const atom = {
value: 1,
addValue(value) {
returnatom.value + value; }};Copy the code
- Use shorthand attribute values
const lukeSkywalker = "Luke Skywalker";
// bad
const obj = {
lukeSkywalker: lukeSkywalker
};
// good
const obj = {
lukeSkywalker
};
Copy the code
- Organize your shorthand property values at the beginning of your object declaration
Why is that? It’s easy to tell you which attributes are abbreviated
const anakinSkywalker = "Anakin Skywalker";
const lukeSkywalker = "Luke Skywalker";
// bad
const obj = {
episodeOne: 1.twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3.mayTheFourth: 4,
anakinSkywalker
};
// good
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1.twoJediWalkIntoACantina: 2.episodeThree: 3.mayTheFourth: 4
};
Copy the code
- Don’t call directly
Object.prototype
For examplehasOwnProperty
.propertyIsEnumerable
和isPrototypeOf
Why is that? This may trace attributes on the Object – consider {hasOwnProperty: false} – or the Object may be an empty Object (object.create (null)).
// bad
console.log(object.hasOwnProperty(key));
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
// best
const has = Object.prototype.hasOwnProperty; // Cache this query, within the scope of the module
console.log(has.call(object, key));
/* or */
import has from "has"; // https://www.npmjs.com/package/has
console.log(has(object, key));
Copy the code
- Use spreads
.
Operator to replaceObject.assign
Go shallow copy. Use REST when you delete properties.
Operator to get a new object
// very bad
const original = { a: 1.b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this
// bad
const original = { a: 1.b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// good
const original = { a: 1.b: 2 };
constcopy = { ... original,c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const{ a, ... noA } = copy;// noA => { b: 2, c: 3 }
Copy the code
An array of
- Creates objects using literal syntax
// bad
const items = new Array(a);// good
const items = [];
Copy the code
- use
Array#push
Instead of assigning directly, add the item to the array
const someStack = [];
// bad
someStack[someStack.length] = "abracadabra";
// good
someStack.push("abracadabra");
Copy the code
- Use spreads
.
To copy an array
// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];
Copy the code
- Will a
可迭代
The object is converted to an array, using spreads.
Rather thanArray.from
。
const foo = document.querySelectorAll(".foo");
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];
Copy the code
- use
Array.from
Converts an array-like object to an array.
const arrLike = { 0: "foo".1: "bar".2: "baz".length: 3 };
// bad
const arr = Array.prototype.slice.call(arrLike);
// good
const arr = Array.from(arrLike);
Copy the code
- with
Array.from
Instead of spreads.
mapping可迭代
Because it avoids creating an intermediate array.
// bad
const baz = [...foo].map(mapFn);
// good
const baz = Array.from(foo, mapFn);
Copy the code
- Use the callback function to return the statement array method. You can omit the returned function body from a statement to return an expression without side effects
// good
[1.2.3].map(x= > {
const y = x + 1;
return x * y;
});
// good
[1.2.3].map(x= > x + 1);
// bad - No return value means acc becomes undefined after the first iteration[[0.1],
[2.3],
[4.5]
].reduce((acc, item, index) = > {
const flatten = acc.concat(item);
});
// good[[0.1],
[2.3],
[4.5]
].reduce((acc, item, index) = > {
const flatten = acc.concat(item);
return flatten;
});
// bad
inbox.filter(msg= > {
const { subject, author } = msg;
if (subject === "Mockingbird") {
return author === "Harper Lee";
} else {
return false; }});// good
inbox.filter(msg= > {
const { subject, author } = msg;
if (subject === "Mockingbird") {
return author === "Harper Lee";
}
return false;
});
Copy the code
deconstruction
- Use properties of multiple objects, accessed using object deconstruction
Why is that? Deconstruction saves you from creating temporary references to these properties.
// bad
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// good
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// best
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
Copy the code
- Using arrays to deconstruct
const arr = [1.2.3.4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;
Copy the code
- Use object deconstruction for multiple return values instead of array deconstruction
Why is that? You can add new properties over time or change things without breaking the site.
// bad
function processInput(input) {
return [left, right, top, bottom];
}
// Return data in the order that the caller needs to consider
const [left, __, top] = processInput(input);
// good
function processInput(input) {
return { left, right, top, bottom };
}
// Callers select only the data they need
const { left, top } = processInput(input);
Copy the code
string
- When creating strings programmatically, use template strings instead of concatenations.
Why is that? Templates give you a readable string, concise syntax with proper line breaks and string interpolation features.
// bad
function sayHi(name) {
return "How are you, " + name + "?";
}
// bad
function sayHi(name) {
return ["How are you, ", name, "?"].join();
}
// bad
function sayHi(name) {
return `How are you, ${name}? `;
}
// good
function sayHi(name) {
return `How are you, ${name}? `;
}
Copy the code
- Never use
eval()
In one string, it opens too many loopholes
function
- Use named function expressions instead of function declarations
Why is that? Function declarations are promoted, which means that it is easy (too easy) to reference functions before they are defined in a file. This impairs readability and maintainability. If you find that the definition of a function is large enough or too complex to understand the rest of the file, it may be time to extract it into its own module! Whether or not you infer the name from the include variable, don’t forget to name the expression explicitly (this is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions about a faulty call stack.
// bad
function foo() {
// ...
}
// bad
const foo = function() {
// ...
};
// good
// Term names are separated from calls to variable references
const short = function longUniqueMoreDescriptiveLexicalFoo() {
// ...
};
Copy the code
- Wrap the immediately invoked function expression in parentheses.
// Immediately called function expression (IIFE)
(function() {
console.log("Welcome to the Internet. Please follow me."); }) ();Copy the code
-
Never declare functionality in non-functional blocks (if, while, etc.). Instead, you assign functions to variables. Browsers will let you do this, but they all interpret it differently, which is bad news.
-
Note: ECMA-262 defines a block as a list of statements. Function declarations are not statements.
// bad
if (currentUser) {
function test() {
console.log("Nope."); }}// good
let test;
if (currentUser) {
test = (a)= > {
console.log("Yup.");
};
}
Copy the code
- Do not name parameters
arguments
. This will take precedence over the one assigned to each function scopearguments
Object.
// bad
function foo(name, options, arguments) {
// ...
}
// good
function foo(name, options, args) {
// ...
}
Copy the code
- Do not use the
arguments
, instead choosing to use rest syntax.
Instead of
Why is that? . Specify the parameters you want to extract. Also, REST ARG is an actual array, not just array-like arguments.
// bad
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join("");
}
// good
function concatenateAll(. args) {
return args.join("");
}
Copy the code
- Use default parameter syntax instead of mutating function parameters.
// really bad
function handleThings(opts) {
/ / don't! We should not mutate function parameters.
// Double error: if opTS is false, it will be set to {}
// becomes the value you want, but it may introduce some subtle errors.
opts = opts || {};
// ...
}
// still bad
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
// ...
}
Copy the code
-
Avoid the side effects of using default parameters.
Why is that? They were confused.
var b = 1;
// bad
function count(a = b++) {
console.log(a);
}
count(); / / 1
count(); / / 2
count(3); / / 3
count(); / / 3
Copy the code
- Always put the default parameters last.
// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}
Copy the code
- Always put the default parameters last.
Why is that? Creating functions in this way evaluates strings, similar to eval(), which opens up vulnerabilities.
// bad
var add = new Function("a"."b"."return a + b");
// still bad
var subtract = Function("a"."b"."return a - b");
Copy the code
- Never change parameters
Why is that? Manipulating an object passed in as a parameter can cause unwanted variable side effects in the original caller.
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, "key")? obj.key :1;
}
Copy the code
- Do not reassign parameters.
Why is that? Reassigning arguments can cause unexpected behavior, especially when accessing Arguments objects. It can also cause optimization problems, especially in V8.
// bad
function f1(a) {
a = 1;
// ...
}
function f2(a) {
if(! a) { a =1;
}
// ...
}
// good
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
Copy the code
- It is best to use the spread operator
.
To call a variable argument function
Why is that? It’s cleaner, you don’t need to provide context, and you can’t easily write new content with Apply.
// bad
const x = [1.2.3.4.5];
console.log.apply(console, x);
// good
const x = [1.2.3.4.5];
console.log(... x);// bad
new (Function.prototype.bind.apply(Date[null.2016.8.5] ()));// good
new Date(... [2016.8.5]);
Copy the code
Arrow function
- Use the arrow function notation when you must use anonymous functions, such as when passing inline callbacks.
Why is that? It creates the version of the function that executes in this context, which is usually what you want, and has a cleaner syntax. Why not? If you have a fairly complex function, you can move the logic into its own named function expression.
// bad
[1.2.3].map(function(x) {
const y = x + 1;
return x * y;
});
// good
[1.2.3].map(x= > {
const y = x + 1;
return x * y;
});
Copy the code
- If the function body consists of a single statement that returns an expression without side effects, omit the curly braces and use an implicit return. Otherwise, keep the parentheses and use them
return
Statements.
Why is that? Syntactic sugar. It reads well when multiple functions are chaining together.
// bad
[1.2.3].map(number= > {
const nextNumber = number + 1;
`A string containing the ${nextNumber}. `;
});
// good
[1.2.3].map(number= > `A string containing the ${number + 1}. `);
// good
[1.2.3].map(number= > {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}. `;
});
// good
[1.2.3].map((number, index) = > ({
[index]: number
}));
// There are no implicit return side effects
function foo(callback) {
const val = callback();
if (val === true) {
// If the callback returns true, some action is performed}}let bool = false;
// bad
foo((a)= > (bool = true));
// good
foo((a)= > {
bool = true;
});
Copy the code
- If the expression spans more than one line, enclose it in parentheses to improve readability.
Why is that? It clearly shows where the function starts and ends.
Class and constructor
- Always use a
class
. Avoid direct operationsprototype
。
Why is that? Class syntax is more concise and easier to reason about.
// bad
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function() {
const value = this.queue[0];
this.queue.splice(0.1);
return value;
};
// good
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0.1);
returnvalue; }}Copy the code
- use
extends
Inheritance.
Why is that? This is a built-in way to inherit prototype functionality without breaking Instanceof.
// bad
const inherits = require("inherits");
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function() {
return this.queue[0];
};
// good
class PeekableQueue extends Queue {
peek() {
return this.queue[0]; }}Copy the code
- Method can return
this
To help with method chaining.
// bad
Jedi.prototype.jump = function() {
this.jumping = true;
return true;
};
Jedi.prototype.setHeight = function(height) {
this.height = height;
};
const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined
// good
class Jedi {
jump() {
this.jumping = true;
return this;
}
setHeight(height) {
this.height = height;
return this; }}const luke = new Jedi();
luke.jump().setHeight(20);
Copy the code
- You can write a custom
toString()
Method, just make sure it runs successfully and has no side effects.
class Jedi {
constructor(options = {}) {
this.name = options.name || "no name";
}
getName() {
return this.name;
}
toString() {
return `Jedi - The ${this.getName()}`; }}Copy the code
- If not specified, the class has a default constructor. No empty constructors or constructors that delegate only to the parent class are required
// bad
class Jedi {
constructor() {}
getName() {
return this.name; }}// bad
class Rey extends Jedi {
constructor(... args) {super(...args);
}
}
// good
class Rey extends Jedi {
constructor(... args) {super(... args);this.name = "Rey"; }}Copy the code
- Avoid duplicate class members.
Why is that? A duplicate class member declaration will silently select the last one – the duplicate is almost certainly an error.
// bad
class Foo {
bar() {
return 1;
}
bar() {
return 2; }}// good
class Foo {
bar() {
return 1; }}// good
class Foo {
bar() {
return 2; }}Copy the code
- Unless an external library or framework requires a specific non-static method, class methods should be used
this
Or make it static. As an example method, it should indicate that its behavior varies depending on the attributes of the receiver.
// bad
class Foo {
bar() {
console.log("bar"); }}// good-this has been used
class Foo {
bar() {
console.log(this.bar); }}// good - constructor is exempt
class Foo {
constructor() {
// ...}}// good - static methods should not use this
class Foo {
static bar() {
console.log("bar"); }}Copy the code
-
Always use modules (import/export) on non-standard module systems. You can always switch to the preferred module system.
Why is that? Modules are the future, so let’s start using the future now.
// bad
const AirbnbStyleGuide = require("./AirbnbStyleGuide");
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from "./AirbnbStyleGuide";
export default AirbnbStyleGuide.es6;
// best
import { es6 } from "./AirbnbStyleGuide";
export default es6;
Copy the code
- Do not use wildcard imports.
Why is that? This ensures that you have a single default export.
// bad
import * as AirbnbStyleGuide from "./AirbnbStyleGuide";
// good
import AirbnbStyleGuide from "./AirbnbStyleGuide";
Copy the code
- Do not export mutable bindings.
Why is that? Mutations should generally be avoided, especially when exporting mutable bindings. In general, only constant references should be exported, although this technique may be necessary in some special cases.
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
Copy the code
- In modules with a single export, default exports are preferred over named exports.
Why is that? Encourage more files that export only one thing, which is better for readability and maintainability.
// bad
export function foo() {}
// good
export default function foo() {}
Copy the code
- Will all
import
In theimport
The statement above
Why is that? Since imports are promoted, placing them all at the top prevents surprises.
// bad
import foo from "foo";
foo.init();
import bar from "bar";
// good
import foo from "foo";
import bar from "bar";
foo.init();
Copy the code
The iterator
- Don’t use iterators. Prefer JavaScript higher-order functions to for-in or for-of loops
Why is that? This enforces our unchanging rules. Pure functions that handle return values are easier to deduce than side effects. Using the map ()/every ()/filter ()/the find ()/findIndex ()/reduce ()/some () /… Iterating over arrays, and object.keys ()/object.values ()/object.entries () produce arrays, so you can iterate over objects.
const numbers = [1.2.3.4.5];
// bad
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// good
let sum = 0;
numbers.forEach(num= > {
sum += num;
});
sum === 15;
// best (use the functional force)
const sum = numbers.reduce((total, num) = > total + num, 0);
sum === 15;
// bad
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// good
const increasedByOne = [];
numbers.forEach(num= > {
increasedByOne.push(num + 1);
});
// best (keeping it functional)
const increasedByOne = numbers.map(num= > num + 1);
Copy the code
attribute
- When accessing properties, use the dot notation.
const luke = {
jedi: true.age: 28
};
// bad
const isJedi = luke["jedi"];
// good
const isJedi = luke.jedi;
Copy the code
- When calculating an exponent, use the exponent operator
支那
。
// bad
const binary = Math.pow(2.10);
// good
const binary = 2支那10;
Copy the code
variable
- Always declare variables using const or let. Failure to do so will result in global variables. We want to avoid contaminating the global namespace.
// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();
Copy the code
- Declare or assign each variable using a const or let.
Why is that? It’s easier to add new variable declarations this way, and you don’t have to worry about changing; Replace, or introduce punctuation only differences. You can also use the debugger to execute each declaration step by step, rather than skipping all at once.
// bad
const items = getItems(),
goSportsTeam = true,
dragonball = "z";
// bad
// (compare to above, and try to spot the mistake)
const items = getItems(),
goSportsTeam = true;
dragonball = "z";
// good
const items = getItems();
const goSportsTeam = true;
const dragonball = "z";
Copy the code
- For all
const
I’m going to group all of themlet
Group.
Why is that? This is useful later when you may need to assign variables based on one of the previously assigned variables.
// bad
let i,
len,
dragonball,
items = getItems(),
goSportsTeam = true;
// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
Copy the code
- Assign variables where they are needed, but put them where they should be.
Why is that? Let and const are block scoped, not function scoped.
// bad - unnecessary function calls
function checkName(hasName) {
const name = getName();
if (hasName === "test") {
return false;
}
if (name === "test") {
this.setName("");
return false;
}
return name;
}
// good
function checkName(hasName) {
if (hasName === "test") {
return false;
}
const name = getName();
if (name === "test") {
this.setName("");
return false;
}
return name;
}
Copy the code
- Do not link variable assignments.
Why is that? Linked variable assignments create implicit global variables.
// bad
(function example() {
// JavaScript interprets it as
// let a = ( b = ( c = 1 ) );
// the let keyword applies only to variable A; Variables B and c become global variables
let a = (b = c = 1); }) ();console.log(a); // throws ReferenceError
console.log(b); / / 1
console.log(c); / / 1
// good
(function example() {
let a = 1;
let b = a;
letc = a; }) ();console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// The same applies to const
Copy the code
- Avoid unitary addition and subtraction (
++
.--
).
Why is that? According to the ESLint documentation, unary increment and decrement statements insert semicolons automatically and may cause silent errors as values increase or decrease in the application. It’s also more expressive to change your value with a statement like num+ = 1 instead of num++ or num++. Disallowing unary increment and decrement statements also prevents unintentional pre-increment/pre-decrement of values, which can also cause unexpected behavior in the program.
// bad
const array = [1.2.3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
let value = array[i];
sum += value;
if(value) { truthyCount++; }}// good
const array = [1.2.3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) = > a + b, 0);
const truthyCount = array.filter(Boolean).length;
Copy the code
- Do not use unused variables.
Why is that? Because of incomplete refactoring, it is probably an error to declare variables that are not used anywhere in the code. Such variables take up space in your code and can cause confusion for the reader.
// bad
var some_unused_var = 42;
// Writing variables only is not considered used.
var y = 10;
y = 5;
// A read that modifies itself is not considered used.
var z = 0;
z = z + 1;
// Unused function arguments.
function getX(x, y) {
return x;
}
// good
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// Even if not used, 'type' is ignored because it has the sibling of the 'rest' attribute.
// This is a form of extracting objects that ignore the specified key.
var{ type, ... coords } = data;// Now, 'coords' is a' data' object without the 'type' attribute.
Copy the code
ascension
var
Declarations are promoted to the top of the nearest enclosing function scope, while their assignments are not. Const and LET are fortunate to have a new concept called “temporary dead zones (TDZ).” It is important to know why Typeof is no longer secure.
// We know this won't work (assuming no notDefined global variables)
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// Creating a variable declaration after referencing a variable will work due to the promotion of the variable. Note: the assignment of 'true' is not promoted.
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// The interpreter pushes variable declarations to the top of the scope, which means our example can be rewritten as:
function example() {
let declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
// using const and let
function example() {
console.log(declaredButNotAssigned); // => throws a ReferenceError
console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
const declaredButNotAssigned = true;
}
Copy the code
- Anonymous function expressions use their variable names instead of function assignments.
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function() {
console.log("anonymous function expression");
};
}
Copy the code
- Naming a function expression elevates the variable name, not the function name or function body.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log("Flying");
};
console.log(named, superPower);
}
The same is true when the function name is the same as the variable name.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log("named");
};
}
Copy the code
- Function declarations promote their name and function body.
function example() {
superPower(); // => Flying
function superPower() {
console.log("Flying"); }}Copy the code
Compare operators and equality
-
Use === and! == replace == and! =
-
A conditional statement (such as an if statement) uses the ToBoolean abstraction method to force its expression to evaluate its expression, always following these simple rules:
- Objects evaluates to true
- Undefined evaluates to false
- Null evaluates to false
- Booleans evaluates as Boolean
- Numbers evaluates to false if +0, -0, or NaN, and true otherwise
- Strings evaluates to false if it is an empty string “, true otherwise
if ([0] && []) {
// true
// An array (even an empty array) is an object that evaluates to true
}
Copy the code
- Use shortcuts for booleans, while strings and numbers are explicitly compared.
// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if(name ! = ="") {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}
Copy the code
- Use curly braces to create block-level scopes containing case and default clauses (e.g., let, const, function, and class)
Why is that? Lexical declarations are visible throughout the switch block, but are initialized only at allocation time, and only occur when case is reached. This can cause problems when multiple case clauses try to define the same thing.
// bad
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}}// good
switch (foo) {
case 1: {
let x = 1;
break;
}
case 2: {
const y = 2;
break;
}
case 3: {
function f() {
// ...
}
break;
}
case 4:
bar();
break;
default: {
class C {}}}Copy the code
- Ternary numbers should not be nested and are usually single-line expressions.
// bad
const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null;
// Divided into two independent ternary expressions
const maybeNull = value1 > value2 ? "baz" : null;
// better
const foo = maybe1 > maybe2 ? "bar" : maybeNull;
// best
const foo = maybe1 > maybe2 ? "bar" : maybeNull;
Copy the code
- Avoid unnecessary ternary statements.
// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
constbar = !! c;constbaz = ! c;Copy the code
- When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operator:
+
.-
和支那
Because their priorities are well known. We recommend enclosing/and * in parentheses, because their priority can be ambiguous when they are mixed.
Why is that? This improves readability and clarifies the developer’s intent.
// bad
const foo = (a && b < 0) || c > 0 || d + 1= = =0;
// bad
const bar = a ** b - (5 % d);
// bad
/ / a person may be confused thinking (a | |) b & c
if (a || (b && c)) {
return d;
}
// bad
const bar = a + (b / c) * d;
// good
const foo = (a && b < 0) || c > 0 || d + 1= = =0;
// good
const bar = a ** b - (5 % d);
// good
if (a || (b && c)) {
return d;
}
// good
const bar = a + (b / c) * d;
Copy the code
block
- if
if
Block always executereturn
Statement, the followingelse
Blocks are unnecessary. Containing the return valueif
The back of the blockelse if
The return value in a block can be divided into multiple typesif
Block.
// bad
function foo() {
if (x) {
return x;
} else {
returny; }}// bad
function cats() {
if (x) {
return x;
} else if (y) {
returny; }}// bad
function dogs() {
if (x) {
return x;
} else {
if (y) {
returny; }}}// good
function foo() {
if (x) {
return x;
}
return y;
}
// good
function cats() {
if (x) {
return x;
}
if (y) {
returny; }}// good
function dogs(x) {
if (x) {
if (z) {
returny; }}else {
returnz; }}Copy the code
annotation
- use
/ * *... * /
Represents a multi-line comment.
// bad
// make() returns a new element
// Based on the tag name passed in
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// good
/** * make() returns a new element based on the tag name passed in */
function make(tag) {
// ...
return element;
}
Copy the code
//
Used for single-line comments. Place a single-line comment on the newline above the comment subject. Place an empty line before the comment except on the first line of the comment.
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log("fetching type...");
// set the default type to 'no type'
const type = this.type || "no type";
return type;
}
// good
function getType() {
console.log("fetching type...");
// set the default type to 'no type'
const type = this.type || "no type";
return type;
}
// also good
function getType() {
// set the default type to 'no type'
const type = this.type || "no type";
return type;
}
Copy the code
- Start all comments with a space to make them easier to read
// bad
//is current tab
const active = true;
// good
// is current tab
const active = true;
// bad
/** *make() returns a new element *based on the passed-in tag name */
function make(tag) {
// ...
return element;
}
// good
/** * make() returns a new element * based on the passed-in tag name */
function make(tag) {
// ...
return element;
}
Copy the code
-
Prefixing your comments with FIXME or TODO helps other developers quickly know if you’re pointing out a problem that needs to be reworked or suggesting a solution to a problem that needs to be fixed. These are different from regular reviews because they are feasible. These actions are FIXME: – need to figure this out or TODO: – need to be implemented.
-
Use // FIXME: annotate the problem
class Calculator extends Abacus {
constructor() {
super(a);// FIXME:Shouldn't use a global here
total = 0; }}Copy the code
- use
// TODO:
Comment out the solution to the problem
class Calculator extends Abacus {
constructor() {
super(a);// TODO: total should be configurable by an options param
this.total = 0; }}Copy the code
Naming conventions
- Avoid single-letter names. Use your name to describe it.
// bad
function q() {
// ...
}
// good
function query() {
// ...
}
Copy the code
- Use hump naming when naming objects, functions, and instances
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
Copy the code
- Use PASCAL names only when naming constructors or classes.
// bad
function user(options) {
this.name = options.name;
}
const bad = new user({
name: "nope"
});
// good
class User {
constructor(options) {
this.name = options.name; }}const good = new User({
name: "yup"
});
Copy the code
- Do not use endings or underscores.
Why is that? JavaScript has no concept of privacy when it comes to properties or methods. Although underlining usually refers to “private” conventions, in reality, these attributes are completely public and therefore part of your public API contract. This convention can lead developers to mistakenly believe that changes are not considered failures or do not require testing. tl; Dr: If you want something to be “private,” you must not make it obvious.
// bad
this.__firstName__ = "Panda";
this.firstName_ = "Panda";
this._firstName = "Panda";
// good
this.firstName = "Panda";
// Good, in an environment where WeakMaps can be used
const firstNames = new WeakMap(a); firstNames.set(this."Panda");
Copy the code
- Don’t save
this
The reference. Use the arrow function orFunction#bind
// bad
function foo() {
const self = this;
return function() {
console.log(self);
};
}
// bad
function foo() {
const that = this;
return function() {
console.log(that);
};
}
// good
function foo() {
return (a)= > {
console.log(this);
};
}
Copy the code
- The base file name should match the default export file name exactly.
// file 1 contents
class CheckBox {
// ...
}
export default CheckBox;
// file 2 contents
export default function fortyTwo() {
return 42;
}
// file 3 contents
export default function insideDirectory() {}
// in some other file
// bad
import CheckBox from "./checkBox"; // Name PASCAL import/export, and the camel name filename
import FortyTwo from "./FortyTwo"; // Name PASCAL import/filename, and name the hump export
import InsideDirectory from "./InsideDirectory"; // Name PASCAL import/filename, and name the hump export
// bad
import CheckBox from "./check_box"; Snake_case filename
import forty_two from "./forty_two"; // snake_case import/filename
import inside_directory from "./inside_directory"; // snake_case import, camel name export
import index from "./inside_directory/index"; // requiring the index file explicitly
import insideDirectory from "./insideDirectory/index"; // requiring the index file explicitly
// good
import CheckBox from "./CheckBox"; // PASCAL names export/import/filename
import fortyTwo from "./fortyTwo"; // Camel name export/import/filename
import insideDirectory from "./insideDirectory"; Export /import/directory name/implicit "index"
// ^ supports both insideDirectory.js and insideDirectory/index.js
Copy the code
- Use a hump name when exporting default functions. The file name should be the same as the function name.
function makeStyleGuide() {
// ...
}
export default makeStyleGuide;
Copy the code
- When exporting constructors/classes/singletons/function libraries/bare objects, use PASCAL names.
const AirbnbStyleGuide = {
es6: {}};export default AirbnbStyleGuide;
Copy the code
- Acronyms and acronyms should always be all uppercase or all lowercase.
Why is that? Names are meant to improve readability, not to placate computer algorithms.
// bad
import SmsContainer from "./containers/SmsContainer";
// bad
const HttpRequests = [
// ...
];
// good
import SMSContainer from "./containers/SMSContainer";
// good
const HTTPRequests = [
// ...
];
// also good
const httpRequests = [
// ...
];
// best
import TextMessageContainer from "./containers/TextMessageContainer";
// best
const requests = [
// ...
];
Copy the code
-
You can choose to capitalize a constant only if (1) it is exported, (2) it is const (it cannot be reassigned), and (3) the programmer can trust it (and its nested properties) to never change.
Why is that? This is a helper tool that helps programmers when they are unsure whether a variable will change. UPPERCASE_VARIABLES (uppercase variables) lets programmers know that they can trust variables (and their attributes) to remain unchanged.
- What about all const variables? – This is not necessary, so uppercase should not be applied to constants in files. However, it applies to exported constants.
- What about exported objects? – Use capital letters at the top level of the export (for example, exported_object.key) and leave all nested properties unchanged.
// bad
const PRIVATE_VARIABLE = "should not be unnecessarily uppercased within a file";
// bad
export const THING_TO_BE_CHANGED = "should obviously not be uppercased";
// bad
export let REASSIGNABLE_VARIABLE = "do not use let with uppercase variables";
// ---
// Allow, but do not provide semantic values
export const apiKey = "SOMEKEY";
// Better in most cases
export const API_KEY = "SOMEKEY";
// ---
// bad - unnecessary uppercase key, but no semantic value added
export const MAPPING = {
KEY: "value"
};
// good
export const MAPPING = {
key: "value"
};
Copy the code
accessor
- Do not use JavaScript getters/setters because they can cause unexpected side effects and are more difficult to test, maintain, and reason about. Instead, if you do make accessor functions, use
getVal()
和setVal('hello')
。
// bad
class Dragon {
get age() {
// ...
}
set age(value) {
// ...}}// good
class Dragon {
getAge() {
// ...
}
setAge(value) {
// ...}}Copy the code
- If the property/method is a Boolean value, use
isVal()
或hasVal()
。
// bad
if(! dragon.age()) {return false;
}
// good
if(! dragon.hasAge()) {return false;
}
Copy the code
- You can create
get()
和set()
Delta function, but keep it consistent.
class Jedi {
constructor(options = {}) {
const lightsaber = options.lightsaber || "blue";
this.set("lightsaber", lightsaber);
}
set(key, val) {
this[key] = val;
}
get(key) {
return this[key]; }}Copy the code
The event
- When attaching data payloads to events (whether DOM events or more proprietary events like Backbone events), pass object literals (also known as “hashes”) instead of raw values. This allows subsequent participants to add more data to the event payload without having to find and update each handler of the event. For example, instead of:
// bad
$(this).trigger("listingUpdated", listing.id);
// ...
$(this).on("listingUpdated", (e, listingID) => {
// do something with listingID
});
Copy the code
Better:
// good
$(this).trigger("listingUpdated", { listingID: listing.id });
// ...
$(this).on("listingUpdated", (e, data) => {
// do something with data.listingID
});
Copy the code
The standard library
The library contains utilities that are functionally broken but retained for legacy reasons.
- use
Number.isNaN
Instead of a globalisNaN
Why is that? Global isNaN casts non-numbers to numbers, returning true for the result of any NaN coercion. If you need this behavior, make it explicit.
// bad
isNaN("1.2"); // false
isNaN("1.2.3"); // true
// good
Number.isNaN("1.2.3"); // false
Number.isNaN(Number("1.2.3")); // true
Copy the code
- use
Number.isFinite
Instead of a globalisFinite
Why is that? Global isFinite forces a non-number to a number, and returns true for any value forced to a finite number. If you need this behavior, make it explicit.
// bad
isFinite("2e3"); // true
// good
Number.isFinite("2e3"); // false
Number.isFinite(parseInt("2e3".10)); // true
Copy the code