许十一前端规范系列:JavaScript规范

157 阅读18分钟

前言

JavaScript 是一种客户端脚本语言,这里列出了编写 JavaScript 时需要遵守的规范和准则。

修订人修订时间修订版本
许十一2024/1/19V 1.0.0

语言规范

类型

原始类型: 存取原始类型直接作用于值本身

const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // 1, 9

复杂类型: 访问复杂类型作用于值的引用

不推荐:

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

推荐:

const foo = [1, 2, 3];
const bar = JSON.parse(JSON.stringify(foo));
bar[0] = 9;
console.log(foo[0], bar[0]); // 1, 9

引用

  • const 和 let 都是块级作用域,var 是函数级作用域
  • 对所有引用都使用 const,不要使用 var,eslint: prefer-const, no-const-assign

推荐:

const a = 1;
const b = 2;

不推荐:

var a = 1;
var b = 2;
  • 如果引用是可变动的,使用 let 代替 var,eslint: no-var

推荐:

let count = 1;
if (count < 10) {
  count += 1;
}

不推荐:

var count = 1;
if (count < 10) {
  count += 1;
}

对象

  • 请使用字面量值创建对象,eslint: no-new-object

推荐:

const a = {};

不推荐:

const a = new Object{}
  • 别使用保留字作为对象的键值,这样在 IE8 下不会运行

推荐:

const a = {
  defaults: {},
  common: {},
};

不推荐:

const a = {
  default: {}, // default 是保留字
  common: {},
};
  • 当使用动态属性名创建对象时,请使用对象计算属性名来进行创建

推荐:

function getKey(k) {
  return `a key named ${k}`;
}

const obj = {
  id: 5,
  name: "San Francisco",
  [getKey("enabled")]: true,
};

不推荐:

function getKey(k) {
  return `a key named ${k}`;
}

const obj = {
  id: 5,
  name: "San Francisco",
};

obj[getKey("enabled")] = true;
  • 请使用对象方法的简写方式,eslint: object-shorthand

推荐:

const item = {
  value: 1,

  addValue(val) {
    return item.value + val;
  },
};

不推荐:

const item = {
  value: 1,

  addValue: function (val) {
    return item.value + val;
  },
};
  • 请使用对象属性值的简写方式,eslint: object-shorthand

推荐:

const job = "FrontEnd";
const item = {
  job,
};

不推荐:

const job = "FrontEnd";
const item = {
  job: job,
};
  • 将简写的对象属性分组后统一放到对象声明的开头

推荐:

const job = "FrontEnd";
const department = "JDC";
const item = {
  job,
  department,
  sex: "male",
  age: 25,
};

不推荐:

const job = "FrontEnd";
const department = "JDC";
const item = {
  sex: "male",
  job,
  age: 25,
  department,
};
  • 只对非法标识符的属性使用引号,eslint: quote-props

推荐:

const good = {
  foo: 3,
  bar: 4,
  "data-blah": 5,
};

不推荐:

const bad = {
  "foo": 3,
  "bar": 4,
  "data-blah": 5,
};
  • 不直接使用 Object.prototype 的方法, 例如 hasOwnProperty, propertyIsEnumerable 和 isPrototypeOf 方法,eslint: no-prototype-builtins
  • 这些方法可能会被对象自身的同名属性覆盖 - 比如 { hasOwnProperty: false } 或者对象可能是一个 null 对象(Object.create(null))

优秀:

const has = Object.prototype.hasOwnProperty // cache the lookup once, in module scope.
console.log(has.call(object, key))
/- or -/
import has from 'has' // https://www.npmjs.com/package/has
console.log(has(object, key))

推荐:

console.log(Object.prototype.hasOwnProperty.call(object, key));

不推荐:

console.log(object.hasOwnProperty(key));
  • 优先使用对象展开运算符 ... 来做对象浅拷贝而不是使用 Object.assign,使用对象剩余操作符来获得一个包含确定的剩余属性的新对象

推荐:

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 }

不推荐:

const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

糟糕的:

