编码规范模板javascript
2019-02-27 22:16:49 weixin_34306593 阅读数 2

原文链接

命名规范

  • 标准变量采用驼峰式命名
  • ‘ID’在变量名中全大写
  • 常量全大写,用下划线连接构造函数,大写第一个字母
  • jquery对象必须以’$’开头命名
let thisIsMyName;
let goodID;
let reportURL;
let AndroidVersion;
let iOSVersion;
let MAX_COUNT = 10;
function Person(name) {
this.name = name;
}
// not good
let body = $('body');
// good
let $body = $('body');

局部变量命名规范

  • s:表示字符串。例如:sName,sHtml;
  • n:表示数字。例如:nPage,nTotal;
  • b:表示逻辑。例如:bChecked,bHasLogin;
  • a:表示数组。例如:aList,aGroup;
  • r:表示正则表达式。例如:rDomain,rEmail;
  • f:表示函数。例如:fGetHtml,fInit;
  • o:表示以上未涉及到的其他对象,例如:oButton,oDate;

函数命名

小驼峰命名法,可使用常见动词约定:
  • can 判断是否可执行某个动作,函数返回一个布尔值。true:可执行;false:不可执行
  • has 判断是否含有某个值, 函数返回一个布尔值。true:含有此值;false:不含有此值
  • is 判断是否为某个值,函数返回一个布尔值。true:为某个值;false:不为某个值
  • get 获取某个之,函数返回一个非布尔值
  • set 设置某个值,无返回值、返回是否设置成功或者返回链式对象load 加载某些数据,无返回值或者返回是否加载完成的结果
// 是否可阅读
function canRead() {
 return true;
}
// 获取名称
function getName() {
 return this.name;
}

引用 References

对所有的引用使用 const ;不要使用 var。

eslint: prefer-const, no-const-assign

这可以确保你无法对引用重新分配,重新分配可能会导致 bug 和难以理解的代码。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;

如果你一定需要可变动的引用,使用 let 代替 var 。

eslint: no-var jscs: disallowVar

// bad
var count = 1;
if (true) {
count += 1;
}
// good, 使用 let.
let count = 1;
if (true) {
count += 1;
}

对象Objects

使用字面量语法创建对象。

eslint: no-new-object

// bad
const item = new Object();
// good
const item = {};

当创建带有动态属性名称的对象时使用计算的属性名称。

它们允许你在一个地方定义一个对象的所有属性。

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,
};

使用对象方法速记语法。

eslint: object-shorthand jscs: requireEnhancedObjectLiterals

// bad
const atom = {
value: 1,
addValue: function (value) {
    return atom.value + value;
  },
};
// good
const atom = {
value: 1,
addValue(value) {
    return atom.value + value;
  },
};

使用对象属性速记语法。

eslint: object-shorthand jscs: requireEnhancedObjectLiterals

const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
  lukeSkywalker,
};

将速记属性分组写在对象声明的开始处

更容易看出哪些属性在使用速记语法
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,
};

只用引号引无效标识符的属性。

eslint: quote-props jscs: disallowQuotedKeysInObjects

一般来说,我们认为比较容易阅读。它改进了语法高亮显示,并且更容易被许多JS引擎优化。
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};

用对象展开操作符浅复制对象,优先于Object.assign 。使用对象剩余操作符来获得一个省略某些属性的新对象。

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); //  `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 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

数组 Arrays

使用字面量创建数组。

eslint: no-array-constructor

// bad
const items = new Array();
// good
const items = [];

使用数组展开操作符 … 复制数组。

// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];

使用展开操作符 … 代替 Array.from,来将一个类数组(array-like) 对象转换成数组。

const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];

实用 Array.from 代替展开操作符 … 来映射迭代,因为它避免了创建媒介数组。

// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);

解构 Destructuring

当访问和使用对象的多个属性时,请使用对象解构。

eslint: prefer-destructuring jscs: requireObjectDestructuring

// 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`;
}

使用数组解构。

eslint: prefer-destructuring jscs: requireArrayDestructuring

const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;

使用对象解构来实现多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn

您可以随着时间的推移添加新的属性或更改排序,而不会改变调用时的位置。
// bad
function processInput(input) {
  return [left, right, top, bottom];
}
// 调用者需要考虑返回数据的顺序
const [left, __, top] = processInput(input);
// good
function processInput(input) {
  return { left, right, top, bottom };
}
// 调用者只选择他们需要的数据
const { left, top } = processInput(input);

字符串 Strings

字符串使用单引号 ‘’。

eslint: quotes jscs: validateQuoteMarks

// bad
const name = "Capt. Janeway";
// bad - 模板字面量应该包含插值或换行符
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';

以编程方式构建字符串时,请使用模板字符串而不是字符串连接。

eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings

// 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}?`;
}

永远不要在字符串上使用 eval() ,它会打开太多的漏洞。

eslint: no-eval

函数 Functions

使用命名函数表达式而不是函数声明。

eslint: func-style jscs: disallowFunctionDeclarations

函数声明很容易被提升(Hoisting),这对可读性和可维护性来说都是不利的;
/ bad
function foo() {
  // ...
}
// bad
const foo = function () {
  // ...
};
// good 
// 用明显区别于变量引用调用的词汇命名
const short = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
};

用圆括号包裹立即调用函数表达式 (IIFE)。

eslint: wrap-iife jscs: requireParenthesesAroundIIFE

一个立即调用函数表达式是一个单独的单元 – 将函数表达式包裹在括号中,后面再跟一个调用括号,这看上去很紧凑。
// 立即调用函数表达式 (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());

不要使用 arguments。可以选择 rest 语法 … 替代。

使用 … 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}
// good
function concatenateAll(...args) {
  return args.join('');
}

使用默认参数语法,而不要使用一个变化的函数参数

// really bad
function handleThings(opts) {
  // 更加糟糕: 如果参数 opts 是 falsy(假值) 的话,它将被设置为一个对象,
  // 这可能是你想要的,但它可以引起一些小的错误。
  opts = opts || {};
  // ...
}
// still bad
function handleThings(opts) {
  if (opts === void 0) {
  opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
  // ...
}

始终将默认参数放在最后。

// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}

隔开函数签名,括号两边用空格隔开。

// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};

不要改变参数。

eslint: no-param-reassign

操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。(对象是引用类型)
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

箭头函数 Arrow Functions

当您必须使用匿名函数(如在传递一个内联回调时),请使用箭头函数表示法。

eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions

它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。
// bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});
// bad
[1, 2, 3].map( _ => {
  
  return 0;
});
// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});
// good
[1, 2, 3].map(() => {
  
  return 0;
});

如果函数体由一个返回无副作用(side effect)的expression(表达式)的单行语句组成,那么可以省略大括号并使用隐式返回。否则,保留大括号并使用 return 语句。

// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1;
return `A string containing the nextNumber.`;
});
// good
[1, 2, 3].map(number => `A string containing the number.`);

如果表达式跨多行,将其包裹在括号中,可以提高可读性。

// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
  httpMagicObjectWithAVeryLongName,
  httpMethod,
  )
);
// good
['get', 'post', 'put'].map(httpMethod => (
  Object.prototype.hasOwnProperty.call(
  httpMagicObjectWithAVeryLongName,
  httpMethod,
  )
));

如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号。

// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
`A long string with the number. It’s so long that we don’t want it to take up space on the .map line!`
));
// 总是添加()
// bad
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
});
// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

避免使用比较运算符(< =, >=)时,混淆箭头函数语法(=>)。

// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item;
  return height > 256 ? largeSize : smallSize;
};

类 Classes & 构造函数 Constructors

总是使用 *class。避免直接操作 prototype 。

// 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);
    return value;
  }
}

使用 extends 继承。

因为 extends 是一个内置的原型继承方法并且不会破坏 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];
  }
}

如果没有指定,类有一个默认的构造函数。一个空的构造函数或者只是委托给父类则不是必须的。

eslint: no-useless-constructor

// 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';
  }
}

避免重复类成员。

eslint: no-dupe-class-members

// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}
// good
class Foo {
  bar() { return 1; }
}
// good
class Foo {
  bar() { return 2; }
}

模块 Modules

总是使用模块 (import/export) 而不是其他非标准模块系统。

// 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;

不要使用通配符 import(导入)。

这样能确保你只有一个默认 export(导出)。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';

不要从 import(导入) 中直接 export(导出)。

虽然一行代码简洁明了,但有一个明确的 import(导入) 方法和一个明确的 export(导出) 方法,使事情能保持一致。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

一个地方只在一个路径中 import(导入) 。

// bad
import foo from 'foo';
// … 其他一些 imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
  named1,
  named2,
} from 'foo';

不要 export(导出) 可变绑定。

eslint: import/no-mutable-exports

一般应该避免可变性,特别是在导出可变绑定时。虽然一些特殊情况下,可能需要这种技术,但是一般而言,只应该导出常量引用。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };

在只有单个导出的模块中,默认 export(导出) 优于命名 export(导出)。

eslint: import/prefer-default-export

为了鼓励更多的文件只有一个 export(导出),这有利于模块的可读性和可维护性。
// bad
export function foo() {}
// good
export default function foo() {}

将所有 import 导入放在非导入语句的上面。

eslint: import/first

由于 import 被提升,保持他们在顶部,防止意外的行为。
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();

多行导入应该像多行数组和对象字面量一样进行缩进。

// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';

属性 Properties

使用 点语法(.) 来访问对象的属性。

eslint: dot-notation jscs: requireDotNotation

const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;

当通过变量访问属性时使用中括号 []。

const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');

求幂时使用求幂运算符 ** 。

eslint: no-restricted-properties.

// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;

变量 Variables

总是使用 const 或 let 来声明变量。 不这样做会导致产生全局变量。 我们希望避免污染全局命名空间。

eslint: no-undef prefer-const

// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();

将所有的 const 和 let 分组 。

当你需要把已分配的变量分配给一个变量时非常有用
// 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;

变量不要链式赋值。

eslint: no-multi-assign

链接变量赋值会创建隐式全局变量。
// bad
(function example() {
// JavaScript 将其解析为
// let a = ( b = ( c = 1 ) );
// let关键字只适用于变量a;
// 变量b和c变成了全局变量。
let a = b = c = 1;
}());
console.log(a); // 抛出 ReferenceError(引用错误)
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // 抛出 ReferenceError(引用错误)
console.log(b); // 抛出 ReferenceError(引用错误)
console.log(c); // 抛出 ReferenceError(引用错误)
// 同样适用于 `const`

避免使用一元递增和递减运算符(++, -–)。

根据 eslint 文档,一元递增和递减语句会受到自动插入分号的影响,并可能导致应用程序中的值递增或递减,从而导致无提示错误。使用像 num += 1 而不是 num++ 或 num ++ 这样的语句来改变你的值也更具有表现力。不允许一元递增和递减语句也会阻止您无意中预先递增/递减值,这也会导致程序中的意外行为。
// 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;

比较运算符 Comparison Operators 和 等号 Equality

使用 === 和 !== 优先于 == 和 !=。

eslint: eqeqeq

对于布尔值使用简写,但对于字符串和数字使用显式比较。

// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}

在 case 和 default 子句中,使用大括号来创建包含词法声明的语句块(例如 let, const, function, 和 class).

eslint: no-case-declarations

// 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 {}
  }
}

三元表达式不应该嵌套,通常写成单行表达式。

eslint: no-nested-ternary

// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// 拆分成2个分离的三元表达式
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

避免不必要的三元表达式语句。

eslint: no-unneeded-ternary

/ bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;

当运算符混合在一个语句中时,请将其放在括号内。混合算术运算符时,不要将 * 和 % 与 + , -,,/ 混合在一起。

eslint: no-mixed-operators

这可以提高可读性,并清晰展现开发者的意图。
/ bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
if (a || b && c) {
return 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;

代码块 Blocks

使用大括号包裹所有的多行代码块。

eslint: nonblock-statement-body-position

// bad
if (test)
  return false;
// good
if (test) return false;
// good
if (test) {
  return false;
}
// bad
function foo() { return false; }
// good
function bar() {
  return false;
}

如果通过 if 和 else 使用多行代码块,把 else 放在 if 代码块闭合括号的同一行。

eslint: brace-style

// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}
// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}

如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return 。

eslint: no-else-return

// bad
function foo() {
  if (x) {
    return x;
  } else {
    return y;
  }
}
// bad
function cats() {
  if (x) {
    return x;
  } else if (y) {
    return y;
  }
}
// bad
function dogs() {
  if (x) {
    return x;
  } else {
  if (y) {
    return y;
  }
}
}
// good
function foo() {
  if (x) {
    return x;
  }
return y;
}
// good
function cats() {
  if (x) {
    return x;
  }
  if (y) {
    return y;
  }
}
//good
function dogs(x) {
  if (x) {
    if (z) {
      return y;
  }
} else {
  return z;
  }
}

控制语句 Control Statements

如果您的控制语句(if, while 的)太长或超过最大行长度,那么每个(分组)条件可以放单独一行。逻辑运算符应该放在每行起始处。

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
 thing1();
}
// bad
if (foo === 123 &&
  bar === 'abc') {
  thing1();
}
// bad
if (foo === 123
  && bar === 'abc') {
  thing1();
}
// bad
if (
  foo === 123 &&
  bar === 'abc'
) {
  thing1();
}
// good
if (
  foo === 123
  && bar === 'abc'
) {
  thing1();
}
// good
if (
  (foo === 123 || bar === "abc")
  && doesItLookGoodWhenItBecomesThatLong()
  && isThisReallyHappening()
) {
  thing1();
}
// good
if (foo === 123 && bar === 'abc') {
  thing1();
}

