1 introduction

As ECMAScript continues to evolve, more and more new language features will be used, making application development easier. The goal of this document is to make the code style of the new ECMAScript features consistent and give some practical suggestions.

This document contains only new features. Follow the JavaScript Style Guide for the basics.

Although this document is designed for ECMAScript, the conventions of this document should be followed when using various ecMAScript-based extensions (such as JSX and TypeScript).

2 Language Features

2.1 variable

[Mandatory] UseletconstDefine variables, do not usevar.

Explanation:

With let and const definitions, the scope of the variable is more explicit.

Example:

// good
for (let i = 0; i < 10; i++) {

}

// bad
for (var i = 0; i < 10; i++) {

}
Copy the code

2.2 deconstruction

Do not use 3 levels or more of deconstruction.

Explanation:

Too many levels of deconstruction make the code hard to read.

Example:

// bad
let {documentElement: {firstElementChild: {nextSibling}}} = window;
Copy the code
When multiple variables are deconstructed, if the length limit is exceeded, each deconstructed variable must have a separate line.

Explanation:

Too much variable deconstruction can make a line of code very long, potentially exceeding the single-line length limit and making the code less readable.

Example:

// good
let {
    name: personName,
    email: personEmail,
    sex: personSex,
    age: personAge
} = person;

// bad
let {name: personName, email: personEmail,
    sex: personSex, age: personAge
} = person;
Copy the code

[Suggestion] Use deconstruction to reduce intermediate variables.

Explanation:

Common scenarios, such as variable value swaps, may produce intermediate variables. Deconstruction is recommended for this scenario.

Example:

// good
[x, y] = [y, x];

// bad
let temp = x;
x = y;
y = temp;
Copy the code

Destructuring is not allowed when defining only one variable.

Explanation:

In this scenario, using deconstruction reduces code readability.

Example:

// good
let len = myString.length;

// bad
let {length: len} = myString;
Copy the code

[Mandatory] Destruct an expression if you do not save intermediate variables generated at the time of writing=The right side of the sign is not allowedObjectLiteralArrayLiteral.

Explanation:

In this scenario, using deconstruction reduces code readability and is usually not beneficial.

Example:

// good
let {first: firstName, last: lastName} = person;
let one = 1;
let two = 2;

// bad
let [one, two] = [1, 2];
Copy the code

When a residual operator is used, all elements preceding the residual operator must be named.

Explanation:

Omitting the name of the element before the remaining operator can cause major program dyslexia. Use the slice method if you only want to fetch the last few items of the array.

Example:

// good
let [one, two, ...anyOther] = myArray;
let other = myArray.slice(3);

// bad
let [,,, ...other] = myArray;
Copy the code

2.3 Template Strings

[Force] Do not use this function when substituting variables in a string2More than one function call.

Explanation:

Complex syntax such as having too many function calls in variable replacements can lead to poor readability.

Example:

// good
let fullName = getFullName(getFirstName(), getLastName());
let s = `Hello ${fullName}`;

// bad
let s = `Hello ${getFullName(getFirstName(), getLastName())}`;
Copy the code

2.4 the function

[Suggestion] Use variable default syntax instead of conditional default value declarations.

Explanation:

Adding default values will help with engine optimization and will work better in strong Mode in the future.

Example:

// good
function foo(text = 'hello') {
}

// bad
function foo(text) {
    text = text || 'hello';
}
Copy the code

Do not useargumentsObject should be used. argsInstead.

Explanation:

Arguments will be disabled in future strong mode.

Example:

// good function foo(... args) { console.log(args.join('')); } // bad function foo() { console.log([].join.call(arguments)); }Copy the code

2.5 Arrow Function

A function is designed to be requiredcallapplyCan’t be an arrow function.

Explanation:

The arrow function forces the binding of this in the current context.

[Mandatory] The arrow function takes only one argument, and when it does not contain destructions, the parentheses in the argument part must be omitted.

Example:

// good
list.map(item => item * 2);

// good
let fetchName = async id => {
    let user = await request(`users/${id}`);
    return user.fullName;
};

// bad
list.map((item) => item * 2);

// bad
let fetchName = async (id) => {
    let user = await request(`users/${id}`);
    return user.fullName;
};
Copy the code
[Suggestion] The body of the arrow function has only one single-line expression statement, and if it is the return value, omit it{}return.

If a single expression is too long, wrap it with ().

Example:

// good
list.map(item => item * 2);

let foo = () => (
    condition
        ? returnValueA()
        : returnValueB()
);