const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // this mutates `original` ಠ_ಠ
delete copy.a; // so does this

数组

  • 请使用字面量值创建数组,eslint: no-array-constructor

推荐:

const items = [];

不推荐:

const items = new Array();
  • 向数组中添加元素时,请使用 push 方法

推荐:

const items = [];
items.push("test");

不推荐:

const items = [];
items[items.length] = "test";
  • 使用展开运算符 ... 复制数组

推荐:

itemsCopy = [...items];

不推荐:

const items = [];
const itemsCopy = [];
const len = items.length;
let i;
for (i = 0; i < len; i++) {
  itemsCopy[i] = items[i];
}
  • 把一个可迭代的对象转换为数组时,使用展开运算符 ... 而不是 Array.from

推荐:

const foo = document.querySelectorAll(".foo");
const nodes = [...foo];

不推荐:

const foo = document.querySelectorAll(".foo");
const nodes = Array.from(foo);
  • 使用 Array.from 来将一个类数组对象转换为数组

推荐:

const arrLike = { 0: "foo", 1: "bar", 2: "baz", length: 3 };
const arr = Array.from(arrLike);

不推荐:

const arrLike = { 0: "foo", 1: "bar", 2: "baz", length: 3 };
const arr = Array.prototype.slice.call(arrLike);
  • 使用数组的 map 等方法时,请使用 return 声明,如果是单一声明语句的情况,可省略 return

推荐:

[1, 2, 3]
  .map((x) => {
    const y = x + 1;
    return x - y;
  })

  [(1, 2, 3)].map((x) => x + 1);

const flat = {}[([0, 1], [2, 3], [4, 5])].reduce((memo, item, index) => {
  const flatten = memo.concat(item);
  flat[index] = flatten;
  return flatten;
});

inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === "Mockingbird") {
    return author === "Harper Lee";
  }

  return false;
});

不推荐:

const flat = {}[([0, 1], [2, 3], [4, 5])].reduce((memo, item, index) => {
  const flatten = memo.concat(item);
  flat[index] = flatten;
});

inbox.filter((msg) => {
  const { subject, author } = msg;
  if (subject === "Mockingbird") {
    return author === "Harper Lee";
  } else {
    return false;
  }
});

解构赋值

  • 当需要使用对象的多个属性时,请使用解构赋值,eslint: prefer-destructuring

优秀的:

function getFullName({ firstName, lastName }) {
  return `${firstName} ${lastName}`;
}

推荐:

function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

不推荐:

function getFullName(user) {
  const firstName = user.firstName;
  const lastName = user.lastName;
  return `${firstName} ${lastName}`;
}
  • 当需要使用数组的多个值时,请同样使用解构赋值,eslint: prefer-destructuring

推荐:

const arr = [1, 2, 3, 4];
const [first, second] = arr;

不推荐:

const arr = [1, 2, 3, 4];
const first = arr[0];
const second = arr[1];
  • 函数需要回传多个值时,请使用对象的解构,而不是数组的解构

推荐:

const [top, xx, xxx, left] = doSomething();
function doSomething() {
  return { top, right, bottom, left };
}
// 此时不需要考虑数据的顺序
const { top, left } = doSomething();

不推荐:

const [top, xx, xxx, left] = doSomething();
function doSomething() {
  return [top, right, bottom, left];
}
// 如果是数组解构,那么在调用时就需要考虑数据的顺序
const [top, xx, xxx, left] = doSomething();

字符串

  • 字符串统一使用单引号的形式 '',eslint: quotes

推荐:

const department = "JDC";

不推荐:

const department = "JDC";
  • 字符串太长的时候,请不要使用字符串连接符换行 \,而是使用 +

推荐:

const str =
  "JavaScript(简称“JS”)," +
  "是一种具有函数优先的轻量级" +
  "解释型或即时编译型的编程语言。";
  • 字符串统一使用单引号的形式 '',eslint: quotes

推荐:

const department = "JDC";

不推荐:

const department = "JDC";
  • 程序化生成字符串时,请使用模板字符串,eslint: prefer-template template-curly-spacing