注释 Comments

多行注释使用 /* … /。

/**
* @param {Grid} grid 需要合并的Grid
* @param {Array} cols 需要合并列的Index(序号)数组;从0开始计数,序号也包含。
* @param {Boolean} isAllSome 是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样
* @return void
* @author 单志永 2018/11/8
*/
function mergeCells(grid, cols, isAllSome) {
    // Do Something
}

单行注释使用 // 。将单行注释放在需注释的语句上方。在注释之前放置一个空行,除非它位于代码块的第一行。

// 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;
}

所有注释符和注释内容用一个空格隔开,让它更容易阅读。

eslint: spaced-comment

// 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;
}

给注释增加 FIXME 或 TODO 的前缀,可以帮助其他开发者快速了解这个是否是一个需要重新复查的问题,或是你正在为需要解决的问题提出解决方案。这将有别于常规注释,因为它们是可操作的。使用 FIXME – need to figure this out 或者 TODO – need to implement。

使用 // FIXME: 来标识需要修正的问题。注:如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。

lass Calculator extends Abacus {
  constructor() {
    super();
    // FIXME: shouldn’t use a global here
    total = 0;
  }
}

使用 // TODO: 来标识需要实现的问题。注:如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明。

class Calculator extends Abacus {
  constructor() {
    super();
    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}

空格 Whitespace

使用 2 个空格作为缩进

// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}

在大括号前放置 1 个空格。

eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements

// bad
function test(){
  console.log('test');
}
// good
function test() {
  console.log('test');
}
// bad
dog.set('attr',{
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});

在控制语句(if、while 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。

eslint: keyword-spacing jscs: requireSpaceAfterKeywords

// bad
if(isJedi) {
  fight ();
}
// good
if (isJedi) {
  fight();
}
// bad
function fight () {
  console.log ('Swooosh!');
}
// good
function fight() {
  console.log('Swooosh!');
}

使用空格把运算符隔开。

eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperators, requireSpaceAfterBinaryOperators

// bad
const x=y+5;
// good
const x = y + 5;

在文件末尾插入一个空行。

eslint: eol-last

// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

// good
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

长方法链式调用时使用缩进(2个以上的方法链式调用)。使用一个点 . 开头,强调该行是一个方法调用,不是一个新的声明。

eslint: newline-per-chained-call no-whitespace-before-property

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();

// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);

// good
const leds = stage.selectAll('.led').data(data);

不要在圆括号内加空格。

// bad
function bar( foo ) {
  return foo;
}
// good
function bar(foo) {
  return foo;
}
// bad
if ( foo ) {
  console.log(foo);
}
// good
if (foo) {
  console.log(foo);
}

不要在中括号内添加空格。

eslint: array-bracket-spacing jscs: disallowSpacesInsideArrayBrackets

// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);

在大括号内添加空格

// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };

类型转换 Type Casting & Coercion

在声明语句的开始处就执行强制类型转换.

字符串:

eslint: no-new-wrappers

// => this.reviewScore = 9;
// bad
const totalScore = new String(this.reviewScore); // typeof totalScore 是 "object" 而不是 "string"
// bad
const totalScore = this.reviewScore + ''; // 调用 this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // 不能保证返回一个字符串
// good
const totalScore = String(this.reviewScore);

使数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串。

eslint: radix no-new-wrappers

const inputValue = '4';
// bad
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// bad
const val = parseInt(inputValue);
// good
const val = Number(inputValue);
// good
const val = parseInt(inputValue, 10);

布尔值:

eslint: no-new-wrappers

const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;

命名规则 Naming Conventions

避免使用单字母名称。使你的命名具有描述性。

eslint: id-length

// bad
function q() {
// ...
}
// good
function query() {
// ...
}

当命名对象,函数和实例时使用驼峰式命名。

eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers

// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}

当命名构造函数或类的时候使用 PascalCase 式命名,(注:即单词首字母大写)。

eslint: new-cap

// 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',
});

当 导出(export) 一个默认函数时使用驼峰式命名。你的文件名应该和你的函数的名字一致。

function makeStyleGuide() {
// ...
}
export default makeStyleGuide;

当导出一个 构造函数 / 类 / 单例 / 函数库 / 纯对象时使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。

const AirbnbStyleGuide = {
  es6: {
    },
};
export default AirbnbStyleGuide;

存取器 Accessors

属性的存取器函数不是必须的。

別使用 JavaScript 的 getters/setters,因为它们会导致意想不到的副作用,而且很难测试,维护和理解。相反,如果要使用存取器函数,使用 getVal() 及 setVal(‘hello’)。

// bad
class Dragon {
  get age() {
    // ...
  }
  set age(value) {
    // ...
  }
}
// good
class Dragon {
  getAge() {
    // ...
  }
  setAge(value) {
    // ...
  }
}

如果属性/方法是一个 boolean, 使用 isVal() 或 hasVal() 方法。

// bad
if (!dragon.age()) {
  return false;
}
// good
if (!dragon.hasAge()) {
  return false;
}

)

更多前端资源请关注微信公众号“前端陌上寒”

2017-11-01 10:55:50 ONROAD0612 阅读数 293

一、代码规范包含的面

 

变量和函数的命名规则
空格,缩进,注释的使用规则。

 

其他常用规范……

二、编码规范的作用

 

规范的代码可以更易于阅读与维护。

 

代码规范一般在开发前规定,可以跟你的团队成员来协商设置。

三、编码规范具体内容

(1)变量名

 

变量名推荐使用驼峰法来命名(camelCase):
firstName = "John";
lastName = "Doe";
price = 19.90;
tax = 0.20;
fullPrice = price + (price * tax);

 

(2)空格与运算符

 

通常运算符 ( = + - * / ) 前后需要添加空格:
实例:
var x = y + z;
var values = ["Volvo", "Saab", "Fiat"];

 

(3)代码缩进

 

通常使用 4 个空格符号来缩进代码块:
函数:
function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}
注意:不推荐使用 TAB 键来缩进,因为不同编辑器 TAB 键的解析不一样。

 

(4)语句规则

A、简单语句的通用规则:

 

一条语句通常以分号作为结束符。
实例:
var values = ["Volvo", "Saab", "Fiat"];
var person = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};

 

B、复杂语句的通用规则:

 

将左花括号放在第一行的结尾。
左花括号前添加一空格。
将右花括号独立放在一行。
不要以分号结束一个复杂的声明。
函数:
function toCelsius(fahrenheit) {
    return (5 / 9) * (fahrenheit - 32);
}
循环:
for (i = 0; i < 5; i++) {
    x += i;
}
条件语句:
if (time < 20) {
    greeting = "Good day";
} else {
    greeting = "Good evening";
}

 

C、对象规则

 

对象定义的规则:
将左花括号与类名放在同一行。
冒号与属性值间有个空格。
字符串使用双引号,数字不需要。
最后一个属性-值对后面不要添加逗号。
将右花括号独立放在一行,并以分号作为结束符号。
实例:
var person = {
    firstName: "John",
    lastName: "Doe",
    age: 50,
    eyeColor: "blue"
};
短的对象代码可以直接写成一行:
实例:
var person = {firstName:"John", lastName:"Doe", age:50, eyeColor:"blue"};
每行代码字符小于 80
为了便于阅读每行字符建议小于数 80 个。
如果一个 JavaScript 语句超过了 80 个字符,建议在 运算符或者逗号后换行。
实例:
document.getElementById("demo").innerHTML =
    "Hello Runoob.";

 

(5)命名规则

 

一般很多代码语言的命名规则都是类似的,例如:
变量和函数为驼峰法( camelCase)
全局变量为大写 (UPPERCASE )
常量 (如 PI) 为大写 (UPPERCASE )
变量命名你是否使用这几种规则: hyp-hens, camelCase, 或 under_scores ?
HTML 和 CSS 的横杠(-)字符:
HTML5 属性可以以 data- (如:data-quantity, data-price) 作为前缀。
CSS 使用 - 来连接属性名 (font-size)。
注意: 通常在 JavaScript 中被认为是减法,所以不允许使用。

 

A、下划线:

 

很多程序员比较喜欢使用下划线(如:date_of_birth), 特别是在 SQL 数据库中。
PHP 语言通常都使用下划线。
帕斯卡拼写法(PascalCase):
帕斯卡拼写法(PascalCase) 在 C 语言中语言较多。

 

B、驼峰法:

 

JavaScript 中通常推荐使用驼峰法,jQuery 及其他 JavaScript 库都使用驼峰法。
注意:变量名不要以 $ 作为开始标记,会与很多 JavaScript 库冲突。
HTML 载入外部 JavaScript 文件
使用简洁的格式载入 JavaScript 文件 ( type 属性不是必须的):
<script src="myscript.js">
使用 JavaScript 访问 HTML 元素
一个糟糕的 HTML 格式可能会导致 JavaScript 执行错误。
以下两个 JavaScript 语句会输出不同结果:
实例
var obj = getElementById("Demo")
var obj = getElementById("demo")
HTML 与 JavaScript 尽量使用相同的命名规则。
文件扩展名
HTML 文件后缀可以是 .html (或r .htm)。
CSS 文件后缀是 .css 。
JavaScript 文件后缀是 .js 。

 

C、使用小写文件名

大多 Web 服务器 (Apache, Unix) 对大小写敏感: london.jpg 不能通过 London.jpg 访问。
其他 Web 服务器 (Microsoft, IIS) 对大小写不敏感: london.jpg 可以通过 London.jpg 或 london.jpg 访问。
你必须保持统一的风格,我们建议统一使用小写的文件名。

2017-02-08 18:30:00 weixin_34061482 阅读数 3

此文档主要是为了解决公司 现阶段 Web 前端 JavaScript 编码不规范,Bug频出,难以维护的问题。

简介

此文档主要包含四个部分,分别是目录及文件结构,编码风格,注释规范和兼容性规范;下面分小节分别介绍。

目录及文件结构

此部分仅约束自己编写的JavaScript代码,使用第三的库时不在此约束范围内。

  1. 所有自己编写的JS文件以及存放JS文件的子目录都必须位于目录 “$projectRoot/htdocs/TPL/$tplName/js/” 下;
  2. 所有通用功能的代码,必须封装成模块;一个模块一个文件,文件名必须有意义(可以是模块名);
  3. 松散的业务性代码不要单独写到一个JS文件中,应该写在HTML文件中的 script 标签中;
  4. 复杂度较高且耦合多个业务逻辑形成的一组功能可以封闭成一个模块,独立写到一个JS文件中;(如:课程教案的编辑器)

编码风格

缩进

在JS中的缩进与《基本的程序编码格式要求》中对缩进的要求相同。

  1. 所有的缩进都采用空格,不允许使用 Tab 缩进
  2. 所有的缩进尺寸均为“两个空格”
  3. 空行需要保留缩进
  4. 当一个逻辑行需要分成多个自然行来写时,每次换行需要“缩进”两个空格

空格

小括号前的空格

所有小括号前不能有空格,包含但不限于以下情况。

  1. 方法声明的小括号
  2. 方法调用的小括号
  3. if 语句中的小括号
  4. for 语句的小括号
  5. while 语句的小括号
  6. switch 语句的小括号
  7. try 语句的小括号
  8. catch 语句的小括号
  9. 其他所有

操作符两侧的空格

在操作符的两侧,有些地方需要使用空格,有些地方不能使用空格,具体要求如下:

  1. 赋值操作符(=,+=,……):两侧都要有空格
  2. 逻辑操作符(&&,||):两侧都要有空格
  3. 关系比较操作符 (<,>,<=,>=,==,!=):两侧都要有空格
  4. 位运算操作符(&,|,^):两侧都要有空格
  5. 算术运算符(+,-,*,/,%):两侧都要有空格
  6. 移位运算操作符(<<,>>,>>>):两侧都要有空格
  7. 三元操作符(?,:):两侧都要有空格
  8. 一元运算操作符(++,--):无空格
  9. 属性运算符(.):无空格

左花括号之前的空格

所有左花括号之前不需要有空格,包括但不限于以下情况:

  1. 函数定义时代码块的左花括号
  2. if 语句的左花括号
  3. else 语句的左花括号
  4. for 语句的左花括号
  5. while 语句的左花括号
  6. do 语句的左花括号
  7. switch 语句的左花括号
  8. try 语句的左花括号
  9. catch 语句的左花括号

关键字之前的空格

所有关键字之前不需要加额外的空格(缩进除外),包括以下情况:

  1. 'else' 关键字
  2. 'while' 关键字
  3. 'catch' 关键字

括号内侧的空格

所有括号(小括号,中括号,花括号)内侧都不需要空格,包括但不限于以下情况:

  1. 所有中括号
  2. 函数声明的小括号
  3. 函数调用的小括号
  4. if 小括号
  5. for 小括号
  6. while 小括号
  7. switch 小括号
  8. catch 小括号
  9. 所有花括号