// bad
list.map(item => {
    return item * 2;
});
Copy the code
[Suggestion] The body of the arrow function has only one functionObject Literal, and as the return value(a)The parcel.

Example:

// good
list.map(item => ({name: item[0], email: item[1]}));
Copy the code

2.6 object

[Suggestion] If all keys refer to variables with the same name, use abbreviations for all keys. If a key cannot point to a variable of the same name, all keys are not abbreviated.

Explanation:

The goal is to keep all key-value pair declarations consistent.

// good
let foo = {x, y, z};

let foo2 = {
    x: 1,
    y: 2,
    z: z
};


// bad
let foo = {
    x: x,
    y: y,
    z: z
};

let foo2 = {
    x: 1,
    y: 2,
    z
};
Copy the code

[Mandatory] Used when defining methodsMethodDefinitionSyntax, not usedPropertyName: FunctionExpressionSyntax.

Explanation:

MethodDefinition syntax is cleaner and more concise.

Example:

// good let foo = { bar(x, y) { return x + y; }}; // bad let foo = { bar: function (x, y) { return x + y; }};Copy the code

[Recommended] UseObject.keysObject.entriesPerform object traversal.

Explanation:

Use for.. is not recommended. In traverses the object to avoid missing hasOwnProperty errors.

Example:

// good
for (let key of Object.keys(foo)) {
    let value = foo[key];
}

// good
for (let [key, value] of Object.entries(foo)) {
    // ...
}
Copy the code

[Suggestion] Methods defining objects should not use arrow functions.

Explanation:

The arrow function binds this to the current environment, which tends to result in an unexpected this when obj.method() is called. You should not use the arrow function unless you explicitly need to bind this.

Example:

// good let foo = { bar(x, y) { return x + y; }}; // bad let foo = { bar: (x, y) => x + y };Copy the code

[Suggestion] Try to use the calculated property key to define an object completely in a complete literal, avoid adding object attributes directly after the object is defined.

Explanation:

Declaring all the key values in a complete literal without breaking the code apart helps to improve code readability.

Example:

// good
const MY_KEY = 'bar';
let foo = {
    [MY_KEY + 'Hash']: 123
};

// bad
const MY_KEY = 'bar';
let foo = {};
foo[MY_KEY + 'Hash'] = 123;
Copy the code

2.7 module

[] compulsoryexportTogether with the content definition.

Explanation:

Use the export keyword where you declare something to export, and do not export it after you declare it.

Example:

// good
export function foo() {
}

export const bar = 3;


// bad
function foo() {
}

const bar = 3;

export {foo};
export {bar};
Copy the code
[] compulsoryexportA semicolon indicating an empty statement is not allowed after a statement.

Explanation:

The export keyword does not affect subsequent statement types.

Example:

// good
export function foo() {
}

export default function bar() {
}


// bad
export function foo() {
};

export default function bar() {
};
Copy the code

[Suggestion] Use names to export unrelated content.

Explanation:

For example, a named export should be used when methods in a tool object are not strongly related to each other and often several external uses are selected.

In short, use named exports when a module only acts as a namespace.

[Mandatory] AllimportStatements are written at the beginning of the module.

Example:

// good
import {bar} from './bar';

function foo() {
    bar.work();
}

// bad
function foo() {
    import {bar} from './bar';
    bar.work();
}
Copy the code

2.8 the collection

[Suggestion] Use the array expansion syntax when connecting arrays.

Explanation:

Use array expansion instead of concat, which is more compatible with Iterable.

Example:

// good
let foo = [...foo, newValue];
let bar = [...bar, ...newValues];

// bad
let foo = foo.concat(newValue);
let bar = bar.concat(newValues);
Copy the code

[Suggestion] Do not use array expansion to copy arrays.

Explanation:

Using array expansion syntax to copy, the code is not readable. The array. from method is recommended for replication.

Example:

// good
let otherArr = Array.from(arr);

// bad
let otherArr = [...arr];
Copy the code

[Recommended] Use as much as possiblefor .. ofI’m going to iterate.

Explanation:

Use for.. Of can better accept any Iterable object, such as the iterator generated by Map#values, making the method more versatile.

Except in the following cases:

  1. Traversal is a real performance bottleneck, requiring the use of nativeforCycling improves performance.
  2. Indexes in the process need to be traversed.

[Mandatory] This parameter is mandatory when the key value may not be a stringMap; Must be used when there is a possibility that the element is not a stringSet.