推荐:

const test = "test";
const str = `ab${test}`;

不推荐:

const test = "test";
const str = ["a", "b", test].join();
const str = "a" + "b" + test;
  • 不要对字符串使用 eval(),会导致太多漏洞, eslint: no-eval
  • 不要在字符串中使用不必要的转义字符, eslint: no-useless-escape

推荐:

const foo = "'this' is \"quoted\"";
const foo = `my name is '${name}'`;

不推荐:

const foo = "'this' is \"quoted\"";

函数

  • 不要使用 Function 构造函数创建函数, eslint: no-new-func

不推荐:

const add = new Function("a", "b", "return a + b");
const subtract = Function("a", "b", "return a - b");
  • 使用具名函数表达式而非函数声明,eslint: func-style

推荐:

const short = function calFoo() {
  // ...
};

不推荐:

function foo() {
  // ...
}

const foo = function () {
  // ...
};
  • 用圆括号包裹自执行匿名函数,eslint:wrap-iife

推荐:

// immediately-invoked function expression (IIFE)
(function () {
  console.log("Welcome to the Internet. Please follow me.");
})();
  • 不要在非函数代码块(if , while 等)中声明函数,eslint:no-loop-func

推荐:

let test;
if (isUse) {
  test = () => {
    // do something
  };
}

不推荐:

if (isUse) {
  function test() {
    // do something
  }
}
  • 不要将参数命名为 arguments,会导致该参数的优先级高于每个函数作用域内原先存在的 arguments 对象

推荐:

function foo(name, options, args) {
  // ...
}

不推荐:

function foo(name, options, arguments) {
  // ...
}
  • 不要使用 arguments,使用 剩余运算符 ...

推荐:

function test(...args) {
  return args.join("");
}

不推荐:

function test() {
  const args = Array.prototype.slice.call(arguments);
  return args.join("");
}
  • 不要使用 arguments,使用 剩余运算符 ...

推荐:

function handleThings(opts = {}) {
  // ...
}

不推荐:

function handleThings(opts) {
  // No! We shouldn't mutate function arguments.
  // Double bad: if opts is falsy it'll be set to an object which may
  // be what you want but it can introduce subtle bugs.
  opts = opts || {};
  // ...
}

function handleThings(opts) {
  if (opts === void 0) {
    opts = {};
  }
  // ...
}
  • 避免参数默认值的副作用

不推荐:

let b = 1;

function count(a = b++) {
  console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
  • 将参数默认值放在最后

推荐:

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

不推荐:

function handleThings(opts = {}, name) {
  // ...
}
  • 不要更改参数,eslint: no-param-reassign

推荐:

function f2(obj) {
  const key = Object.prototype.hasOwnProperty.call(obj, "key") ? obj.key : 1;
}

不推荐:

function f1(obj) {
  obj.key = 1;
}
  • 不要给参数重新赋值,eslint: no-param-reassign
  • 参数重新赋值可能会导致无法预期的行为,尤其是当操作 arguments 对象时,也可能导致优化问题,尤其是在 V8 引擎中

推荐:

function f3(a) {
  const b = a || 1;
}

function f4(a = 1) {}

不推荐:

function f1(a) {
  a = 1;
}

function f2(a) {
  if (!a) {
    a = 1;
  }
}
  • 调用可变参数函数时建议使用展开运算符 ...., eslint: prefer-spread

推荐:

const x = [1, 2, 3, 4, 5];
console.log(...x);

new Date(...[2016, 8, 5]);

不推荐:

const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);

new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]))();

箭头函数

  • 当你必须使用函数表达式(传递匿名函数)时,使用箭头函数标记. eslint: prefer-arrow-callback, arrow-spacing
  • 它将创建在 this 上下文中执行的函数版本,通常是您想要的,并且语法更简洁
  • 如果您有一个相当复杂的函数,则可以将该逻辑移到其自己的命名函数表达式中

推荐:

[1, 2, 3].map((x) => {
  const y = x + 1
  return x * y
})