其他地方的空格

这是一些比较零散的内容,具体如下:

  1. 逗号前:无
  2. 逗号后:有
  3. 分号前:无
  4. 属性分隔符“:”前:无
  5. 属性分隔符“:”后:无

其他需要注意的项

  1. 所有JS代码中,一条“语句”的结尾不允许省略分号;
  2. 暂时不允许使用ES6的语法;

注释规范

JSDoc 规范中常用的注释标签简介

  1. @author - 指定项目的作者;(@author <name> [<emailAddress>])
  2. @param - 记录传递给一个函数的参数
  3. @returns - 记录一个函数的返回值
  4. @description - 描述一个标识;(@description <some description>)
  5. @constructs - 这个函数成员将成为类的构造函数;(@constructs [<name>])
  6. @module - 记录一个 JavaScript 模块。(@module [[{<type>}] <moduleName>])
  7. @property - 记录一个对象的属性;(@property {<type>} <moduleName> )
  8. @readonly - 标记为只读的;
  9. @see - 更多详细信息请参阅其他一些文档。(@see {@link http://www.xxxx.com|xxxx})
  10. @throws - 说明可能会被抛出什么样的错误。(@throws {<type>} free-form description)
  11. @todo - 记录一个将要完成的任务。(@todo text describing thing to do.)
  12. @type - 记录一个对象的类型。(@type {typeName})
  13. @version - 描述版本信息。(@version 1.2.3)
  14. @link - 链接到文档中的另一个项目。({@link namepathOrURL})

在JS代码中对注释的要求

  1. 所有模块必须有详细的符合JSDoc规范的注释;
  2. 所有函数必须有详细的符合 JSDoc规范的注释;
  3. 分支和循环前必须有相应的说明性注释;
  4. 重要变量也需要给出注释,说明用途。
  5. 所有直接定义的对象的(var a={})必须有详细的符合JSDoc规范的注释;

在定义一个模块时必须要加的注释项:

  1. 作者 @author
  2. 模块 @module
  3. 版本 @version
  4. 详细的功能说明 @description
  5. 修订时间 - 修订内容 - 修订人 @description
  6. …… @description

在定义一个函数时必须要加的注释项:

  1. 作者 @author
  2. 版本 @version
  3. 参数说明 @param
  4. 返回值说明 @returns
  5. 详细的功能说明 @description
  6. 修订时间 - 修订内容 - 修订人 @description
  7. …… @description

在定义一个对象时必须要加的注释项:

  1. 作者 @author
  2. 属性 @property
  3. 详细的功能说明 @description
  4. 修订时间 - 修订内容 - 修订人 @description
  5. …… @description

兼容性规范

  1. 在选择使用第三方库时需要注意库的兼容性,必须保证所使用的第三方库能够很好的兼容Safari,Firefox,Chrome,IE10+ 和 360浏览器。
  2. 自己写的JavaScript代码也必须要兼容Safari,Firefox,Chrome,IE10+ 和 360浏览器。
  3. 非必要情况下,请尽量不要直接使用底接口;如:XMLHttpRequest,FileApi,CanvasApi等;
  4. 所有使用HTML5特性的代码,都必须提供在不支持HTML5浏览器上的兼容性方案;(如:提供替代方案,提示升级或下载兼容性更好的浏览器等)
  5. 对于 Safari 浏览器兼容性的测试必须在 OS X 系统下进行。
2017-06-18 21:00:00 weixin_33725807 阅读数 4

JavaScript编码规范

1 前言

2 代码风格

2.1 文件

2.2 结构

2.2.1 缩进

2.2.2 空格

2.2.3 换行

2.2.4 语句

2.3 命名

2.4 注释

2.4.1 单行注释

2.4.2 多行注释

2.4.3 文档化注释

2.4.4 类型定义

2.4.5 文件注释

2.4.6 命名空间注释

2.4.7 类注释

2.4.8 函数/方法注释

2.4.9 事件注释

2.4.10 常量注释

2.4.11 复杂类型注释

2.4.12 AMD 模块注释

2.4.13 细节注释

3 语言特性

3.1 变量

3.2 条件

3.3 循环

3.4 类型

3.4.1 类型检测

3.4.2 类型转换

3.5 字符串

3.6 对象

3.7 数组

3.8 函数

3.8.1 函数长度

3.8.2 参数设计

3.8.3 闭包

3.8.4 空函数

3.9 面向对象

3.10 动态特性

3.10.1 eval

3.10.2 动态执行代码

3.10.3 with

3.10.4 delete

3.10.5 对象属性

4 浏览器环境

4.1 模块化

4.1.1 AMD

4.1.2 define

4.1.3 require

4.2 DOM

4.2.1 元素获取

4.2.2 样式获取

4.2.3 样式设置

4.2.4 DOM 操作

4.2.5 DOM 事件

1 前言

JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护。

虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。

2 代码风格

2.1 文件

[建议] JavaScript 文件使用无 BOMUTF-8 编码。

解释:

UTF-8 编码具有更广泛的适应性。BOM 在使用程序或工具处理文件时可能造成不必要的干扰。

[建议] 在文件结尾处,保留一个空行。

2.2 结构

2.2.1 缩进

[强制] 使用 4 个空格做为一个缩进层级,不允许使用 2 个空格 或 tab 字符。
[强制] switch 下的 casedefault 必须增加一个缩进层级。

示例:

// 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...

}

2.2.2 空格

[强制] 二元运算符两侧必须有一个空格,一元运算符与操作对象之间不允许有空格。

示例:

var 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
var obj = {
    a: 1,
    b: 2,
    c: 3
};

// bad
var obj = {
    a : 1,
    b:2,
    c :3
};
[强制] 函数声明、具名函数表达式、函数调用中,函数名和 ( 之间不允许有空格。

示例:

// good
function funcName() {
}

var funcName = function funcName() {
};

funcName();

// bad
function funcName () {
}

var funcName = function funcName () {
};

funcName ();
[强制] ,; 前不允许有空格。

示例:

// good
callFunc(a, b);

// bad
callFunc(a , b) ;
[强制] 在函数调用、函数声明、括号表达式、属性访问、if / for / while / switch / catch 等语句中,()[] 内紧贴括号部分不允许有空格。

示例:

// good

callFunc(param1, param2, param3);

save(this.list[this.indexes[i]]);

needIncream && (variable += increament);

if (num > list.length) {
}

while (len--) {
}


// bad

callFunc( param1, param2, param3 );

save( this.list[ this.indexes[ i ] ] );

needIncreament && ( variable += increament );

if ( num > list.length ) {
}

while ( len-- ) {
}
[强制] 单行声明的数组与对象,如果包含元素,{}[] 内紧贴括号部分不允许包含空格。

解释:

声明包含元素的数组与对象,只有当内部元素的形式较为简单时,才允许写在一行。元素复杂的情况,还是应该换行书写。

示例:

// good
var arr1 = [];
var arr2 = [1, 2, 3];
var obj1 = {};
var obj2 = {name: 'obj'};
var obj3 = {
    name: 'obj',
    age: 20,
    sex: 1
};

// bad
var arr1 = [ ];
var arr2 = [ 1, 2, 3 ];
var obj1 = { };
var obj2 = { name: 'obj' };
var obj3 = {name: 'obj', age: 20, sex: 1};
[强制] 行尾不得有多余的空格。

2.2.3 换行

[强制] 每个独立语句结束后必须换行。
[强制] 每行不得超过 120 个字符。

解释:

超长的不可分割的代码允许例外,比如复杂的正则表达式。长字符串不在例外之列。

[强制] 运算符处换行时,运算符必须在新行的行首。

示例:

// good
if (user.isAuthenticated()
    && user.isInRole('admin')
    && user.hasAuthority('add-admin')
    || user.hasAuthority('delete-admin')
) {
    // Code
}

var result = number1 + number2 + number3
    + number4 + number5;


// bad
if (user.isAuthenticated() &&
    user.isInRole('admin') &&
    user.hasAuthority('add-admin') ||
    user.hasAuthority('delete-admin')) {
    // Code
}

var result = number1 + number2 + number3 +
    number4 + number5;
[强制] 在函数声明、函数表达式、函数调用、对象创建、数组创建、for语句等场景中,不允许在 ,; 前换行。

示例:

// good
var obj = {
    a: 1,
    b: 2,
    c: 3
};

foo(
    aVeryVeryLongArgument,
    anotherVeryLongArgument,
    callback
);


// bad
var 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;
}
[建议] 在语句的行长度超过 120 时,根据逻辑条件合理缩进。

示例:

// 较复杂的逻辑条件组合,将每个条件独立一行,逻辑运算符放置在行首进行分隔,或将部分逻辑按逻辑组合进行分隔。
// 建议最终将右括号 ) 与左大括号 { 放在独立一行,保证与 if 内语句块能容易视觉辨识。
if (user.isAuthenticated()
    && user.isInRole('admin')
    && user.hasAuthority('add-admin')
    || user.hasAuthority('delete-admin')
) {
    // Code
}

// 按一定长度截断字符串,并使用 + 运算符进行连接。
// 分隔字符串尽量按语义进行,如不要在一个完整的名词中间断开。
// 特别的,对于HTML片段的拼接,通过缩进,保持和HTML相同的结构。
var html = '' // 此处用一个空字符串,以便整个HTML片段都在新行严格对齐
    + '<article>'
    +     '<h1>Title here</h1>'
    +     '<p>This is a paragraph</p>'
    +     '<footer>Complete</footer>'
    + '</article>';

// 也可使用数组来进行拼接,相对 + 更容易调整缩进。
var html = [
    '<article>',
        '<h1>Title here</h1>',
        '<p>This is a paragraph</p>',
        '<footer>Complete</footer>',
    '</article>'
];
html = html.join('');

// 当参数过多时,将每个参数独立写在一行上,并将结束的右括号 ) 独立一行。
// 所有参数必须增加一个缩进。
foo(
    aVeryVeryLongArgument,
    anotherVeryLongArgument,
    callback
);

// 也可以按逻辑对参数进行组合。
// 最经典的是baidu.format函数,调用时将参数分为“模板”和“数据”两块
baidu.format(
    dateFormatTemplate,
    year, month, date, hour, minute, second
);

// 当函数调用时,如果有一个或以上参数跨越多行,应当每一个参数独立一行。
// 这通常出现在匿名函数或者对象初始化等作为参数时,如setTimeout函数等。
setTimeout(
    function () {
        alert('hello');
    },
    200
);

order.data.read(
    'id=' + me.model.id, 
    function (data) {
        me.attchToModel(data.result);
        callback();
    }, 
    300
);

// 链式调用较长时采用缩进进行调整。
$('#items')
    .find('.selected')
    .highlight()
    .end();

// 三元运算符由3部分组成,因此其换行应当根据每个部分的长度不同,形成不同的情况。
var result = thisIsAVeryVeryLongCondition
    ? resultA : resultB;

var result = condition
    ? thisIsAVeryVeryLongResult
    : resultB;

// 数组和对象初始化的混用,严格按照每个对象的 { 和结束 } 在独立一行的风格书写。
var array = [
    {
        // ...
    },
    {
        // ...
    }
];
[建议] 对于 if...else...try...catch...finally 等语句,推荐使用在 } 号后添加一个换行 的风格,使代码层次结构更清晰,阅读性更好。

示例:

if (condition) {
    // some statements;
}
else {
    // some statements;
}

try {
    // some statements;
}
catch (ex) {
    // some statements;
}

2.2.4 语句

[强制] 不得省略语句结束的分号。
[强制] 在 if / else / for / do / while 语句中,即使只有一行,也不得省略块 {...}

示例:

// good
if (condition) {
    callFunc();
}

// bad
if (condition) callFunc();
if (condition)
    callFunc();
[强制] 函数定义结束不允许添加分号。

示例:

// good
function funcName() {
}

// bad
function funcName() {
};

