Javascript 代码规范

代码与风格

空格与缩进

switch 下的 case 和 default 必须增加一个缩进层级
// good
switch (variable) {
  case '1':
    // do...
    break;
  case '2':
    // do...
    break;
  default:
    // do...
}

// bad
switch (variable) {
case '1':
  // do...
  break;
case '2':
  // do...
  break;
default:
// do...
}
使用多行模板字符串时遵循缩进原则。当空行与空白字符敏感时,不使用多行模板字符串
// good
function foo() {
  let html = `
    <div>
        <p></p>
        <p></p>
    </div>
  `;
}
// good
function greeting(name) {
  return 'Hello, \n'
    + `${name.firstName} ${name.lastName}`;
}

// bad
function greeting(name) {
  return `Hello,
${name.firstName} ${name.lastName}`;
}
二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格
// good
let a = !arr.length;
a++;
a = b + c;

// bad
let a =!arr.length;
a ++;
a = b+c;
用作代码块起始的左花括号 { 前必须有一个空格
// good
if (condition) {
}
while (condition) {
}
function funcName() {
}

// bad
if (condition){
}
while (condition){
}
function funcName(){
}
if / else / for / while / function / switch / do / try / catch / finally 关键字后,必须有一个空格
// good
if (condition) {
}
while (condition) {
}
(function () {
})();

// bad
if(condition) {
}
while(condition) {
}
(function() {
})();
在对象创建时,属性中的 : 之后必须有空格,: 之前不允许有空格
// good
const obj = {
  a: 1,
  b: 2,
  c: 3,
};

// bad
const obj = {
  a : 1,
  b:2,
  c :3,
};
函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格
// good
function funcName() {
}
const funcName = function funcName() {
};
funcName();

// bad
function funcName () {
}
const funcName = function funcName () {
};
funcName ();
, 和 ; 前不允许有空格。如果不位于行尾,, 和 ; 后必须跟一个空格
// good
callFunc(a, b);

for (let i = 0; i < length; i++) {
  // xxx
}

// bad
callFunc(a , b) ;

for (let i = 0;i < length;i++) {
  // xxx
}
在函数调用、函数声明、单行声明的数组、括号表达式、属性访问、if / for / while / switch / catch 等语句中,() 和 [] 内紧贴括号部分不允许有空格
// good
callFunc(param1, param2, param3);
const arr2 = [1, 2, 3];
save(this.list[this.indexes[i]]);
needIncream && (variable += increament);
if (num > list.length) {
}
while (len--) {
}

// bad
callFunc( param1, param2, param3 );
const arr2 = [ 1, 2, 3 ];
save( this.list[ this.indexes[ i ] ] );
needIncreament && ( variable += increament );
if ( num > list.length ) {
}
while ( len-- ) {
}
generator 时,_ 前面不允许有空格,_ 后面必须有一个空格
// good
function* caller() {
  yield 'a';
  yield* callee();
  yield 'd';
}

// bad
function * caller() {
  yield 'a';
  yield *callee();
  yield 'd';
}
单行对象的大括号内侧要保留一个空格
// good
// simple object literals
const obj = { foo: 'bar' };
// nested object literals
const obj = { foo: { zoo: 'bar' } };
// destructuring assignment (EcmaScript 6)
const { x, y } = y;
// import/export declarations (EcmaScript 6)
import { foo } from 'bar';
export { foo };

// bad
// simple object literals
const obj = {foo: 'bar'};
// nested object literals
const obj = {foo: { zoo: 'bar' }};
// destructuring assignment (EcmaScript 6)
const {x, y} = y;
// import/export declarations (EcmaScript 6)
import {foo} from 'bar';
export {foo};

换行

每个独立语句结束后必须换行
// good
console.log('a');
console.log('b');

// bad
console.log('a');console.log('b');
运算符处换行时,运算符必须在上一行的行末
// good
if (user.isAuthenticated() &&
  user.isInRole('admin') &&
  user.hasAuthority('add-admin') ||
  user.hasAuthority('delete-admin')) {
  // Code
}
const result = number1 + number2 + number3 +
  number4 + number5;

// bad
if (user.isAuthenticated()
  && user.isInRole('admin')
  && user.hasAuthority('add-admin')
  || user.hasAuthority('delete-admin')) {
  // Code
}
const result = number1 + number2 + number3
  + number4 + number5;
在函数声明、函数表达式、函数调用、对象创建、数组创建、for 语句等场景中,不允许在 , 或 ; 前换行
// good
const obj = {
  a: 1,
  b: 2,
  c: 3,
};
foo(
  aVeryVeryLongArgument,
  anotherVeryLongArgument,
  callback
);

// bad
const obj = {
  a: 1
  , b: 2
  , c: 3
};
foo(
  aVeryVeryLongArgument
  , anotherVeryLongArgument
  , callback
);

不同行为或逻辑的语句集,使用空行隔开,更易阅读
// 仅为按逻辑换行的示例,不代表setStyle的最优实现
function setStyle(element, property, value) {
  if (element == null) {
    return;
  }

  element.style[property] = value;
}
解构多个变量或声明数组或对象时,如果变量数量超过 3 个或者单行字符超过 120 时,每个解构的变量必须单独一行
// good
const { name: personName, email: personEmail } = person;
const {
  name: personName,
  email: personEmail,
  sex: personSex,
  age: personAge
} = person;
const person = { name: personName, email: personEmail };
const person = {
  name: personName,
  email: personEmail,
  sex: personSex,
  age: personAge,
};

// bad
const {name: personName, email: personEmail,
  sex: personSex, age: personAge
} = person;
const person = {name: personName, email: personEmail,
  sex: personSex, age: personAge};
2.3.6 [强制] else 语句不允许换行
// good
if (xxx) {
} else {
}

// bad 
if (xxx) {
}
else {
}

命名

变量、函数、函数的 参数 使用 Camel 命名法
const loadingModules = {};

function stringFormat(source) {
}

function hear(theBells) {
}
类使用 Pascal 命名法
class Demo {
  text = 'this demo';
  appendTo() {
  }
}
类的方法使用 Camel 命名法
class TextNode( {
  cloneData() {
  }
}
枚举变量 使用 Pascal 命名法,枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式
const TargetState = {
  READING: 1,
  READED: 2,
  APPLIED: 3,
  READY_TO_GO: 4,
};
由多个单词组成的缩写词(如 HTTP),在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致
// good
function insertHTML(element, html) {
}
const httpRequest = new HTTPRequest();

// bad
function insertHtml(element, html) {
}
const httpRequest = new httpRequest();

分号

不得省略语句结束的分号
// good
const age = 10;
export const age = 10;

// bad
const age = 10
export const age = 10
函数定义结束不允许添加分号
// good
function funcName() {
}
export function funcName() {
}
// bad
function funcName() {
};
export function funcName() {
};

// 如果是函数表达式,分号是不允许省略的。
const funcName = function () {
};
类声明结束不允许添加分号
// good
class A {
}

// bad
class A {
};
类成员定义中,方法定义后不允许添加分号,成员属性定义后必须添加分号
// good
class Foo {
  foo = 3;
  bar() {
  }
}

// bad
class Foo {
  foo = 3
  bar() {
  };
}
2.5.5 [强制] 属性装饰器后,可以不加分号的场景,不允许加分号
// good
class Foo {
  @log('INFO')
  bar() {
  }
  @log('INFO');
  ['bar' + 2]() {
  }
}

// bad
class Foo {
  @log('INFO');
  bar() {
  }
  @log('INFO')
  ['bar' + 2]() {
  }
}

括号

IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (
// good
const task = (function () {
  // Code
  return result;
})();
const func = function () {
};

// bad
const task = function () {
  // Code
  return result;
}();
const func = (function () {
});
箭头函数的参数只有一个,并且不包含解构时,参数部分的括号必须省略
// good
list.map(item => item * 2);
// good
const fetchName = async id => {
  const user = await request(`users/${id}`);
  return user.fullName;
};

// bad
list.map((item) => item * 2);
// bad
const fetchName = async (id) => {
  const user = await request(`users/${id}`);
  return user.fullName;
};
箭头函数的函数体只有一个单行表达式语句,且作为返回值时,省略 {} 和 return
// good
list.map(item => item * 2);
const foo = () => (
  condition
    ? returnValueA()
    : returnValueB()
);

// bad
list.map(item => {
  return item * 2;
});
箭头函数的函数体只有一个 Object Literal,且作为返回值时,使用 () 包裹
// good
list.map(item => ({ name: item[0], email: item[1] }));
在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {…}
// good
if (condition) {
  callFunc();
}
if (condition) {
  return;
}

// bad
if (condition) callFunc();
if (condition) { callFunc(); }
if (condition) { return; }
if (condition) return;
if (condition)
  callFunc();
  callFunc();

注释

优先使用 // 单行注释。// 后跟一个空格,缩进与下一行被注释说明的代码一致
避免使用 /…/ 这样的多行注释,有多行注释内容时,使用多个单行注释
为了便于代码阅读和自文档化,以下内容建议包含以 /*…/ 形式的块注释中
  • 文件
  • namespace
  • 函数或方法
  • 类属性
  • 事件
  • 全局变量
  • 常量
  • AMD 模块
注释应该符合 JSDoc 的语法约定

语言特性

变量

使用 let 和 const 定义变量,不使用 var
// good
for (let i = 0; i < 10; i++) {
}

// bad
for (var i = 0; i < 10; i++) {
}
变量、函数在使用前必须先定义
// good
const name = 'MyName';

// bad
name = 'MyName';
声明并赋值时,每次只能声明一个变量;只声明不赋值时,允许一次声明多个变量。禁止使用保留字作为变量名,首次声明时禁止将 undefined 赋值给变量,禁止连续赋值
// good
const hangModules = [];
const missModules = [];
const visited = {};

// bad 一次声明多个变量
const hangModules = [],
  missModules = [],
  visited = {};
// bad 使用保留字作为变量名
const function = 'a';
// bad 将`undefined`赋值给变量
const a = undefined;
// bad 连续赋值
const a = b = c = 5;
变量建议 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量
// good
function kv2List(source) {
  const list = [];
  for (let key in source) {
    if (source.hasOwnProperty(key)) {
      const item = {
        k: key,
        v: source[key],
      };
      list.push(item);
    }
  }
  const str = list.join('/');
  return { list, str };
}

// bad
function kv2List(source) {
  const list = [];
  let key;
  let item;
  let str;
  for (key in source) {
    if (source.hasOwnProperty(key)) {
      item = {
        k: key,
        v: source[key],
      };
      list.push(item);
    }
  }
  str = list.join('/');
  return { list, str };
}

解构

不要使用 3 层及以上的解构
// bad
const { documentElement: { firstElementChild: { nextSibling } } } = window;
使用解构减少中间变量
// 下面交换变量的 demo 中,使用解构会牺牲一部分性能,但却获得了更简洁的代码,所以在非强性能场景下,我们推荐使用解构的方式。关于性能的影响,可以参考:这个链接

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

// bad
let temp = x;
x = y;
y = temp;
仅定义一个变量时不允许使用解构
// good
const len = myString.length;

// bad
const { length: len } = myString;
如果不节省编写时产生的中间变量,解构表达式 = 号右边不允许是 ObjectLiteral 和 ArrayLiteral
// good
const { first: firstName, last: lastName } = person;
const one = 1;
const two = 2;

// bad
const [ one, two, three ] = [ 1, 2, 3 ];
const { one, two } = { one: 1, two: 2 };
使用剩余运算符时,剩余运算符之前的所有元素必需具名
// good
let [one, two, ...anyOther] = myArray;
let other = myArray.slice(3);

// bad
let [,,, ...other] = myArray;

条件

在 Equality Expression 中使用类型严格的 ===
// good
if (age === 30) {
  // ......
}

// bad
if (age == 30) {
  // ......
}
当判断 null 或 undefined 时,允许使用 == null
// good
if (x == null) {
}

// bad
if (x === null || x === undefined) {
}
尽可能使用简洁的表达式
// 字符串为空
// good
if (!name) {
  // ......
}
// bad
if (name === '') {
  // ......
}
// 字符串非空
// good
if (name) {
  // ......
}
// bad
if (name !== '') {
  // ......
}
// 数组非空
// good
if (collection.length) {
  // ......
}
// bad
if (collection.length > 0) {
  // ......
}
// 布尔不成立
// good
if (!notTrue) {
  // ......
}
// bad
if (notTrue === false) {
  // ......
}
// null 或 undefined
// good
if (noValue == null) {
  // ......
}
// bad
if (noValue === null 
  || typeof noValue === 'undefined') {
  // ......
}
// 不要使用嵌套的三元表达式
// bad
if (a ? b : c ? d : e) {
  // ......
}
通过条件判断的提前返回来避免多层嵌套的条件判断
// good
if (!expressionA) {
  return;
}
process(a);
if (!expressionB) {
  return;
}
process(b);
if (expressionC) {
  // do sth
} else {
  // do sth
}

// bad
if (expresssionA) {
  process(a);
  if (expressionB) {
    process(b);
    if (expressionC) {
      // do sth
    } else {
      // do sth
    }
  }
}
按执行频率排列分支的顺序
if (condition === '执行频率最高') {
  // ...
} else if (condition === '执行频率中等') {
  // ...
} else if (condition === '执行频率最低') {
  // ...
}

switch (condition) {
  case '执行频率最高':
    // ...
    break;
  case '执行频率中等':
    // ...
    break;
  case '执行频率最低':
    // ...
    break;
}
对于相同变量或表达式的多值条件,用 switch 代替 if
switch (typeof variable) {
  case 'object':
    // ......
    break;
  case 'number':
  case 'boolean':
  case 'string':
    // ......
    break;
}

// bad
const type = typeof variable;
if (type === 'object') {
  // ......
} else if (type === 'number' 
  || type === 'boolean' 
  || type === 'string') {
  // ......
}
switch 语句
  • 每个 case 标签要么通过 break,return,throw 结束,要么通过注释直接跳过到下一标签。
  • 即使 default 标签中不包含逻辑,也不要省略。
// good
switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}

循环

不要在循环体中包含函数表达式,事先将函数提取到循环体外
// good
function clicker() {
  // ......
}
for (let i = 0, len = elements.length; i < len; i++) {
  const element = elements[i];
  addListener(element, 'click', clicker);
}

// bad
for (let i = 0, len = elements.length; i < len; i++) {
  const element = elements[i];
  addListener(element, 'click', function () {});
}
对循环内多次使用的不变值,在循环外用变量缓存
// good
const width = wrap.offsetWidth + 'px';
for (let i = 0, len = elements.length; i < len; i++) {
  const element = elements[i];
  element.style.width = width;
  // ......
}

// bad
for (let i = 0, len = elements.length; i < len; i++) {
  const element = elements[i];
  element.style.width = wrap.offsetWidth + 'px';
  // ......
}
对有序集合进行遍历时,缓存 length
// 正序遍历
for (let i = 0, len = elements.length; i < len; i++) {
  const element = elements[i];
  // ......
}
// 逆序遍历
let len = elements.length;
while (len--) {
  const element = elements[len];
  // ......
}
类型检测优先使用 typeof。对象类型检测使用 instanceof
// string
typeof variable === 'string'
// number
typeof variable === 'number'
// boolean
typeof variable === 'boolean'
// Function
typeof variable === 'function'
// Object
typeof variable === 'object'
// RegExp
variable instanceof RegExp
// Array
Array.isArray(variable)
// null
variable === null
// null or undefined
variable == null
// undefined
typeof variable === 'undefined'
转换成 string 时,使用 String 方法
// good
const str = String(num);

// bad
const str = num + '';
转换成 number 时,使用 Number 方法
// good
const number = Number(str);

// bad
const number = +str;
string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt,并且使用 parseInt 时,必须填写第二个参数来声明进制选项
// good
const width = '200px';
parseInt(width, 10);

// bad
const width = '200px';
parseInt(width);
转换成 boolean 时,使用 Boolean 方法
// good
const isRed = Boolean(red);

// bad
const isRed = !!red;
number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt
// good
const num = 3.14;
Math.ceil(num);

// bad
const num = 3.14;
parseInt(num, 10);
表示小数时,不要省略整数位或者小数位,比如 .5
// good
const num1 = 0.5;
const num2 = 2.0;
const num3 = -0.7;

// bad
const num1 = .5;
const num2 = 2.;
const num3 = -.7;

字符串

字符串开头和结束使用单引号 ’
const str = '我是一个字符串';
const html = '<div class="cls">拼接HTML可以省去双引号转义</div>';
使用字符串拼接的方式生成 HTML,需要根据语境进行合理的转义
// HTML 转义
const str = '<p>' + htmlEncode(content) + '</p>';
// HTML 转义
const str = '<input type="text" value="' + htmlEncode(value) + '">';
// URL 转义
const str = '<a href="/?key=' + htmlEncode(urlEncode(value)) + '">link</a>';
// JavaScript字符串 转义 + HTML 转义
const str = '<button οnclick="check(\'' + htmlEncode(strLiteral(name)) + '\')">提交</button>';
单行字符串的拼接或者字符串本身包含单引号建议使用字符串模板
const str = `'name': ${name}`;
不要使用 \ 进行多行接续
// good
const longString = 'This is a very long string that far exceeds the 80 '
  + 'column limit. It does not contain long stretches of spaces since '
  + 'the concatenated strings are cleaner.';

// bad
const longString = 'This is a very long string that far exceeds the 80 \
  column limit. It unfortunately contains long stretches of spaces due \
  to how the continued lines are indented.';
字符串内变量替换时,不要使用 2 层及以上的函数调用
// good
const fullName = getFullName(getFirstName(), getLastName());
const s = `Hello ${fullName}`;

// bad
const s = `Hello ${getFullName(getFirstName(), getLastName())}`;

对象

使用对象字面量 {} 创建新 Object
// good
const obj = {};

// bad
const obj = new Object();
对象创建时,如果一个对象的 属性 可以不添加引号,建议该 属性 不添加引号
// good
const info = {
  name: 'someone',
  age: 28,
  'x-y': 20,
};

// bad
const info = {
  'name': 'someone',
  'age': 28,
  'x-y': 20,
};
对象属性后需要加结束的逗号
// good
const foo = {
  name: 'someone',
  age: 28,
};
const bar = { name: 'someone', age: 28 };

// bad
const foo = {
  name: 'someone',
  age: 28
};
const bar = { name: 'someone', age: 28, };
不允许修改和扩展任何原生对象和宿主对象的原型
// 以下行为绝对禁止
String.prototype.trim = function () {
};
属性访问时,尽量使用 .。对于不符合 Identifier 的命名的情况(如后端接口返回的数据),可以使用[expr]形式。
info.age;
info.more_info;
info['more-info'];
定义对象时,如果键指向同名变量,需要进行缩写;能缩写的键需要写在不能缩写的键的前面
// good
const foo = {
  z,
  x: 1,
  y: 2,
};
// bad
const foo = {
  x: 1,
  y: 2,
  z: z,
};
const bar = {
  x: 1,
  y: 2,
  z,
};
定义方法时使用 MethodDefinition 语法,不使用 PropertyName: FunctionExpression 语法,不使用箭头函数语法
// good
const foo = {
  bar(x, y) {
    return x + y;
  }
};
const foo = {
  bar: hoc(function (x, y) {
    return x + y;
  })
};

// bad
const foo = {
  bar: function (x, y) {
    return x + y;
  }
};
const foo = {
  bar: (x, y) => x + y
};
使用 Object.keys 或 Object.entries 进行对象遍历
// good
for (let key of Object.keys(foo)) {
  const value = foo[key];
}

// good
for (const [key, value] of Object.entries(foo)) {
  // ...
}
尽量使用计算属性键在一个完整的字面量中完整地定义一个对象,避免对象定义后直接增加对象属性
// good
const MY_KEY = 'bar';
const foo = {
  [MY_KEY + 'Hash']: 123
};

// bad
const MY_KEY = 'bar';
const foo = {};
foo[MY_KEY + 'Hash'] = 123;
避免直接调用 Object.prototype 上面的方法,例如 hasOwnProperty propertyIsEnumerable 以及 isPrototypeOf
// good
console.log(Object.prototype.hasOwnProperty.call(object, key));
const has = Object.prototype.hasOwnProperty;
console.log(has.call(object, key));

// bad
console.log(object.hasOwnProperty(key));
进行对象浅拷贝时,使用 … 运算符。
// good
const obj = { ...oldObj };

// bad
const obj = Object.assign({}, oldObj);

数组和集合

使用数组字面量 [] 创建新数组。
// good
const arr = [];

// bad
const arr = new Array();
进行数组浅拷贝时,使用 … 运算符。
// good
const arr = [ ...oldArr ];

// bad
const arr = Array.from(oldArr);
不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法
清空数组使用 .length = 0
对数组进行连接操作时,使用数组展开语法
// good
foo = [...foo, newValue];
bar = [...bar, ...newValues];

// bad
foo = foo.concat(newValue);
bar = bar.concat(newValues);
多行数组最后一个元素后面加逗号
// good
const foo = [
  'first value',
  'second value',
];
const bar = [ 'first value', 'second value' ];

// bad
const foo = [
  'first value',
  'second value'
];
const bar = [ 'first value', 'second value', ];
数组遍历时,使用数组的原生方法以替代 for in 、 for of 用法
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 使用最恰当的方法
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;

// 数组所有元素 +1
// 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 使用最恰当的方法
const increasedByOne = numbers.map(num => num + 1);

函数

一个函数的长度控制在 50 行以内
// bad
function syncViewStateOnUserAction() {
  if (x.checked) {
    y.checked = true;
    z.value = '';
  } else {
    y.checked = false;
  }
  if (a.value) {
    warning.innerText = '';
    submitButton.disabled = false;
  } else {
    warning.innerText = 'Please enter it';
    submitButton.disabled = true;
  }
}
// good 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式:
function syncViewStateOnUserAction() {
  syncXStateToView();
  checkAAvailability();
}
function syncXStateToView() {
  y.checked = x.checked;
  if (x.checked) {
    z.value = '';
  }
}
function checkAAvailability() {
  if (a.value) {
    clearWarnignForA();
  } else {
    displayWarningForAMissing();
  }
}
一个函数的参数控制在 6 个以内,且对于超宽的函数签名进行换行处理;超过六个参数的情况要把参数封装成对象传入
// good
function doSomething(firstname, lastname, age) {
  // …
}
function doSomething(
  veryLongArgumentOne,
  veryLongArgumentTwo,
  veryLongArgumentThree,
  veryLongArgumentFour) {
  // …
}
function doSomething(person) {
  // …
}

// bad
function doSomething(firstname, lastname, age, veryLongArgumentOne, veryLongArgumentTwo, veryLongArgumentThree, veryLongArgumentFour) {
  // …
}
避免修改外部传入的参数对象
// good    
function info (person) {
  const thisInfo = {...person.info};
  thisInfo.xx = 'info';
  return thisInfo;
}
// bad    
function info (person) {
  const thisInfo = person.info;
  thisInfo.xx = 'info';
  return thisInfo;
}
使用变量默认语法代替基于条件判断的默认值声明
// good
function foo(text = 'hello') {
}

// bad
function foo(text) {
  text = (typeof text !== 'undefined') ? text : 'hello';
}
function foo(text) {
  text = text || 'hello';
}
不要使用 arguments 对象,应使用 …args 代替
// good
function foo(...args) {
  console.log(args.join(''));
}

// bad
function foo() {
  console.log([].join.call(arguments));
}
在适当的时候将闭包内大对象置为 null
一个函数被设计为需要 call 和 apply 的时候,不能是箭头函数

面向对象

使用 class 关键字定义一个类
// good
class TextNode {
  constructor(value, engine) {
    this.value = value;
    this.engine = engine;
  }
  clone() {
    return this;
  }
}
// bad
function TextNode(value, engine) {
  this.value = value;
  this.engine = engine;
}
TextNode.prototype.clone = function () {
  return this;
};
避免无用的构造函数
// good
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
    this.name = 'Rey';
  }
}
// bad
class Jedi {
  constructor() {}
  getName() {
    return this.name;
  }
}
class Rey extends Jedi {
  constructor(...args) {
    super(...args);
  }
}
使用 super 访问父类成员,而非父类的 prototype,不允许在子类中通过 super 修改父类成员。
// good
class TextNode extends Node {
  constructor(value, engine) {
    super(value);
    this.engine = engine;
  }
  setNodeValue(value) {
    super.setNodeValue(value);
    this.textContent = value;
  }
}

// bad
class TextNode extends Node {
  constructor(value, engine) {
    Node.apply(this, value);
    this.engine = engine;
  }
  setNodeValue(value) {
    Node.prototype.setNodeValue.call(this, value);
    this.textContent = value;
  }
}
class TextNode extends Node {
  constructor(value, engine) {
    super(value);
    super.engine = engine;
  }
}

ES Module

优先使用 ES Module
// good
import util from './util';
export default function () {};

// bad
const util = require('./util');
module.exports = function () {};
export 与内容定义放在一起
// good
export function foo() {
}

// bad
function foo() {
}
export { foo };
import 语句写在模块开始处,dynamic import 除外
// good
import { bar } from './bar';
function foo() {
  import('./foo').then(foo => console.log(foo));
  bar.work();
}

// bad
function foo() {
  bar.work();
}
import { bar } from './bar';
每一个文件以 default export 的形式暴露一个组件
// good
export default ComponentA;

// bad
const ComponentA = /* ... */;
const ComponentB = /* ... */;
export default {
  ComponentA,
  ComponentB
}
优先使用 named export
禁止同一个文件中同时使用 ES Module export 和 CommonJS export

CommonJS Module

对于服务端 Node.js 的项目,可以使用 CommonJS Module
禁止对 exports 赋值
// good
exports.foo = function foo() {
};
// good
module.exports = function foo() {
};

// bad
exports = function foo() {
};

参考: https://styleguide.bytedance.net/#/zh-cn/js?id=_3131-%e5%bb%ba%e8%ae%ae-%e5%9b%9e%e8%b0%83%e5%87%bd%e6%95%b0%e7%9a%84%e5%b5%8c%e5%a5%97%e4%b8%8d%e5%be%97%e8%b6%85%e8%bf%87-3-%e5%b1%82

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值