不推荐:

[1, 2, 3].map(function (x) {
  const y = x + 1
  return x * y
})
  • 如果函数体只包含一条没有副作用的返回表达式的语句,可以省略花括号并使用隐式的 return, 否则保留花括号并使用 return 语句,eslint: arrow-parens, arrow-body-style

推荐:

[1, 2, 3].map(number => `A string containing the ${number}.`)

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

[1, 2, 3].map((number, index) => ({
  index: number
}))


function foo(callback) {
  const val = callback()
  if (val === true) {
    // Do something if callback returns true
  }
}

let bool = false
foo(() => {
  bool = true
})

不推荐:

[1, 2, 3].map(number => {
  const nextNumber = number + 1
  `A string containing the ${nextNumber}.`
})

let bool = false
foo(() => bool = true)

  • 一旦表达式跨多行,使用圆括号包裹以便更好阅读

推荐:

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

不推荐:

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

-函数如果只接收一个参数并且没使用用花括号,则省略圆括号,否则为了清晰明确则使用圆括号包裹参数,注意:总是使用圆括号也是可以接受的,eslint 中的 “always” 选项,eslint: arrow-parens

推荐:

[1, 2, 3].map(x => x * x)

[1, 2, 3].map(number => (
  `A long string with the ${number}. It’s so long that we’ve broken it ` +
  'over multiple lines!'
))

[1, 2, 3].map((x) => {
  const y = x + 1
  return x * y
})

不推荐:

[1, 2, 3].map((x) => x * x)

[1, 2, 3].map(x => {
  const y = x + 1
  return x * y
})

类&构造函数

  • 使用 class,避免直接操作 prototype

推荐:

class Queue {
  constructor (contents = []) {
    this._queue = [...contents]
  }

  pop () {
    const value = this._queue[0]
    this._queue.splice(0, 1)
    return value
  }
}

不推荐:

function Queue (contents = []) {
  this._queue = [..contents]
}
Queue.prototype.pop = function () {
  const value = this._queue[0]
  this._queue.splice(0, 1)
  return value
}
  • 使用 extends 来实现继承
  • 这是一个不会破坏 instanceof 的内建实现原型式继承的方式

推荐:

class PeekableQueue extends Queue {
  peek () {
    return this.queue[0]
  }
}

不推荐:

const inherits = require('inherits')
function PeekableQueue(contents) {
  Queue.apply(this, contents)
}
inherits(PeekableQueue, Queue)
PeekableQueue.prototype.peek = function () {
  return this.queue[0]
}
  • 如果未声明构造函数,则类会有一个默认的构造函数,没必要用空的构造函数或者将其委托给父类,eslint: no-useless-constructor

推荐:

class Rey extends Jedi {
  constructor (...args) {
    super(...args)
    this.name = 'Rey'
  }
}

不推荐:

class Jedi {
  constructor () {}

  getName() {
    return this.name
  }
}

class Rey extends Jedi {
  constructor (...args) {
    super(...args)
  }
}
  • 避免类成员重复,eslint: no-dupe-class-members
  • 重复的类成员声明会默认使用最后声明的,通常会导致 bug

推荐:

class Foo {
  bar () { return 1 }
}

class Foo {
  bar () { return 2 }
}

不推荐:

class Foo {
  bar () { return 1 }
  bar () { return 2 }
}

模块

  • 使用标准的 ES6 模块语法 import 和 export
// bad
const util = require('./util')
module.exports = util

// good
import Util from './util'
export default Util

// better
import { Util } from './util'
export default Util
  • 不要使用 import 的通配符 *,这样可以确保你只有一个默认的 export
// bad
import * as Util from './util'

// good
import Util from './util'
  • 同个文件每个模块只允许 import 一次,有多个 import 请书写在一起,eslint: no-duplicate-imports
// bad
import foo from 'foo'
// … some other imports … //
import { named1, named2 } from 'foo'

// good
import foo, { named1, named2 } from 'foo'