Explanation:

With ordinary Object, you need to implement your own serialization for keys that are not strings. And it is difficult to notify Object of changes in objects during execution.

[Suggestion] This parameter should be used when a non-repeatable set is requiredSet.

Explanation:

Do not use ordinary objects like {foo: true}.

Example:

// good
let members = new Set(['one', 'two', 'three']);

// bad
let members = {
    one: true,
    two: true,
    three: true
};
Copy the code

Suggestion: Enable the traversal function when requiredMapSet.

Explanation:

Maps and sets are traversable objects that make it easy to use for… Of traversal. Do not use ordinary objects.

Example:

// good let membersAge = new Map([ ['one', 10], ['two', 20], ['three', 30] ]); for (let [key, value] of map) { } // bad let membersAge = { one: 10, two: 20, three: 30 }; for (let key in membersAge) { if (membersAge.hasOwnProperty(key)) { let value = membersAge[key]; }}Copy the code

[Suggestion] This parameter is used when the program is adding or removing elementsMapSet.

Explanation:

With Map and Set, the program is more understandable. The semantics of ordinary objects tend to express fixed structures.

Example:

// good
let membersAge = new Map();
membersAge.set('one', 10);
membersAge.set('two', 20);
membersAge.set('three', 30);
membersAge.delete('one');

// bad
let membersAge = {};
membersAge.one = 10;
membersAge.two = 20;
membersAge.three = 30;
delete membersAge['one'];
Copy the code

2.9 the asynchronous

The nesting of callback functions must not exceed 3 levels.

Explanation:

Deep nesting of callback functions can make code difficult to read.

Example:

// bad
getUser(userId, function (user) {
    validateUser(user, function (isValid) {
        if (isValid) {
            saveReport(report, user, function () {
                notice('Saved!');
            });
        }
    });
});
Copy the code

[Recommended] UsePromiseInstead ofcallback.

Explanation:

Using Promise makes the code for complex asynchronous procedures clearer than callback.

Example:

// good let user; getUser(userId) .then(function (userObj) { user = userObj; return validateUser(user); }) .then(function (isValid) { if (isValid) { return saveReport(report, user); } return Promise.reject('Invalid! '); }) .then( function () { notice('Saved! '); }, function (message) { notice(message); });Copy the code

Forced to use standardPromiseAPI.

Explanation:

  1. Non-standard is not allowedPromiseAPI, such asjQueryDeferred,Q.jsdeferAnd so on.
  2. Non-standard is not allowedPromiseExtend the API, such asbluebirdPromise.anyAnd so on.

Using the standard Promise API, you can simply drop the Promise Lib when the runtime supports it.

[Force] Direct extension is not allowedPromiseThe object’sprototype.

Explanation:

For the same reason that it is not allowed to modify or extend any of the prototypes of native and host objects. If you want to make it easier, you can use utility functions.

Do not serialize parallel I/O procedures for ease of writing.

Explanation:

The parallel I/O consumption time is approximately equal to the process with the maximum I/O time, and the serial consumption time is the sum of the time of all processes.

Example:

requestData().then(function (data) { renderTags(data.tags); renderArticles(data.articles); }); // good async function requestData() { const [tags, articles] = await Promise.all([ requestTags(), requestArticles() ]);  return {tags, articles}; } // bad async function requestData() { let tags = await requestTags(); let articles = await requestArticles(); return Promise.resolve({tags, articles}); }Copy the code

[Recommended] Useasync/awaitInstead ofgenerator + co.

Explanation:

The ability to use the language itself makes the code clearer without the need to introduce co libraries.

Example:

addReport(report, userId).then(
    function () {
        notice('Saved!');
    },
    function (message) {
        notice(message);
    }
);

// good
async function addReport(report, userId) {
    let user = await getUser(userId);
    let isValid = await validateUser(user);

    if (isValid) {
        let savePromise = saveReport(report, user);
        return savePromise();
    }

    return Promise.reject('Invalid');
}

// bad
function addReport(report, userId) {
    return co(function* () {
        let user = yield getUser(userId);
        let isValid = yield validateUser(user);

        if (isValid) {
            let savePromise = saveReport(report, user);
            return savePromise();
        }

        return Promise.reject('Invalid');
    });
}
Copy the code

3 environmental

3.1 Operating Environment

[Suggestion] Follow up and pay attention to the level of support for language features in the operating environment.

Explanation:

View your environment’s support for language features