// 如果是函数表达式,分号是不允许省略的。
var funcName = function () {
};
[强制] IIFE 必须在函数表达式外添加 (,非 IIFE 不得在函数表达式外添加 (

解释:

IIFE = Immediately-Invoked Function Expression.

额外的 ( 能够让代码在阅读的一开始就能判断函数是否立即被调用,进而明白接下来代码的用途。而不是一直拖到底部才恍然大悟。

示例:

// good
var task = (function () {
   // Code
   return result;
})();

var func = function () {
};


// bad
var task = function () {
    // Code
    return result;
}();

var func = (function () {
});

2.3 命名

[强制] 变量 使用 Camel命名法

示例:

var loadingModules = {};
[强制] 常量 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例:

var HTML_ENTITY = {};
[强制] 函数 使用 Camel命名法

示例:

function stringFormat(source) {
}
[强制] 函数的 参数 使用 Camel命名法

示例:

function hear(theBells) {
}
[强制] 使用 Pascal命名法

示例:

function TextNode(options) {
}
[强制] 类的 方法 / 属性 使用 Camel命名法

示例:

function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}

TextNode.prototype.clone = function () {
    return this;
};
[强制] 枚举变量 使用 Pascal命名法枚举的属性 使用 全部字母大写,单词间下划线分隔 的命名方式。

示例:

var TargetState = {
    READING: 1,
    READED: 2,
    APPLIED: 3,
    READY: 4
};
[强制] 命名空间 使用 Camel命名法

示例:

equipments.heavyWeapons = {};
[强制] 由多个单词组成的缩写词,在命名中,根据当前命名法和出现的位置,所有字母的大小写与首字母的大小写保持一致。

示例:

function XMLParser() {
}

function insertHTML(element, html) {
}

var httpRequest = new HTTPRequest();
[强制] 类名 使用 名词

示例:

function Engine(options) {
}
[建议] 函数名 使用 动宾短语

示例:

function getStyle(element) {
}
[建议] boolean 类型的变量使用 ishas 开头。

示例:

var isReady = false;
var hasMoreCommands = false;
[建议] Promise对象动宾短语的进行时 表达。

示例:

var loadingData = ajax.get('url');
loadingData.then(callback);

2.4 注释

2.4.1 单行注释

[强制] 必须独占一行。// 后跟一个空格,缩进与下一行被注释说明的代码一致。

2.4.2 多行注释

[建议] 避免使用 /*...*/ 这样的多行注释。有多行注释内容时,使用多个单行注释。

2.4.3 文档化注释

[强制] 为了便于代码阅读和自文档化,以下内容必须包含以 /**...*/ 形式的块注释中。

解释:

  1. 文件
  2. namespace
  3. 函数或方法
  4. 类属性
  5. 事件
  6. 全局变量
  7. 常量
  8. AMD 模块
[强制] 文档注释前必须空一行。
[建议] 自文档化的文档说明 what,而不是 how。

2.4.4 类型定义

[强制] 类型定义都是以{开始, 以}结束。

解释:

常用类型如:{string}, {number}, {boolean}, {Object}, {Function}, {RegExp}, {Array}, {Date}。

类型不仅局限于内置的类型,也可以是自定义的类型。比如定义了一个类 Developer,就可以使用它来定义一个参数和返回值的类型。

[强制] 对于基本类型 {string}, {number}, {boolean},首字母必须小写。
类型定义 语法示例 解释
String {string} --
Number {number} --
Boolean {boolean} --
Object {Object} --
Function {Function} --
RegExp {RegExp} --
Array {Array} --
Date {Date} --
单一类型集合 {Array.<string>} string 类型的数组
多类型 {(number|boolean)} 可能是 number 类型, 也可能是 boolean 类型
允许为null {?number} 可能是 number, 也可能是 null
不允许为null {!Object} Object 类型, 但不是 null
Function类型 {function(number, boolean)} 函数, 形参类型
Function带返回值 {function(number, boolean):string} 函数, 形参, 返回值类型
参数可选 @param {string=} name 可选参数, =为类型后缀
可变参数 @param {...number} args 变长参数, ...为类型前缀
任意类型 {*} 任意类型
可选任意类型 @param {*=} name 可选参数,类型不限
可变任意类型 @param {...*} args 变长参数,类型不限

2.4.5 文件注释

[强制] 文件顶部必须包含文件注释,用 @file 标识文件说明。

示例:

/**
 * @file Describe the file
 */
[建议] 文件注释中可以用 @author 标识开发者信息。

解释:

开发者信息能够体现开发人员对文件的贡献,并且能够让遇到问题或希望了解相关信息的人找到维护人。通常情况文件在被创建时标识的是创建者。随着项目的进展,越来越多的人加入,参与这个文件的开发,新的作者应该被加入 @author 标识。

@author 标识具有多人时,原则是按照 责任 进行排序。通常的说就是如果有问题,就是找第一个人应该比找第二个人有效。比如文件的创建者由于各种原因,模块移交给了其他人或其他团队,后来因为新增需求,其他人在新增代码时,添加 @author 标识应该把自己的名字添加在创建人的前面。

@author 中的名字不允许被删除。任何劳动成果都应该被尊重。

业务项目中,一个文件可能被多人频繁修改,并且每个人的维护时间都可能不会很长,不建议为文件增加 @author 标识。通过版本控制系统追踪变更,按业务逻辑单元确定模块的维护责任人,通过文档与wiki跟踪和查询,是更好的责任管理方式。

对于业务逻辑无关的技术型基础项目,特别是开源的公共项目,应使用 @author 标识。

示例:

/**
 * @file Describe the file
 * @author author-name(mail-name@domain.com)
 *         author-name2(mail-name2@domain.com)
 */

2.4.6 命名空间注释

[建议] 命名空间使用 @namespace 标识。

示例:

/**
 * @namespace
 */
var util = {};

2.4.7 类注释

[建议] 使用 @class 标记类或构造函数。

解释:

对于使用对象 constructor 属性来定义的构造函数,可以使用 @constructor 来标记。

示例:

/**
 * 描述
 *
 * @class
 */
function Developer() {
    // constructor body
}
[建议] 使用 @extends 标记类的继承信息。

示例:

/**
 * 描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}
util.inherits(Fronteer, Developer);
[强制] 使用包装方式扩展类成员时, 必须通过 @lends 进行重新指向。

解释:

没有 @lends 标记将无法为该类生成包含扩展类成员的文档。

示例:

/**
 * 类描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
    Developer.call(this);
    // constructor body
}

util.extend(
    Fronteer.prototype,
    /** @lends Fronteer.prototype */{
        _getLevel: function () {
            // TODO
        }
    }
);
[强制] 类的属性或方法等成员信息使用 @public / @protected / @private 中的任意一个,指明可访问性。

解释:

生成的文档中将有可访问性的标记,避免用户直接使用非 public 的属性或方法。

示例:

/**
 * 类描述
 *
 * @class
 * @extends Developer
 */
var Fronteer = function () {
    Developer.call(this);

    /**
     * 属性描述
     *
     * @type {string}
     * @private
     */
    this._level = 'T12';

    // constructor body
};
util.inherits(Fronteer, Developer);

/**
 * 方法描述
 *
 * @private
 * @return {string} 返回值描述
 */
Fronteer.prototype._getLevel = function () {
};

2.4.8 函数/方法注释

[强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。
[强制] 参数和返回值注释必须包含类型信息和说明。
[建议] 当函数是内部函数,外部不可访问时,可以使用 @inner 标识。

示例:

/**
 * 函数描述
 *
 * @param {string} p1 参数1的说明
 * @param {string} p2 参数2的说明,比较长
 *     那就换行了.
 * @param {number=} p3 参数3的说明(可选)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
    var p3 = p3 || 10;
    return {
        p1: p1,
        p2: p2,
        p3: p3
    };
}
[强制] 对 Object 中各项的描述, 必须使用 @param 标识。

示例:

/**
 * 函数描述
 *
 * @param {Object} option 参数描述
 * @param {string} option.url option项描述
 * @param {string=} option.method option项描述,可选参数
 */
function foo(option) {
    // TODO
}
[建议] 重写父类方法时, 应当添加 @override 标识。如果重写的形参个数、类型、顺序和返回值类型均未发生变化,可省略 @param@return,仅用 @override 标识,否则仍应作完整注释。

解释:

简而言之,当子类重写的方法能直接套用父类的方法注释时可省略对参数与返回值的注释。

2.4.9 事件注释

[强制] 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同。

示例:

/**
 * 值变更时触发
 *
 * @event
 * @param {Object} e e描述
 * @param {string} e.before before描述
 * @param {string} e.after after描述
 */
onchange: function (e) {
}
[强制] 在会广播事件的函数前使用 @fires 标识广播的事件,在广播事件代码前使用 @event 标识事件。
[建议] 对于事件对象的注释,使用 @param 标识,生成文档时可读性更好。

示例:

/**
 * 点击处理
 *
 * @fires Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
    /**
     * 值变更时触发
     *
     * @event Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
    this.fire(
        'change',
        {
            before: 'foo',
            after: 'bar'
        }
    );
};

2.4.10 常量注释

[强制] 常量必须使用 @const 标记,并包含说明和类型信息。

示例:

/**
 * 常量说明
 *
 * @const
 * @type {string}
 */
var REQUEST_URL = 'myurl.do';

2.4.11 复杂类型注释

[建议] 对于类型未定义的复杂结构的注释,可以使用 @typedef 标识来定义。

示例:

// `namespaceA~` 可以换成其它 namepaths 前缀,目的是为了生成文档中能显示 `@typedef` 定义的类型和链接。
/**
 * 服务器
 *
 * @typedef {Object} namespaceA~Server
 * @property {string} host 主机
 * @property {number} port 端口
 */

/**
 * 服务器列表
 *
 * @type {Array.<namespaceA~Server>}
 */
var servers = [
    {
        host: '1.2.3.4',
        port: 8080
    },
    {
        host: '1.2.3.5',
        port: 8081
    }
];

2.4.12 AMD 模块注释

[强制] AMD 模块使用 @module@exports 标识。

解释:

@exports 与 @module 都可以用来标识模块,区别在于 @module 可以省略模块名称。而只使用 @exports 时在 namepaths 中可以省略 module: 前缀。

示例:

define(
    function (require) {

        /**
         * foo description
         *
         * @exports Foo
         */
        var foo = {
            // TODO
        };

        /**
         * baz description
         *
         * @return {boolean} return description
         */
        foo.baz = function () {
            // TODO
        };

        return foo;

    }
);

也可以在 exports 变量前使用 @module 标识:

define(
    function (require) {

        /**
         * module description.
         *
         * @module foo
         */
        var exports = {};


        /**
         * bar description
         *
         */
        exports.bar = function () {
            // TODO
        };

        return exports;
    }
);

如果直接使用 factory 的 exports 参数,还可以:

/**
 * module description.
 *
 * @module
 */
define(
    function (require, exports) {

        /**
         * bar description
         *
         */
        exports.bar = function () {
            // TODO
        };
        return exports;
    }
);
[强制] 对于已使用 @module 标识为 AMD模块 的引用,在 namepaths 中必须增加 module: 作前缀。

解释:

namepaths 没有 module: 前缀时,生成的文档中将无法正确生成链接。

示例:

/**
 * 点击处理
 *
 * @fires module:Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
    /**
     * 值变更时触发
     *
     * @event module:Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
    this.fire(
        'change',
        {
            before: 'foo',
            after: 'bar'
        }
    );
};
[建议] 对于类定义的模块,可以使用 @alias 标识构建函数。

示例:

/**
 * A module representing a jacket.
 * @module jacket
 */
define(
    function () {

        /**
         * @class
         * @alias module:jacket
         */
        var Jacket = function () {
        };

        return Jacket;
    }
);
[建议] 多模块定义时,可以使用 @exports 标识各个模块。

示例:

// one module
define('html/utils',
    /**
     * Utility functions to ease working with DOM elements.
     * @exports html/utils
     */
    function () {
        var exports = {
        };

        return exports;
    }
);

// another module
define('tag',
    /** @exports tag */
    function () {
        var exports = {
        };

        return exports;
    }
);
[建议] 对于 exports 为 Object 的模块,可以使用@namespace标识。

解释:

使用 @namespace 而不是 @module 或 @exports 时,对模块的引用可以省略 module: 前缀。

[建议] 对于 exports 为类名的模块,使用 @class@exports 标识。

示例:


// 只使用 @class Bar 时,类方法和属性都必须增加 @name Bar#methodName 来标识,与 @exports 配合可以免除这一麻烦,并且在引用时可以省去 module: 前缀。
// 另外需要注意类名需要使用 var 定义的方式。

/**
 * Bar description
 *
 * @see foo
 * @exports  Bar
 * @class
 */
var Bar = function () {
    // TODO
};

/**
 * baz description
 *
 * @return {(string|Array)} return description
 */
Bar.prototype.baz = function () {
    // TODO
};

2.4.13 细节注释

对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。

[建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。

示例:

function foo(p1, p2, opt_p3) {
    // 这里对具体内部逻辑进行说明
    // 说明太长需要换行
    for (...) {
        ....
    }
}
[强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:

解释:

  1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
  2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
  3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
  4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。

3 语言特性

3.1 变量

[强制] 变量在使用前必须通过 var 定义。

解释:

不通过 var 定义变量将导致变量污染全局环境。

示例:

// good
var name = 'MyName';

// bad
name = 'MyName';
[强制] 每个 var 只能声明一个变量。

解释:

一个 var 声明多个变量,容易导致较长的行长度,并且在修改时容易造成逗号和分号的混淆。

示例:

// good
var hangModules = [];
var missModules = [];
var visited = {};

// bad
var hangModules = [],
    missModules = [],
    visited = {};
[强制] 变量必须 即用即声明,不得在函数或其它形式的代码块起始位置统一声明所有变量。

解释:

变量声明与使用的距离越远,出现的跨度越大,代码的阅读与维护成本越高。虽然JavaScript的变量是函数作用域,还是应该根据编程中的意图,缩小变量出现的距离空间。

示例:

// good
function kv2List(source) {
    var list = [];

    for (var key in source) {
        if (source.hasOwnProperty(key)) {
            var item = {
                k: key,
                v: source[key]
            };
            list.push(item);
        }
    }

    return list;
}

// bad
function kv2List(source) {
    var list = [];
    var key;
    var item;

    for (key in source) {
        if (source.hasOwnProperty(key)) {
            item = {
                k: key,
                v: source[key]
            };
            list.push(item);
        }
    }

    return list;
}

3.2 条件

[强制] 在 Equality Expression 中使用类型严格的 ===。仅当判断 null 或 undefined 时,允许使用 == null

解释:

使用 === 可以避免等于判断中隐式的类型转换。

示例:

// good
if (age === 30) {
    // ......
}

// bad
if (age == 30) {
    // ......
}
[建议] 尽可能使用简洁的表达式。

示例:

// 字符串为空

// 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') {
  // ......
}
[建议] 按执行频率排列分支的顺序。

解释:

按执行频率排列分支的顺序好处是:

  1. 阅读的人容易找到最常见的情况,增加可读性。
  2. 提高执行效率。
[建议] 对于相同变量或表达式的多值条件,用 switch 代替 if

示例:

// good
switch (typeof variable) {
    case 'object':
        // ......
        break;
    case 'number':
    case 'boolean':
    case 'string':
        // ......
        break;
}

// bad
var type = typeof variable;
if (type === 'object') {
    // ......
} 
else if (type === 'number' || type === 'boolean' || type === 'string') {
    // ......
}
[建议] 如果函数或全局中的 else 块后没有任何语句,可以删除 else

示例:

// good
function getName() {
    if (name) {
        return name;
    }

    return 'unnamed';
}

// bad
function getName() {
    if (name) {
        return name;
    }
    else {
        return 'unnamed';
    }
}

3.3 循环

[建议] 不要在循环体中包含函数表达式,事先将函数提取到循环体外。

解释:

循环体中的函数表达式,运行过程中会生成循环次数个函数对象。

示例:

// good
function clicker() {
    // ......
}

for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', clicker);
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    addListener(element, 'click', function () {});
}
[建议] 对循环内多次使用的不变值,在循环外用变量缓存。

示例:

// good
var width = wrap.offsetWidth + 'px';
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = width;
    // ......
}


// bad
for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    element.style.width = wrap.offsetWidth + 'px';
    // ......
}
[建议] 对有序集合进行遍历时,缓存 length