// good
import foo, {
  named1,
  named2
} from 'foo'
  • 将所有 import 语句放在文件最前方,eslint: import/imports-first
// 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'
  • 在模块 import 声明中禁止使用 Webpack 的 loader 语法,eslint: import/no-webpack-loader-syntax
// bad
import fooSass from 'css!sass!foo.scss'
import barCss from 'style!css!bar.css'

// good
import fooSass from 'foo.scss'
import barCss from 'bar.css'

迭代器

  • 不要使用 iterators,建议使用 JS 更高优先级的函数代替 for-in 或 for-of 循环,除非迫不得已,eslint: no-iterator no-restricted-syntax
const numbers = [1, 2, 3, 4, 5]

// bad
let sum = 0
for (let num of numbers) {
  sum += num
}

// good
let sum = 0
numbers.forEach(num => sum += num)

// better
const sum = numbers.reduce((total, num) => total + num, 0)

生成器

  • 现阶段请不要使用生成器 generator
  • 因为不能很好地翻译成 ES5 代码

对象属性

  • 使用 . 来访问对象属性
const joke = {
  name: 'haha',
  age: 28
}

// bad
const name = joke['name']

// good
const name = joke.name
  • 当访问的属性是变量时使用 []
const luke = {
  jedi: true,
  age: 28,
}

function getProp (prop) {
  return luke[prop]
}

const isJedi = getProp('jedi')

变量声明

  • 声明变量时,请使用 const、let 关键字,如果没有写关键字,变量就会暴露在全局上下文中,这样很可能会和现有变量冲突,另外,也很难明确该变量的作用域是什么。这里推荐使用 const 来声明变量,我们需要避免全局命名空间的污染。eslint: no-undef prefer-const
// bad
demo = new Demo()

// good
const demo = new Demo()
  • 将所有的 const 和 let 分组
// bad
let a
const b
let c
const d
let e

// good
const b
const d
let a
let c
let e
  • 变量不要进行链式赋值,变量链式赋值会创建隐藏的全局变量.
// bad
(function example() {
  // JavaScript interprets this as
  // let a = ( b = ( c = 1 ) );
  // The let keyword only applies to variable a; variables b and c become
  // global variables.
  let a = b = c = 1
}())

console.log(a) // throws ReferenceError
console.log(b) // 1
console.log(c) // 1

// good
(function example() {
  let a = 1
  let b = a
  let c = a
}())

console.log(a) // throws ReferenceError
console.log(b) // throws ReferenceError
console.log(c) // throws ReferenceError

// the same applies for `const`
  • 不允许出现未被使用的变量,eslint: no-unused-vars,声明但未被使用的变量通常是不完全重构犯下的错误.这种变量在代码里浪费空间并会给读者造成困扰
// bad

var some_unused_var = 42

// Write-only variables are not considered as used.
var y = 10
y = 5

// A read for a modification of itself is not considered as used.
var z = 0
z = z + 1

// Unused function arguments.
function getX (x, y) {
  return x
}

// good

function getXPlusY (x, y) {
  return x + y
}

const x = 1
const y = a + 2

alert(getXPlusY(x, y))

// 'type' is ignored even if unused because it has a rest property sibling.
// This is a form of extracting an object that omits the specified keys.
const { type, ...coords } = data
// 'coords' is now the 'data' object without its 'type' property.

变量提升

  • var 存在变量提升的情况,即 var 声明会被提升至该作用域的顶部,但是他们的赋值并不会。而 · - const 和 let 并不存在这种情况,他们被赋予了 Temporal Dead Zones, TDZ, 了解 typeof 不再安全很重要
function example () {
  console.log(notDefined)   // => throws a ReferenceError
}

function example () {
  console.log(declareButNotAssigned)  // => undefined
  var declaredButNotAssigned = true
}

function example () {
  let declaredButNotAssigned
  console.log(declaredButNotAssigned)   // => undefined
  declaredButNotAssigned = true
}