The ES standard is a work in progress, and support for language features in various environments is changing rapidly. It is important to understand which ESNext features are being used in the project, to understand the environment in which the project is being run, and to keep track of how well these features are supported in the environment. This means:

  1. If any runtime environment (such as Chrome) supports all the features used in your project, you can discard precompilation at development time.
  2. If all environments support a feature (such as Promise), you can discard the associated SHIM or do not need to convert at precompile time.
  3. If all environments support all the features used in your project, you can discard precompilation entirely.

In any case, when choosing a precompile tool, you need to be clear about which language features you will be using in your current project, and how well the precompile tool supports those features.

[Force] Not in the run environmentPromiseWhen willPromiseThe implementation of theshimglobalIn the.

Explanation:

You can introduce an extension to shim when there are no promises in the current running environment. If you implement it yourself, you need to implement it under Global and in accordance with the standard API.

This way, the Promise extension can be thrown away at any time in the future when the runtime environment supports it, without any changes to the application code.

3.2 the precompiled

[Recommended] UsebabelRecommended as a precompilation tool5.xVersion.

Explanation:

Since Babel’s latest 6 is not stable for the time being, we recommend using 5.x for the time being. Different products have different browser support, and there are some differences in the parameters you need to set when using Babel. The following are some suggested parameters in the example.

Example:

# if you need to use es7.classProperties, es7.decorators, etc. Additional --stage 0 parameters are required --loose all --modules amd --blacklist strict --stage 0Copy the code

[Recommended] UsebabelAs a precompilation tool, passexternal-helpersReduce the size of the generated file.

Explanation:

When Babel finds features needed during code transformation, it generates the corresponding helper code in the header of the file. By default, for each file processed by Babel, the required helper function is generated in the header of the file. The helper function is duplicated across multiple files, taking up unnecessary code volume.

Therefore, it is recommended to turn on externalHelpers: true so that Babel does not write helper code in its converted content, and instead uses an external.js to provide all helpers. You can use external-helpers in two ways:

  1. Default mode: Yes<script>To introducebabel-polyfill.jsbabel-external-helpers.js.
  2. Customization: Implement babel-Runtime by yourself.

Example:

--loose all --modules amd --external-helpers # 'babelHelpers' var --loose all --modules AMD --optional RuntimeCopy the code

[Recommended] UseTypeScriptRecommended as a precompilation tool1.6 +Version.

Explanation:

With TypeScript 1.6, previous conflicts with ESNext are largely eliminated. TypeScript’s current approach is to follow standards, incorporate stage’s mature features, and provide static typing and type checking, so it’s not as good at Stage 0/1 as Babel. Also, TypeScript can’t specify that a transform is turned off, but it compiles faster than Babel.

Examples of common TypeScript parameters are shown below.

Example:

--module amd --target ES3
--module commonjs --target ES6
Copy the code

[Recommended] UseTypeScriptNot used as a precompilation tooltscCommand.

Explanation:

TSC commands in TypeScript only support the compilation of files with the.ts,.tsx, and.d.ts suffixes. For JavaScript, it is a rule to keep the.js suffix as required in the documentation section.

If you want to use TypeScript as a precompilation tool, you can develop your own precompilation tool based on its Compiler API. If you are an FIS user, you can use the FIS TypeScript plugin.

[Suggestion] Generate AMD modular code when the generated code runs in the browser environment.

Explanation:

AMD is more mature in the browser environment.

[Suggestion] If the ESNext code and ES3/5 code are mixed in the browser-side project, do not use itTypeScriptAs a precompilation tool.

Explanation:

TypeScript generates module code that exports the default export using exports.default but does not directly assign a value to module.exports. As a result, using require(‘moduleName’) in another normal file will not fetch anything.

To use TypeScript, it is recommended that all files in the entire project be in ESNext Module. Mixed modules can have unexpected results.

[Suggestion] If THE AMD/CommonJS module relies on the ESNext module, the AMD/CommonJS module needs to modify the require of default export.

Explanation:

When the ESNext module is compiled, named export is mounted on the exports object and default export is mounted on the exports object with the property named Default. The Exports object also contains an __esModule property with a value of true. The problem is that when AMD/CommonJS modules rely on ESNext modules, require expects exports. Default, but you actually get exports.

Therefore, if the old AMD/CommonJS module relies on the ESNext module of default export, the require of default export needs to be changed to require(‘name’).default.

In addition, if the ESNext modules are interdependent, transpiler does not cause this problem by adding intermediate objects and introducing interop methods.