解释:

虽然现代浏览器都对数组长度进行了缓存,但对于一些宿主对象和老旧浏览器的数组对象,在每次 length 访问时会动态计算元素个数,此时缓存 length 能有效提高程序性能。

示例:

for (var i = 0, len = elements.length; i < len; i++) {
    var element = elements[i];
    // ......
}
[建议] 对有序集合进行顺序无关的遍历时,使用逆序遍历。

解释:

逆序遍历可以节省变量,代码比较优化。

示例:

var len = elements.length;
while (len--) {
    var element = elements[len];
    // ......
}

3.4 类型

3.4.1 类型检测

[建议] 类型检测优先使用 typeof。对象类型检测使用 instanceofnullundefined 的检测使用 == null

示例:

// 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
variable instanceof Array

// null
variable === null

// null or undefined
variable == null

// undefined
typeof variable === 'undefined'

3.4.2 类型转换

[建议] 转换成 string 时,使用 + ''

示例:

// good
num + '';

// bad
new String(num);
num.toString();
String(num);
[建议] 转换成 number 时,通常使用 +

示例:

// good
+str;

// bad
Number(str);
[建议] string 转换成 number,要转换的字符串结尾包含非数字并期望忽略时,使用 parseInt

示例:

var width = '200px';
parseInt(width, 10);
[强制] 使用 parseInt 时,必须指定进制。

示例:

// good
parseInt(str, 10);

// bad
parseInt(str);
[建议] 转换成 boolean 时,使用 !!

示例:

var num = 3.14;
!!num;
[建议] number 去除小数点,使用 Math.floor / Math.round / Math.ceil,不使用 parseInt

示例:

// good
var num = 3.14;
Math.ceil(num);

// bad
var num = 3.14;
parseInt(num, 10);

3.5 字符串

[强制] 字符串开头和结束使用单引号 '

解释:

  1. 输入单引号不需要按住 shift,方便输入。
  2. 实际使用中,字符串经常用来拼接 HTML。为方便 HTML 中包含双引号而不需要转义写法。

示例:

var str = '我是一个字符串';
var html = '<div class="cls">拼接HTML可以省去双引号转义</div>';
[建议] 使用 数组+ 拼接字符串。

解释:

  1. 使用 + 拼接字符串,如果拼接的全部是 StringLiteral,压缩工具可以对其进行自动合并的优化。所以,静态字符串建议使用 + 拼接。
  2. 在现代浏览器下,使用 + 拼接字符串,性能较数组的方式要高。
  3. 如需要兼顾老旧浏览器,应尽量使用数组拼接字符串。

示例:

// 使用数组拼接字符串
var str = [
    // 推荐换行开始并缩进开始第一个字符串, 对齐代码, 方便阅读.
    '<ul>',
        '<li>第一项</li>',
        '<li>第二项</li>',
    '</ul>'
].join('');

// 使用 + 拼接字符串
var str2 = '' // 建议第一个为空字符串, 第二个换行开始并缩进开始, 对齐代码, 方便阅读
    + '<ul>',
    +    '<li>第一项</li>',
    +    '<li>第二项</li>',
    + '</ul>';
[建议] 复杂的数据到视图字符串的转换过程,选用一种模板引擎。

解释:

使用模板引擎有如下好处:

  1. 在开发过程中专注于数据,将视图生成的过程由另外一个层级维护,使程序逻辑结构更清晰。
  2. 优秀的模板引擎,通过模板编译技术和高质量的编译产物,能获得比手工拼接字符串更高的性能。
  • artTemplate: 体积较小,在所有环境下性能高,语法灵活。
  • dot.js: 体积小,在现代浏览器下性能高,语法灵活。
  • etpl: 体积较小,在所有环境下性能高,模板复用性高,语法灵活。
  • handlebars: 体积大,在所有环境下性能高,扩展性高。
  • hogon: 体积小,在现代浏览器下性能高。
  • nunjucks: 体积较大,性能一般,模板复用性高。

3.6 对象

[强制] 使用对象字面量 {} 创建新 Object

示例:

// good
var obj = {};

// bad
var obj = new Object();
[强制] 对象创建时,如果一个对象的所有 属性 均可以不添加引号,则所有 属性 不得添加引号。

示例:

var info = {
    name: 'someone',
    age: 28
};
[强制] 对象创建时,如果任何一个 属性 需要添加引号,则所有 属性 必须添加 '

解释:

如果属性不符合 Identifier 和 NumberLiteral 的形式,就需要以 StringLiteral 的形式提供。

示例:

// good
var info = {
    'name': 'someone',
    'age': 28,
    'more-info': '...'
};

// bad
var info = {
    name: 'someone',
    age: 28,
    'more-info': '...'
};
[强制] 不允许修改和扩展任何原生对象和宿主对象的原型。

示例:

// 以下行为绝对禁止
String.prototype.trim = function () {
};
[建议] 属性访问时,尽量使用 .

解释:

属性名符合 Identifier 的要求,就可以通过 . 来访问,否则就只能通过 [expr] 方式访问。

通常在 JavaScript 中声明的对象,属性命名是使用 Camel 命名法,用 . 来访问更清晰简洁。部分特殊的属性(比如来自后端的JSON),可能采用不寻常的命名方式,可以通过 [expr] 方式访问。

示例:

info.age;
info['more-info'];
[建议] for in 遍历对象时, 使用 hasOwnProperty 过滤掉原型中的属性。

示例:

var newInfo = {};
for (var key in info) {
    if (info.hasOwnProperty(key)) {
        newInfo[key] = info[key];
    }
}

3.7 数组

[强制] 使用数组字面量 [] 创建新数组,除非想要创建的是指定长度的数组。

示例:

// good
var arr = [];

// bad
var arr = new Array();
[强制] 遍历数组不使用 for in

解释:

数组对象可能存在数字以外的属性, 这种情况下 for in 不会得到正确结果.

示例:

var arr = ['a', 'b', 'c'];
arr.other = 'other things'; // 这里仅作演示, 实际中应使用Object类型

// 正确的遍历方式
for (var i = 0, len = arr.length; i < len; i++) {
    console.log(i);
}

// 错误的遍历方式
for (i in arr) {
    console.log(i);
}
[建议] 不因为性能的原因自己实现数组排序功能,尽量使用数组的 sort 方法。

解释:

自己实现的常规排序算法,在性能上并不优于数组默认的 sort 方法。以下两种场景可以自己实现排序:

  1. 需要稳定的排序算法,达到严格一致的排序结果。
  2. 数据特点鲜明,适合使用桶排。
[建议] 清空数组使用 .length = 0

3.8 函数

3.8.1 函数长度

[建议] 一个函数的长度控制在 50 行以内。

解释:

将过多的逻辑单元混在一个大函数中,易导致难以维护。一个清晰易懂的函数应该完成单一的逻辑单元。复杂的操作应进一步抽取,通过函数的调用来体现流程。

特定算法等不可分割的逻辑允许例外。

示例:

function syncViewStateOnUserAction() {
    if (x.checked) {
        y.checked = true;
        z.value = '';
    }
    else {
        y.checked = false;
    }

    if (!a.value) {
        warning.innerText = 'Please enter it';
        submitButton.disabled = true;
    }
    else {
        warning.innerText = '';
        submitButton.disabled = false;
    }
}

// 直接阅读该函数会难以明确其主线逻辑,因此下方是一种更合理的表达方式:

function syncViewStateOnUserAction() {
    syncXStateToView();
    checkAAvailability();
}

function syncXStateToView() {
    if (x.checked) {
        y.checked = true;
        z.value = '';
    }
    else {
        y.checked = false;
    }
}

function checkAAvailability() {
    if (!a.value) {
        displayWarningForAMissing();
    }
    else {
        clearWarnignForA();
    }
}

3.8.2 参数设计

[建议] 一个函数的参数控制在 6 个以内。

解释:

除去不定长参数以外,函数具备不同逻辑意义的参数建议控制在 6 个以内,过多参数会导致维护难度增大。

某些情况下,如使用 AMD Loader 的 require 加载多个模块时,其 callback 可能会存在较多参数,因此对函数参数的个数不做强制限制。

[建议] 通过 options 参数传递非数据输入型参数。

解释:

有些函数的参数并不是作为算法的输入,而是对算法的某些分支条件判断之用,此类参数建议通过一个 options 参数传递。

如下函数:

/**
 * 移除某个元素
 *
 * @param {Node} element 需要移除的元素
 * @param {boolean} removeEventListeners 是否同时将所有注册在元素上的事件移除
 */
function removeElement(element, removeEventListeners) {
    element.parent.removeChild(element);
    if (removeEventListeners) {
        element.clearEventListeners();
    }
}

可以转换为下面的签名:

/**
 * 移除某个元素
 *
 * @param {Node} element 需要移除的元素
 * @param {Object} options 相关的逻辑配置
 * @param {boolean} options.removeEventListeners 是否同时将所有注册在元素上的事件移除
 */
function removeElement(element, options) {
    element.parent.removeChild(element);
    if (options.removeEventListeners) {
        element.clearEventListeners();
    }
}

这种模式有几个显著的优势:

  • boolean 型的配置项具备名称,从调用的代码上更易理解其表达的逻辑意义。
  • 当配置项有增长时,无需无休止地增加参数个数,不会出现 removeElement(element, true, false, false, 3) 这样难以理解的调用代码。
  • 当部分配置参数可选时,多个参数的形式非常难处理重载逻辑,而使用一个 options 对象只需判断属性是否存在,实现得以简化。

3.8.3 闭包

[建议] 在适当的时候将闭包内大对象置为 null

解释:

在 JavaScript 中,无需特别的关键词就可以使用闭包,一个函数可以任意访问在其定义的作用域外的变量。需要注意的是,函数的作用域是静态的,即在定义时决定,与调用的时机和方式没有任何关系。

闭包会阻止一些变量的垃圾回收,对于较老旧的JavaScript引擎,可能导致外部所有变量均无法回收。

首先一个较为明确的结论是,以下内容会影响到闭包内变量的回收:

  • 嵌套的函数中是否有使用该变量。
  • 嵌套的函数中是否有 直接调用eval
  • 是否使用了 with 表达式。

Chakra、V8 和 SpiderMonkey 将受以上因素的影响,表现出不尽相同又较为相似的回收策略,而JScript.dll和Carakan则完全没有这方面的优化,会完整保留整个 LexicalEnvironment 中的所有变量绑定,造成一定的内存消耗。

由于对闭包内变量有回收优化策略的 Chakra、V8 和 SpiderMonkey 引擎的行为较为相似,因此可以总结如下,当返回一个函数 fn 时:

  1. 如果 fn 的 [[Scope]] 是ObjectEnvironment(with 表达式生成 ObjectEnvironment,函数和 catch 表达式生成 DeclarativeEnvironment),则:
    1. 如果是 V8 引擎,则退出全过程。
    2. 如果是 SpiderMonkey,则处理该 ObjectEnvironment 的外层 LexicalEnvironment。
  2. 获取当前 LexicalEnvironment 下的所有类型为 Function 的对象,对于每一个 Function 对象,分析其 FunctionBody:
    1. 如果 FunctionBody 中含有 直接调用eval,则退出全过程。
    2. 否则得到所有的 Identifier。
    3. 对于每一个 Identifier,设其为 name,根据查找变量引用的规则,从 LexicalEnvironment 中找出名称为 name 的绑定 binding。
    4. 对 binding 添加 notSwap 属性,其值为 true。
  3. 检查当前 LexicalEnvironment 中的每一个变量绑定,如果该绑定有 notSwap 属性且值为 true,则:
    1. 如果是V8引擎,删除该绑定。
    2. 如果是SpiderMonkey,将该绑定的值设为 undefined,将删除 notSwap 属性。