function example () {
  console.log(declaredButNotAssigned)   // => throws a ReferenceError
  console.log(typeof declaredButNotAssigned)  // => throws a ReferenceError
  const declaredButNotAssigned = true
}
  • 匿名函数的变量名会提升,但函数内容不会
function example () {
  console.log(anonymous)  // => undefined

  anonymous()

  var anonymous = function () {
    console.log('test')
  }
}
  • 命名的函数表达式的变量名会被提升,但函数名和函数函数内容并不会
function example() {
  console.log(named)  // => undefined

  named()   // => TypeError named is not a function

  superPower()  // => ReferenceError superPower is not defined

  var named = function superPower () {
    console.log('Flying')
  }
}

function example() {
  console.log(named)  // => undefined

  named()   // => TypeError named is not a function

  var named = function named () {
    console.log('named')
  }
}

比较运算符&相等

  • 使用 === 和 !== 而非 == 和 !=,eslint: eqeqeq

  • 条件声明例如 if 会用 ToBoolean 这个抽象方法将表达式转成布尔值并遵循如下规则

  1. Objects 等于 true
  2. Undefined 等于 false
  3. Null 等于 false
  4. Booleans 等于 布尔值
  5. Numbers 在 +0, -0, 或者 NaN 的情况下等于 false, 其他情况是 true
  6. Strings 为 '' 时等于 false, 否则是 true

分号

  • 我们遵循 Standard 的规范,不使用分号。

  • 关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的 JS 程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。

// bad
const test = 'good';
(function () {
  const str = 'hahaha';
})()

// good
const test = 'good'
;(() => {
  const str = 'hahaha'
})();

标准特性

  • 为了代码的可移植性和兼容性,我们应该最大化的使用标准方法,例如优先使用 string.charAt(3) 而不是 string[3]

eval()

  • 由于 eval 方法可以干扰作用域链,所以我们约定禁止使用该方法
  • 如果你现在的代码中使用了eval(),记住该咒语“eval()是魔鬼”。

with() {}

` 由于 with 方法会欺骗词法作用域,所以我们也是禁止使用该方法的

修改内置对象的原型

不要修改内置对象,如 Object 和 Array

编码规范

单行代码块

不推荐

function foo () {return true}
if (foo) {bar = 0}

推荐

function foo () { return true }
if (foo) { bar = 0 }

大括号风格

在编程过程中,大括号风格与缩进风格紧密联系,用来描述大括号相对代码块位置的方法有很多。在 JavaScript 中,主要有三种风格,如下:

  • One True Brace Style
if (foo) {
  bar()
} else {
  baz()
}
  • Stroustrup
if (foo) {
  bar()
}
else {
  baz()
}
  • Allman
if (foo)
{
  bar()
}
else
{
  baz()
}

我们团队约定使用 ==One True Brace Style== 风格

变量命名

当命名变量时,主流分为驼峰式命名(constiableName)和下划线命名(constiable_name)两大阵营。 团队约定使用==驼峰式==命名

拖尾逗号

拖尾逗号的例子:

const foo = {
  name: 'foo',
  age: '22',
}

团队约定允许在最后一个元素或属性与闭括号 ] 或 }在不同行时, 可以(但不要求)使用拖尾逗号。 当在同一行时,禁止使用拖尾逗号。

逗号空格

逗号前后的空格可以提高代码的可读性,团队约定在逗号后面使用空格,

不推荐

const foo = 1,bar = 2
const foo = 1 , bar = 2
const foo = 1 ,bar = 2

推荐

const foo = 1, bar = 2

逗号风格

逗号分隔列表时,在 JavaScript 中主要有两种逗号风格:

  • 标准风格,逗号放置在当前行的末尾
  • 逗号前置风格,逗号放置在下一行的开始位置

团队约定使用==标准风格==

不推荐

const foo = 1
,
bar = 2

const foo = 1
, bar = 2

const foo = ['name'
          , 'age']

推荐

const foo = 1,
      bar = 2

const foo = ['name',
            'age']

计算属性的空格

团队约定在对象的计算属性内,禁止使用空格

不推荐

obj['foo' ]
obj[ 'foo']
obj[ 'foo' ]

推荐

obj['foo']

拖尾换行

在非空文件中,存在拖尾换行是一个常见的 UNIX 风格,它的好处是可以方便在串联和追加文件时不会打断 Shell 的提示。在日常的项目中,保留拖尾换行的好处是,可以减少版本控制时的代码冲突。

不推荐

function func () {
  // do something
};var a = b

推荐

function func () {
  // do something
}
// 此处是新的一行

缩进

代码保持一致的缩进,是作为工程师的职业素养。但缩进用两个空格,还是四个空格,是用 Tab 还是空格呢?这样的争论太多了,也得不出答案。本规范结合了市面上优秀的开源项目,姑且约定使用 空格 来缩进,而且缩进使用两个空格。

那是不是不能使用 Tab 进行缩进了?我们可以通过配置 .editorconfig ,将 Tab 自动转换为空格。

对象字面量的键值缩进

团队约定对象字面量的键和值之间不能存在空格,且要求对象字面量的冒号和值之间存在一个空格

不推荐

const obj = { 'foo' : 'haha' }

推荐

const obj = { 'foo': 'haha' }

构造函数首字母大写

在 JavaScript 中 new 操作符用来创建某个特定类型的对象的一个实例,该类型的对象是由一个构造函数表示的。由于构造函数只是常规函数,唯一区别是使用 new 来调用。所以我们团队约定构造函数的首字母要大小,以此来区分构造函数和普通函数。

不推荐

const fooItem = new foo()

推荐

const fooItem = new Foo()

构造函数的参数

在 JavaScript 中,通过 new 调用构造函数时,如果不带参数,可以省略后面的圆括号。但这样会造成与整体的代码风格不一致,所以团队约定使用圆括号

不推荐

const person = new Person

推荐

const person = new Person()

链式调用

链式调用如果放在同一行,往往会造成代码的可读性差,但有些时候,短的链式调用并不会影响美观。所以本规范约定一行最多只能有四个链式调用,超过就要求换行。

空行

空白行对于分离代码逻辑有帮助,但过多的空行会占据屏幕的空间,影响可读性。团队约定最大连续空行数为 2

不推荐

const a = 1






const b = 2

推荐

const a = 1


const b = 2

链式赋值

链式赋值容易造成代码的可读性差,所以团队约定禁止使用链式赋值

不推荐

const a = b = c = 1

推荐

const a = 1
const b = 1
const c = 1

变量声明

JavaScript 允许在一个声明中,声明多个变量。团队约定在声明变量时,一个声明只能有一个变量 不推荐

const a, b, c

推荐

const a
const b
const c

分号

我们遵循 Standard 的规范,不使用分号。

关于应不应该使用分号的讨论有很多,本规范认为非必要的时候,应该不使用分号,好的 JS 程序员应该清楚场景下是一定要加分号的,相信你也是名好的开发者。

代码块空格

一致性是任何风格指南的重要组成部分。虽然在哪里放置块的开括号纯属个人偏好,但在整个项目中应该保持一致。不一致的风格将会分散读者阅读代码的注意力。

不推荐

if (a){
  b()
}

function a (){}

推荐

if (a) {
  b()
}

function a () {}

函数声明的空格

当格式化一个函数,函数名或 function 关键字与左括号之间允许有空白。命名函数要求函数名和 function 关键字之间有空格,但是匿名函数要求不加空格。

团队约定函数括号前要加空格

不推荐

function func(x) {
  // ...
}

推荐

function func (x) {
  // ...
}

操作符的空格

团队约定操作符前后都需要添加空格

不推荐

const sum = 1+2

推荐

const sum = 1 + 2

BOM

Unicode 字节顺序标记 (BOM) 用来指定代码单元是高字节序还是低字节序。也就是说,是高位在前还是低位在前。UTF-8 不需要 BOM 来表明字节顺序,因为单个字节并不影响字节顺序。

相信不少同学遇到过 BOM 的坑,这里不多说了,切记不要使用 windows 的记事本改代码!