对于Chakra引擎,暂无法得知是按 V8 的模式还是按 SpiderMonkey 的模式进行。

如果有 非常庞大 的对象,且预计会在 老旧的引擎 中执行,则使用闭包时,注意将闭包不需要的对象置为空引用。

[建议] 使用 IIFE 避免 Lift 效应

解释:

在引用函数外部变量时,函数执行时外部变量的值由运行时决定而非定义时,最典型的场景如下:

var tasks = [];
for (var i = 0; i < 5; i++) {
    tasks[tasks.length] = function () {
        console.log('Current cursor is at ' + i);
    };
}

var len = tasks.length;
while (len--) {
    tasks[len]();
}

以上代码对 tasks 中的函数的执行均会输出 Current cursor is at 5,往往不符合预期。

此现象称为 Lift 效应 。解决的方式是通过额外加上一层闭包函数,将需要的外部变量作为参数传递来解除变量的绑定关系:

var tasks = [];
for (var i = 0; i < 5; i++) {
    // 注意有一层额外的闭包
    tasks[tasks.length] = (function (i) {
        return function () {
            console.log('Current cursor is at ' + i);
        };
    })(i);
}

var len = tasks.length;
while (len--) {
    tasks[len]();
}

3.8.4 空函数

[建议] 空函数不使用 new Function() 的形式。

示例:

var emptyFunction = function () {};
[建议] 对于性能有高要求的场合,建议存在一个空函数的常量,供多处使用共享。

示例:

var EMPTY_FUNCTION = function () {};

function MyClass() {
}

MyClass.prototype.abstractMethod = EMPTY_FUNCTION;
MyClass.prototype.hooks.before = EMPTY_FUNCTION;
MyClass.prototype.hooks.after = EMPTY_FUNCTION;

3.9 面向对象

[强制] 类的继承方案,实现时需要修正 constructor

解释:

通常使用其他 library 的类继承方案都会进行 constructor 修正。如果是自己实现的类继承方案,需要进行 constructor 修正。

示例:

/**
 * 构建类之间的继承关系
 * 
 * @param {Function} subClass 子类函数
 * @param {Function} superClass 父类函数
 */
function inherits(subClass, superClass) {
    var F = new Function();
    F.prototype = superClass.prototype;
    subClass.prototype = new F();
    subClass.prototype.constructor = subClass;
}
[建议] 声明类时,保证 constructor 的正确性。

示例:

function Animal(name) {
    this.name = name;
}

// 直接prototype等于对象时,需要修正constructor
Animal.prototype = {
    constructor: Animal,

    jump: function () {
        alert('animal ' + this.name + ' jump');
    }
};

// 这种方式扩展prototype则无需理会constructor
Animal.prototype.jump = function () {
    alert('animal ' + this.name + ' jump');
};
[建议] 属性在构造函数中声明,方法在原型中声明。

解释:

原型对象的成员被所有实例共享,能节约内存占用。所以编码时我们应该遵守这样的原则:原型对象包含程序不会修改的成员,如方法函数或配置项。

function TextNode(value, engine) {
    this.value = value;
    this.engine = engine;
}

TextNode.prototype.clone = function () {
    return this;
};
[强制] 自定义事件的 事件名 必须全小写。

解释:

在 JavaScript 广泛应用的浏览器环境,绝大多数 DOM 事件名称都是全小写的。为了遵循大多数 JavaScript 开发者的习惯,在设计自定义事件时,事件名也应该全小写。

[强制] 自定义事件只能有一个 event 参数。如果事件需要传递较多信息,应仔细设计事件对象。

解释:

一个事件对象的好处有:

  1. 顺序无关,避免事件监听者需要记忆参数顺序。
  2. 每个事件信息都可以根据需要提供或者不提供,更自由。
  3. 扩展方便,未来添加事件信息时,无需考虑会破坏监听器参数形式而无法向后兼容。
[建议] 设计自定义事件时,应考虑禁止默认行为。

解释:

常见禁止默认行为的方式有两种:

  1. 事件监听函数中 return false。
  2. 事件对象中包含禁止默认行为的方法,如 preventDefault。

3.10 动态特性

3.10.1 eval

[强制] 避免使用直接 eval 函数。

解释:

直接 eval,指的是以函数方式调用 eval 的调用方法。直接 eval 调用执行代码的作用域为本地作用域,应当避免。

如果有特殊情况需要使用直接 eval,需在代码中用详细的注释说明为何必须使用直接 eval,不能使用其它动态执行代码的方式,同时需要其他资深工程师进行 Code Review。

[建议] 尽量避免使用 eval 函数。

3.10.2 动态执行代码

[建议] 使用 new Function 执行动态代码。

解释:

通过 new Function 生成的函数作用域是全局使用域,不会影响当当前的本地作用域。如果有动态代码执行的需求,建议使用 new Function。

示例:

var handler = new Function('x', 'y', 'return x + y;');
var result = handler($('#x').val(), $('#y').val());

3.10.3 with

[建议] 尽量不要使用 with

解释:

使用 with 可能会增加代码的复杂度,不利于阅读和管理;也会对性能有影响。大多数使用 with 的场景都能使用其他方式较好的替代。所以,尽量不要使用 with。

3.10.4 delete

[建议] 减少 delete 的使用。

解释:

如果没有特别的需求,减少或避免使用deletedelete的使用会破坏部分 JavaScript 引擎的性能优化。

[建议] 处理 delete 可能产生的异常。

解释:

对于有被遍历需求,且值 null 被认为具有业务逻辑意义的值的对象,移除某个属性必须使用 delete 操作。

在严格模式或IE下使用 delete 时,不能被删除的属性会抛出异常,因此在不确定属性是否可以删除的情况下,建议添加 try-catch 块。

示例:

try {
    delete o.x;
}
catch (deleteError) {
    o.x = null;
}

3.10.5 对象属性

[建议] 避免修改外部传入的对象。

解释:

JavaScript 因其脚本语言的动态特性,当一个对象未被 seal 或 freeze 时,可以任意添加、删除、修改属性值。

但是随意地对 非自身控制的对象 进行修改,很容易造成代码在不可预知的情况下出现问题。因此,设计良好的组件、函数应该避免对外部传入的对象的修改。

下面代码的 selectNode 方法修改了由外部传入的 datasource 对象。如果 datasource 用在其它场合(如另一个 Tree 实例)下,会造成状态的混乱。

function Tree(datasource) {
    this.datasource = datasource;
}

Tree.prototype.selectNode = function (id) {
    // 从datasource中找出节点对象
    var node = this.findNode(id);
    if (node) {
        node.selected = true;
        this.flushView();
    }
};

对于此类场景,需要使用额外的对象来维护,使用由自身控制,不与外部产生任何交互的 selectedNodeIndex 对象来维护节点的选中状态,不对 datasource 作任何修改。

function Tree(datasource) {
    this.datasource = datasource;
    this.selectedNodeIndex = {};
}

Tree.prototype.selectNode = function (id) {
    // 从datasource中找出节点对象
    var node = this.findNode(id);
    if (node) {
        this.selectedNodeIndex[id] = true;
        this.flushView();
    }
};

除此之外,也可以通过 deepClone 等手段将自身维护的对象与外部传入的分离,保证不会相互影响。

[建议] 具备强类型的设计。

解释:

  • 如果一个属性被设计为 boolean 类型,则不要使用 1 / 0 作为其值。对于标识性的属性,如对代码体积有严格要求,可以从一开始就设计为 number 类型且将 0 作为否定值。
  • 从 DOM 中取出的值通常为 string 类型,如果有对象或函数的接收类型为 number 类型,提前作好转换,而不是期望对象、函数可以处理多类型的值。

4 浏览器环境

4.1 模块化

4.1.1 AMD

[强制] 使用 AMD 作为模块定义。

解释:

AMD 作为由社区认可的模块定义形式,提供多种重载提供灵活的使用方式,并且绝大多数优秀的 Library 都支持 AMD,适合作为规范。

目前,比较成熟的 AMD Loader 有:

[强制] 模块 id 必须符合标准。

解释:

模块 id 必须符合以下约束条件:

  1. 类型为 string,并且是由 / 分割的一系列 terms 来组成。例如:this/is/a/module
  2. term 应该符合 [a-zA-Z0-9_-]+ 规则。
  3. 不应该有 .js 后缀。
  4. 跟文件的路径保持一致。

4.1.2 define

[建议] 定义模块时不要指明 iddependencies

解释:

在 AMD 的设计思想里,模块名称是和所在路径相关的,匿名的模块更利于封包和迁移。模块依赖应在模块定义内部通过 local require 引用。

所以,推荐使用 define(factory) 的形式进行模块定义。

示例:

define(
    function (require) {
    }
);
[建议] 使用 return 来返回模块定义。

解释:

使用 return 可以减少 factory 接收的参数(不需要接收 exports 和 module),在没有 AMD Loader 的场景下也更容易进行简单的处理来伪造一个 Loader。

示例:

define(
    function (require) {
        var exports = {};

        // ...

        return exports;
    }
);

4.1.3 require

[强制] 全局运行环境中,require 必须以 async require 形式调用。

解释:

模块的加载过程是异步的,同步调用并无法保证得到正确的结果。

示例:

// good
require(['foo'], function (foo) {
});

// bad
var foo = require('foo');
[强制] 模块定义中只允许使用 local require,不允许使用 global require

解释:

  1. 在模块定义中使用 global require,对封装性是一种破坏。
  2. 在 AMD 里,global require 是可以被重命名的。并且 Loader 甚至没有全局的 require 变量,而是用 Loader 名称做为 global require。模块定义不应该依赖使用的 Loader。
[强制] Package在实现时,内部模块的 require 必须使用 relative id

解释:

对于任何可能通过 发布-引入 的形式复用的第三方库、框架、包,开发者所定义的名称不代表使用者使用的名称。因此不要基于任何名称的假设。在实现源码中,require 自身的其它模块时使用 relative id。

示例:

define(
    function (require) {
        var util = require('./util');
    }
);
[建议] 不会被调用的依赖模块,在 factory 开始处统一 require

解释:

有些模块是依赖的模块,但不会在模块实现中被直接调用,最为典型的是 css / js / tpl 等 Plugin 所引入的外部内容。此类内容建议放在模块定义最开始处统一引用。

示例:

define(
    function (require) {
        require('css!foo.css');
        require('tpl!bar.tpl.html');

        // ...
    }
);

4.2 DOM

4.2.1 元素获取

[建议] 对于单个元素,尽可能使用 document.getElementById 获取,避免使用document.all
[建议] 对于多个元素的集合,尽可能使用 context.getElementsByTagName 获取。其中 context 可以为 document 或其他元素。指定 tagName 参数为 * 可以获得所有子元素。
[建议] 遍历元素集合时,尽量缓存集合长度。如需多次操作同一集合,则应将集合转为数组。

解释:

原生获取元素集合的结果并不直接引用 DOM 元素,而是对索引进行读取,所以 DOM 结构的改变会实时反映到结果中。

示例:

<div></div>
<span></span>

<script>
var elements = document.getElementsByTagName('*');

// 显示为 DIV
alert(elements[0].tagName);

var div = elements[0];
var p = document.createElement('p');
document.body.insertBefore(p, div);

// 显示为 P
alert(elements[0].tagName);
</script>
[建议] 获取元素的直接子元素时使用 children。避免使用childNodes,除非预期是需要包含文本、注释和属性类型的节点。

4.2.2 样式获取

[建议] 获取元素实际样式信息时,应使用 getComputedStylecurrentStyle

解释:

通过 style 只能获得内联定义或通过 JavaScript 直接设置的样式。通过 CSS class 设置的元素样式无法直接通过 style 获取。

4.2.3 样式设置

[建议] 尽可能通过为元素添加预定义的 className 来改变元素样式,避免直接操作 style 设置。
[强制] 通过 style 对象设置元素样式时,对于带单位非 0 值的属性,不允许省略单位。

解释:

除了 IE,标准浏览器会忽略不规范的属性值,导致兼容性问题。

4.2.4 DOM 操作

[建议] 操作 DOM 时,尽量减少页面 reflow

解释:

页面 reflow 是非常耗时的行为,非常容易导致性能瓶颈。下面一些场景会触发浏览器的reflow:

  • DOM元素的添加、修改(内容)、删除。
  • 应用新的样式或者修改任何影响元素布局的属性。
  • Resize浏览器窗口、滚动页面。
  • 读取元素的某些属性(offsetLeft、offsetTop、offsetHeight、offsetWidth、scrollTop/Left/Width/Height、clientTop/Left/Width/Height、getComputedStyle()、currentStyle(in IE)) 。
[建议] 尽量减少 DOM 操作。

解释:

DOM 操作也是非常耗时的一种操作,减少 DOM 操作有助于提高性能。举一个简单的例子,构建一个列表。我们可以用两种方式:

  1. 在循环体中 createElement 并 append 到父元素中。
  2. 在循环体中拼接 HTML 字符串,循环结束后写父元素的 innerHTML。

第一种方法看起来比较标准,但是每次循环都会对 DOM 进行操作,性能极低。在这里推荐使用第二种方法。

4.2.5 DOM 事件

[建议] 优先使用 addEventListener / attachEvent 绑定事件,避免直接在 HTML 属性中或 DOM 的 expando 属性绑定事件处理。

解释:

expando 属性绑定事件容易导致互相覆盖。

[建议] 使用 addEventListener 时第三个参数使用 false

解释:

标准浏览器中的 addEventListener 可以通过第三个参数指定两种时间触发模型:冒泡和捕获。而 IE 的 attachEvent 仅支持冒泡的事件触发。所以为了保持一致性,通常 addEventListener 的第三个参数都为 false。

[建议] 在没有事件自动管理的框架支持下,应持有监听器函数的引用,在适当时候(元素释放、页面卸载等)移除添加的监听器。
2019-02-27 14:05:49 weixin_33871366 阅读数 0

原文链接

命名规范

  • 标准变量采用驼峰式命名
  • ‘ID’在变量名中全大写
  • 常量全大写,用下划线连接构造函数,大写第一个字母
  • jquery对象必须以’$’开头命名
let thisIsMyName;
let goodID;
let reportURL;
let AndroidVersion;
let iOSVersion;
let MAX_COUNT = 10;
function Person(name) {
this.name = name;
}
// not good
let body = $('body');
// good
let $body = $('body');

局部变量命名规范

  • s:表示字符串。例如:sName,sHtml;
  • n:表示数字。例如:nPage,nTotal;
  • b:表示逻辑。例如:bChecked,bHasLogin;
  • a:表示数组。例如:aList,aGroup;
  • r:表示正则表达式。例如:rDomain,rEmail;
  • f:表示函数。例如:fGetHtml,fInit;
  • o:表示以上未涉及到的其他对象,例如:oButton,oDate;

函数命名

小驼峰命名法,可使用常见动词约定:
  • can 判断是否可执行某个动作,函数返回一个布尔值。true:可执行;false:不可执行
  • has 判断是否含有某个值, 函数返回一个布尔值。true:含有此值;false:不含有此值
  • is 判断是否为某个值,函数返回一个布尔值。true:为某个值;false:不为某个值
  • get 获取某个之,函数返回一个非布尔值
  • set 设置某个值,无返回值、返回是否设置成功或者返回链式对象load 加载某些数据,无返回值或者返回是否加载完成的结果
// 是否可阅读
function canRead() {
 return true;
}
// 获取名称
function getName() {
 return this.name;
}

引用 References

对所有的引用使用 const ;不要使用 var。

eslint: prefer-const, no-const-assign

这可以确保你无法对引用重新分配,重新分配可能会导致 bug 和难以理解的代码。
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;

如果你一定需要可变动的引用,使用 let 代替 var 。

eslint: no-var jscs: disallowVar

// bad
var count = 1;
if (true) {
count += 1;
}
// good, 使用 let.
let count = 1;
if (true) {
count += 1;
}

对象Objects

使用字面量语法创建对象。

eslint: no-new-object

// bad
const item = new Object();
// good
const item = {};

当创建带有动态属性名称的对象时使用计算的属性名称。

它们允许你在一个地方定义一个对象的所有属性。

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,
};

使用对象方法速记语法。

eslint: object-shorthand jscs: requireEnhancedObjectLiterals

// bad
const atom = {
value: 1,
addValue: function (value) {
    return atom.value + value;
  },
};
// good
const atom = {
value: 1,
addValue(value) {
    return atom.value + value;
  },
};

使用对象属性速记语法。

eslint: object-shorthand jscs: requireEnhancedObjectLiterals

const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
  lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
  lukeSkywalker,
};

将速记属性分组写在对象声明的开始处

更容易看出哪些属性在使用速记语法
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,
};

只用引号引无效标识符的属性。

eslint: quote-props jscs: disallowQuotedKeysInObjects

一般来说,我们认为比较容易阅读。它改进了语法高亮显示,并且更容易被许多JS引擎优化。
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};

用对象展开操作符浅复制对象,优先于Object.assign 。使用对象剩余操作符来获得一个省略某些属性的新对象。

// very bad
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); //  `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 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

数组 Arrays

使用字面量创建数组。

eslint: no-array-constructor

// bad
const items = new Array();
// good
const items = [];

使用数组展开操作符 … 复制数组。

// bad
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// good
const itemsCopy = [...items];

使用展开操作符 … 代替 Array.from,来将一个类数组(array-like) 对象转换成数组。

const foo = document.querySelectorAll('.foo');
// good
const nodes = Array.from(foo);
// best
const nodes = [...foo];

实用 Array.from 代替展开操作符 … 来映射迭代,因为它避免了创建媒介数组。

// bad
const baz = [...foo].map(bar);
// good
const baz = Array.from(foo, bar);

解构 Destructuring

当访问和使用对象的多个属性时,请使用对象解构。

eslint: prefer-destructuring jscs: requireObjectDestructuring

// 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`;
}

使用数组解构。

eslint: prefer-destructuring jscs: requireArrayDestructuring

const arr = [1, 2, 3, 4];
// bad
const first = arr[0];
const second = arr[1];
// good
const [first, second] = arr;

使用对象解构来实现多个返回值,而不是数组解构。jscs: disallowArrayDestructuringReturn

您可以随着时间的推移添加新的属性或更改排序,而不会改变调用时的位置。
// bad
function processInput(input) {
  return [left, right, top, bottom];
}
// 调用者需要考虑返回数据的顺序
const [left, __, top] = processInput(input);
// good
function processInput(input) {
  return { left, right, top, bottom };
}
// 调用者只选择他们需要的数据
const { left, top } = processInput(input);

字符串 Strings

字符串使用单引号 ‘’。

eslint: quotes jscs: validateQuoteMarks

// bad
const name = "Capt. Janeway";
// bad - 模板字面量应该包含插值或换行符
const name = `Capt. Janeway`;
// good
const name = 'Capt. Janeway';

以编程方式构建字符串时,请使用模板字符串而不是字符串连接。

eslint: prefer-template template-curly-spacing jscs: requireTemplateStrings

// 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}?`;
}

永远不要在字符串上使用 eval() ,它会打开太多的漏洞。

eslint: no-eval

函数 Functions

使用命名函数表达式而不是函数声明。

eslint: func-style jscs: disallowFunctionDeclarations

函数声明很容易被提升(Hoisting),这对可读性和可维护性来说都是不利的;
/ bad
function foo() {
  // ...
}
// bad
const foo = function () {
  // ...
};
// good 
// 用明显区别于变量引用调用的词汇命名
const short = function longUniqueMoreDescriptiveLexicalFoo() {
  // ...
};

用圆括号包裹立即调用函数表达式 (IIFE)。

eslint: wrap-iife jscs: requireParenthesesAroundIIFE

一个立即调用函数表达式是一个单独的单元 – 将函数表达式包裹在括号中,后面再跟一个调用括号,这看上去很紧凑。
// 立即调用函数表达式 (IIFE)
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());

不要使用 arguments。可以选择 rest 语法 … 替代。

使用 … 能明确你要传入的参数。另外 rest(剩余)参数是一个真正的数组,而 arguments 是一个类数组(Array-like)。
// bad
function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}
// good
function concatenateAll(...args) {
  return args.join('');
}

使用默认参数语法,而不要使用一个变化的函数参数

// really bad
function handleThings(opts) {
  // 更加糟糕: 如果参数 opts 是 falsy(假值) 的话,它将被设置为一个对象,
  // 这可能是你想要的,但它可以引起一些小的错误。
  opts = opts || {};
  // ...
}
// still bad
function handleThings(opts) {
  if (opts === void 0) {
  opts = {};
}
// ...
}
// good
function handleThings(opts = {}) {
  // ...
}

始终将默认参数放在最后。

// bad
function handleThings(opts = {}, name) {
// ...
}
// good
function handleThings(name, opts = {}) {
// ...
}

隔开函数签名,括号两边用空格隔开。

// bad
const f = function(){};
const g = function (){};
const h = function() {};
// good
const x = function () {};
const y = function a() {};

不要改变参数。

eslint: no-param-reassign

操作作为参数传入的对象,可能会在调用原始对象时造成不必要的变量副作用。(对象是引用类型)
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}

箭头函数 Arrow Functions

当您必须使用匿名函数(如在传递一个内联回调时),请使用箭头函数表示法。

eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions

它创建了一个在 this 上下文中执行的函数的版本,这通常是你想要的,而且这样的写法更为简洁。
// bad
[1, 2, 3].map(function (x) {
  const y = x + 1;
  return x * y;
});
// bad
[1, 2, 3].map( _ => {
  
  return 0;
});
// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});
// good
[1, 2, 3].map(() => {
  
  return 0;
});

如果函数体由一个返回无副作用(side effect)的expression(表达式)的单行语句组成,那么可以省略大括号并使用隐式返回。否则,保留大括号并使用 return 语句。

// bad
[1, 2, 3].map(number => {
const nextNumber = number + 1;
return `A string containing the nextNumber.`;
});
// good
[1, 2, 3].map(number => `A string containing the number.`);

如果表达式跨多行,将其包裹在括号中,可以提高可读性。

// bad
['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call(
  httpMagicObjectWithAVeryLongName,
  httpMethod,
  )
);
// good
['get', 'post', 'put'].map(httpMethod => (
  Object.prototype.hasOwnProperty.call(
  httpMagicObjectWithAVeryLongName,
  httpMethod,
  )
));

如果你的函数只有一个参数并且不使用大括号,则可以省略参数括号。否则,为了清晰和一致性,总是给参数加上括号。

// bad
[1, 2, 3].map((x) => x * x);
// good
[1, 2, 3].map(x => x * x);
// good
[1, 2, 3].map(number => (
`A long string with the number. It’s so long that we don’t want it to take up space on the .map line!`
));
// 总是添加()
// bad
[1, 2, 3].map(x => {
  const y = x + 1;
  return x * y;
});
// good
[1, 2, 3].map((x) => {
  const y = x + 1;
  return x * y;
});

避免使用比较运算符(< =, >=)时,混淆箭头函数语法(=>)。

// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;
// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;
// good
const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize);
// good
const itemHeight = (item) => {
  const { height, largeSize, smallSize } = item;
  return height > 256 ? largeSize : smallSize;
};

类 Classes & 构造函数 Constructors

总是使用 *class。避免直接操作 prototype 。

// 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);
    return value;
  }
}

使用 extends 继承。

因为 extends 是一个内置的原型继承方法并且不会破坏 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];
  }
}

如果没有指定,类有一个默认的构造函数。一个空的构造函数或者只是委托给父类则不是必须的。

eslint: no-useless-constructor

// 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';
  }
}

避免重复类成员。

eslint: no-dupe-class-members

// bad
class Foo {
  bar() { return 1; }
  bar() { return 2; }
}
// good
class Foo {
  bar() { return 1; }
}
// good
class Foo {
  bar() { return 2; }
}

模块 Modules

总是使用模块 (import/export) 而不是其他非标准模块系统。

// 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;

不要使用通配符 import(导入)。

这样能确保你只有一个默认 export(导出)。
// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// good
import AirbnbStyleGuide from './AirbnbStyleGuide';

不要从 import(导入) 中直接 export(导出)。

虽然一行代码简洁明了,但有一个明确的 import(导入) 方法和一个明确的 export(导出) 方法,使事情能保持一致。
// bad
// filename es6.js
export { es6 as default } from './AirbnbStyleGuide';
// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

一个地方只在一个路径中 import(导入) 。

// bad
import foo from 'foo';
// … 其他一些 imports … //
import { named1, named2 } from 'foo';
// good
import foo, { named1, named2 } from 'foo';
// good
import foo, {
  named1,
  named2,
} from 'foo';

不要 export(导出) 可变绑定。

eslint: import/no-mutable-exports

一般应该避免可变性,特别是在导出可变绑定时。虽然一些特殊情况下,可能需要这种技术,但是一般而言,只应该导出常量引用。
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };

在只有单个导出的模块中,默认 export(导出) 优于命名 export(导出)。

eslint: import/prefer-default-export

为了鼓励更多的文件只有一个 export(导出),这有利于模块的可读性和可维护性。
// bad
export function foo() {}
// good
export default function foo() {}

将所有 import 导入放在非导入语句的上面。

eslint: import/first

由于 import 被提升,保持他们在顶部,防止意外的行为。
// bad
import foo from 'foo';
foo.init();
import bar from 'bar';
// good
import foo from 'foo';
import bar from 'bar';
foo.init();

多行导入应该像多行数组和对象字面量一样进行缩进。

// bad
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// good
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';

属性 Properties

使用 点语法(.) 来访问对象的属性。

eslint: dot-notation jscs: requireDotNotation

const luke = {
jedi: true,
age: 28,
};
// bad
const isJedi = luke['jedi'];
// good
const isJedi = luke.jedi;

当通过变量访问属性时使用中括号 []。

const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');

求幂时使用求幂运算符 ** 。

eslint: no-restricted-properties.

// bad
const binary = Math.pow(2, 10);
// good
const binary = 2 ** 10;

变量 Variables

总是使用 const 或 let 来声明变量。 不这样做会导致产生全局变量。 我们希望避免污染全局命名空间。

eslint: no-undef prefer-const

// bad
superPower = new SuperPower();
// good
const superPower = new SuperPower();

将所有的 const 和 let 分组 。

当你需要把已分配的变量分配给一个变量时非常有用
// 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;

变量不要链式赋值。

eslint: no-multi-assign

链接变量赋值会创建隐式全局变量。
// bad
(function example() {
// JavaScript 将其解析为
// let a = ( b = ( c = 1 ) );
// let关键字只适用于变量a;
// 变量b和c变成了全局变量。
let a = b = c = 1;
}());
console.log(a); // 抛出 ReferenceError(引用错误)
console.log(b); // 1
console.log(c); // 1
// good
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // 抛出 ReferenceError(引用错误)
console.log(b); // 抛出 ReferenceError(引用错误)
console.log(c); // 抛出 ReferenceError(引用错误)
// 同样适用于 `const`

避免使用一元递增和递减运算符(++, -–)。

根据 eslint 文档,一元递增和递减语句会受到自动插入分号的影响,并可能导致应用程序中的值递增或递减,从而导致无提示错误。使用像 num += 1 而不是 num++ 或 num ++ 这样的语句来改变你的值也更具有表现力。不允许一元递增和递减语句也会阻止您无意中预先递增/递减值,这也会导致程序中的意外行为。
// 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;

比较运算符 Comparison Operators 和 等号 Equality

使用 === 和 !== 优先于 == 和 !=。

eslint: eqeqeq

对于布尔值使用简写,但对于字符串和数字使用显式比较。

// bad
if (isValid === true) {
// ...
}
// good
if (isValid) {
// ...
}
// bad
if (name) {
// ...
}
// good
if (name !== '') {
// ...
}
// bad
if (collection.length) {
// ...
}
// good
if (collection.length > 0) {
// ...
}

在 case 和 default 子句中,使用大括号来创建包含词法声明的语句块(例如 let, const, function, 和 class).

eslint: no-case-declarations

// 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 {}
  }
}

三元表达式不应该嵌套,通常写成单行表达式。

eslint: no-nested-ternary

// bad
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// 拆分成2个分离的三元表达式
const maybeNull = value1 > value2 ? 'baz' : null;
// better
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// best
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

避免不必要的三元表达式语句。

eslint: no-unneeded-ternary

/ bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// good
const foo = a || b;
const bar = !!c;
const baz = !c;

当运算符混合在一个语句中时,请将其放在括号内。混合算术运算符时,不要将 * 和 % 与 + , -,,/ 混合在一起。

eslint: no-mixed-operators

这可以提高可读性,并清晰展现开发者的意图。
/ bad
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// bad
const bar = a ** b - 5 % d;
// bad
if (a || b && c) {
return 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;

代码块 Blocks

使用大括号包裹所有的多行代码块。

eslint: nonblock-statement-body-position

// bad
if (test)
  return false;
// good
if (test) return false;
// good
if (test) {
  return false;
}
// bad
function foo() { return false; }
// good
function bar() {
  return false;
}

如果通过 if 和 else 使用多行代码块,把 else 放在 if 代码块闭合括号的同一行。

eslint: brace-style

// bad
if (test) {
  thing1();
  thing2();
}
else {
  thing3();
}
// good
if (test) {
  thing1();
  thing2();
} else {
  thing3();
}

如果一个 if 块总是执行一个 return 语句,后面的 else 块是不必要的。在 else if 块中的 return,可以分成多个 if 块来 return 。

eslint: no-else-return

// bad
function foo() {
  if (x) {
    return x;
  } else {
    return y;
  }
}
// bad
function cats() {
  if (x) {
    return x;
  } else if (y) {
    return y;
  }
}
// bad
function dogs() {
  if (x) {
    return x;
  } else {
  if (y) {
    return y;
  }
}
}
// good
function foo() {
  if (x) {
    return x;
  }
return y;
}
// good
function cats() {
  if (x) {
    return x;
  }
  if (y) {
    return y;
  }
}
//good
function dogs(x) {
  if (x) {
    if (z) {
      return y;
  }
} else {
  return z;
  }
}

控制语句 Control Statements

如果您的控制语句(if, while 的)太长或超过最大行长度,那么每个(分组)条件可以放单独一行。逻辑运算符应该放在每行起始处。

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
 thing1();
}
// bad
if (foo === 123 &&
  bar === 'abc') {
  thing1();
}
// bad
if (foo === 123
  && bar === 'abc') {
  thing1();
}
// bad
if (
  foo === 123 &&
  bar === 'abc'
) {
  thing1();
}
// good
if (
  foo === 123
  && bar === 'abc'
) {
  thing1();
}
// good
if (
  (foo === 123 || bar === "abc")
  && doesItLookGoodWhenItBecomesThatLong()
  && isThisReallyHappening()
) {
  thing1();
}
// good
if (foo === 123 && bar === 'abc') {
  thing1();
}

注释 Comments

多行注释使用 /* … /。

/**
* @param {Grid} grid 需要合并的Grid
* @param {Array} cols 需要合并列的Index(序号)数组;从0开始计数,序号也包含。
* @param {Boolean} isAllSome 是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样
* @return void
* @author 单志永 2018/11/8
*/
function mergeCells(grid, cols, isAllSome) {
    // Do Something
}

单行注释使用 // 。将单行注释放在需注释的语句上方。在注释之前放置一个空行,除非它位于代码块的第一行。

// 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;
}

所有注释符和注释内容用一个空格隔开,让它更容易阅读。

eslint: spaced-comment

// 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;
}

给注释增加 FIXME 或 TODO 的前缀,可以帮助其他开发者快速了解这个是否是一个需要重新复查的问题,或是你正在为需要解决的问题提出解决方案。这将有别于常规注释,因为它们是可操作的。使用 FIXME – need to figure this out 或者 TODO – need to implement。

使用 // FIXME: 来标识需要修正的问题。注:如果代码中有该标识,说明标识处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明。

lass Calculator extends Abacus {
  constructor() {
    super();
    // FIXME: shouldn’t use a global here
    total = 0;
  }
}

使用 // TODO: 来标识需要实现的问题。注:如果代码中有该标识,说明在标识处有功能代码待编写,待实现的功能在说明中会简略说明。

class Calculator extends Abacus {
  constructor() {
    super();
    // TODO: total should be configurable by an options param
    this.total = 0;
  }
}

空格 Whitespace

使用 2 个空格作为缩进

// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}

在大括号前放置 1 个空格。

eslint: space-before-blocks jscs: requireSpaceBeforeBlockStatements

// bad
function test(){
  console.log('test');
}
// good
function test() {
  console.log('test');
}
// bad
dog.set('attr',{
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
  age: '1 year',
  breed: 'Bernese Mountain Dog',
});

在控制语句(if、while 等)的小括号前放一个空格。在函数调用及声明中,不在函数的参数列表前加空格。

eslint: keyword-spacing jscs: requireSpaceAfterKeywords

// bad
if(isJedi) {
  fight ();
}
// good
if (isJedi) {
  fight();
}
// bad
function fight () {
  console.log ('Swooosh!');
}
// good
function fight() {
  console.log('Swooosh!');
}

使用空格把运算符隔开。

eslint: space-infix-ops jscs: requireSpaceBeforeBinaryOperators, requireSpaceAfterBinaryOperators

// bad
const x=y+5;
// good
const x = y + 5;

在文件末尾插入一个空行。

eslint: eol-last

// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

// bad
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

// good
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;

长方法链式调用时使用缩进(2个以上的方法链式调用)。使用一个点 . 开头,强调该行是一个方法调用,不是一个新的声明。

eslint: newline-per-chained-call no-whitespace-before-property

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();

// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);

// good
const leds = stage.selectAll('.led').data(data);

不要在圆括号内加空格。

// bad
function bar( foo ) {
  return foo;
}
// good
function bar(foo) {
  return foo;
}
// bad
if ( foo ) {
  console.log(foo);
}
// good
if (foo) {
  console.log(foo);
}

不要在中括号内添加空格。

eslint: array-bracket-spacing jscs: disallowSpacesInsideArrayBrackets

// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);

在大括号内添加空格

// bad
const foo = {clark: 'kent'};
// good
const foo = { clark: 'kent' };

类型转换 Type Casting & Coercion

在声明语句的开始处就执行强制类型转换.

字符串:

eslint: no-new-wrappers

// => this.reviewScore = 9;
// bad
const totalScore = new String(this.reviewScore); // typeof totalScore 是 "object" 而不是 "string"
// bad
const totalScore = this.reviewScore + ''; // 调用 this.reviewScore.valueOf()
// bad
const totalScore = this.reviewScore.toString(); // 不能保证返回一个字符串
// good
const totalScore = String(this.reviewScore);

使数字: 使用 Number 进行转换,而 parseInt 则始终以基数解析字串。

eslint: radix no-new-wrappers

const inputValue = '4';
// bad
const val = new Number(inputValue);
// bad
const val = +inputValue;
// bad
const val = inputValue >> 0;
// bad
const val = parseInt(inputValue);
// good
const val = Number(inputValue);
// good
const val = parseInt(inputValue, 10);

布尔值:

eslint: no-new-wrappers

const age = 0;
// bad
const hasAge = new Boolean(age);
// good
const hasAge = Boolean(age);
// best
const hasAge = !!age;

命名规则 Naming Conventions

避免使用单字母名称。使你的命名具有描述性。

eslint: id-length

// bad
function q() {
// ...
}
// good
function query() {
// ...
}

当命名对象,函数和实例时使用驼峰式命名。

eslint: camelcase jscs: requireCamelCaseOrUpperCaseIdentifiers

// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}
// good
const thisIsMyObject = {};
function thisIsMyFunction() {}

当命名构造函数或类的时候使用 PascalCase 式命名,(注:即单词首字母大写)。

eslint: new-cap

// 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',
});

当 导出(export) 一个默认函数时使用驼峰式命名。你的文件名应该和你的函数的名字一致。

function makeStyleGuide() {
// ...
}
export default makeStyleGuide;

当导出一个 构造函数 / 类 / 单例 / 函数库 / 纯对象时使用 PascalCase 式命名,(愚人码头注:即单词首字母大写)。

const AirbnbStyleGuide = {
  es6: {
    },
};
export default AirbnbStyleGuide;

存取器 Accessors

属性的存取器函数不是必须的。

別使用 JavaScript 的 getters/setters,因为它们会导致意想不到的副作用,而且很难测试,维护和理解。相反,如果要使用存取器函数,使用 getVal() 及 setVal(‘hello’)。

// bad
class Dragon {
  get age() {
    // ...
  }
  set age(value) {
    // ...
  }
}
// good
class Dragon {
  getAge() {
    // ...
  }
  setAge(value) {
    // ...
  }
}

如果属性/方法是一个 boolean, 使用 isVal() 或 hasVal() 方法。

// bad
if (!dragon.age()) {
  return false;
}
// good
if (!dragon.hasAge()) {
  return false;
}

)

更多前端资源请关注微信公众号“前端陌上寒”

JavaScript编码规范

阅读数 5

1结构1.1缩进[强制]使用4个空格作为一个缩进层级[强制]switch下的case和default必须增加一个缩进层级。示例://goodswitch(variable){case'1'://do...break;case'2'://d...

博文 来自: twx843571091

JavaScript编码规范

阅读数 1460

1前言JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护。虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。2代码风格2.1文件[建议] J

博文 来自: u012906135

JavaScript编码规范

阅读数 386

转载自WADE JavaScript文件JavaScript程序应该作为一个.js文件存储和发布。JavaScript代码不应该嵌入在HTML文件里,除非那些代码是一个单独的会话特有的。HTML里的JavaScript代码大大增加了页面的大小,并且很难通过缓存和压缩来缓解。 不要让一行代码超过80个字符。当一条语句不能在单独一行写完时,可能有必要拆分它。在操作符后进行拆分,

博文 来自: u010200222

JavaScript 编码规范

阅读数 4

1前言JavaScript在百度一直有着广泛的应用,特别是在浏览器端的行为管理。本文档的目标是使JavaScript代码风格保持一致,容易被理解和被维护。虽然本文档是针对JavaScript设计的,但是在使用各种JavaScript的预编译语言时(如TypeScript等)时,适用的部分也应尽量遵循本文档的约定。2代码风格2.1文件[建议]...

博文 来自: weixin_34097242

javascript编码规范

阅读数 554

前言在实际工作中,我们应该经常会看到一些功能上没有问题,但编码风格和规范却十分糟糕的代码,这往往会让人不敢再往下阅读,甚至会影响阅读者一天的心情。这些代码不仅不易阅读,而且难以维护,它们一般会出自刚入门的编程新手,也会出自工作了好几年的老程序员手下。因此本文的目的在于帮助那些没有养成良好的编码风格,缺乏相应编码规范意识的JavaScript学习者们改善他们的编码形象。编码形象

博文 来自: yonggeit
没有更多推荐了,返回首页