你能够直接获取到基本类型的值javascript
string
number
boolean
null
undefined
symbol
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
注意: Symbols 不能被完整的polyfill
,因此,在不支持 Symbols 的环境下中,不该该使用symbol
类型。
复杂类型赋值就是获取到他的引用的值,至关于引用传递css
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
const
为了确保你不会改变你的初始值,重复引用会致使一些不可预见的 bug
,还会让代码难以理解,全部的赋值都应该使用 const
,避免使用 var
。html
eslintjava
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
let
若是你必定要对参数从新赋值,那就使用 let
,而不是 var
, let
是块级做用域,而 ver
是函数级做用域。node
eslintwebpack
// bad var count = 1; if (true) { count += 1; } // good let count = 1; if (true) { count += 1; }
const
与 let
的块级做用域const
与 let
声明的常量与变量都只存在于定义它们的那个块级做用域中。git
{ let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError
eslintes6
// bad const obj = new Object(); // good const obj = {};
当你须要建立一个带有 动态属性名 的对象时,请将全部的属性定义放在一块儿,可使用 计算属性名。github
function getKey(key) { return `a key named ${key}`; } // bad const obj = { id: 1, name: 'Parc MG', }; obj[getKey('enabled')] = true; // good const obj = { id: 1, name: 'Parc MG', [getKey('enabled')]: true };
eslintweb
// bad const atom = { value: 1, add: function (value) { return atom.value + value; } }; // good const atom = { value: 1, add(value) { return atom.value + value; } };
eslint
const name = 'Parc MG'; // bad const org = { name: name, }; // good const org = { name, };
const name = 'Parc MG'; const url = 'https://parcmg.com'; // bad const org = { email: 'contact@parcmg.com', name, created: new Date(), url, }; // good const org = { name, url, email: 'contact@parcmg.com', created: new Date(), };
''
号eslint
// bad const bad = { 'foo': 1, 'bar': 2, 'foo-bar': 3, }; // good const good = { foo: 1, bar: 2, 'foo-bar': 3, };
不直接调用一个对象的 hasOwnProperty
、propertyIsEnumerable
、isPrototypeOf
等这些原型的方法,在某些状况下,这些方法可能会被屏蔽掉,好比 { hasOwnProperty: false }
或者是一个空对象 Object.create(null)
。
// bad obj.hasOwnProperty(key); // good Object.prototype.hasOwnProperty.call(obj, key); // best const has = Object.prototype.hasOwnProperty; has.call(obj, key);
...
{ ...obj }
,而不是 Object.assign
。{ foo, bar, ...rest } = obj
eslint
// very bad const original = { a: 1, b: 2 }; const copied = Object.assign(original, { c: 3 }); // 这将致使 original 也被修改 delete copied.a; // 这样操做以后会致使 original 也被修改 console.log(original); // => {b: 2, c: 3} // bad const original = { a: 1, b: 2 }; const copied = Object.assign({}, original, { c: 3}}; // good const original = { a: 1, b: 2 }; const copied = { ...original, c: 3 }; // 解构运算与 `rest` 赋值运算 const obj = { a: 1, b: 2, c: 3 }; const { a, b } = obj; // 从对象 obj 中解构出 a, b 两个属性的值,并赋值给名为 a,b 的常量 const { a, ...rest } = obj; // 从对象 obj 中解构出 a 的值,并赋值给名为 a 的常量,同时,建立一个由全部其它属性组成的名为 `rest` 的新对象 console.log(rest); // => { b: 2, c: 3 } // 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}`; } // the most best const getFullName = ({ firstName, lastName }) => `${firstName} ${lastName}`;
因为 JavaScript
不支持多值返回,当一个函数或者方法有多个值须要建立时,请为每个值命名,并以全部值组成的对象为单一值返回,而不是以数组的形式返回。
// 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); // 调用者能够明确的指定须要哪一个值,并且不须要建立多余的变量
eslint
// bad const items = new Array(); // good const items = [];
.push
方法代替直接索引赋值const items = []; // bad items[items.length] = 'new item'; // good items.push('new item');
const items = [1, 2, 3, 4, 5]; // bad const length = items.length; const copied = []; let index; for (index = 0; index < length; index += 1) { copied[index] = items[index]; } // good const copied = [ ...items ];
...
运算符代替 Array.from
当须要将一个可迭代的对象转换成数组时,推荐使用 ...
操做符。
const elements = document.querySelectorAll('.foobar'); // not bad const nodes = Array.from(elements); // good const nodes = [ ...elements ];
...
解构数组const array = [1, 2, 3, 4, 5]; // bad const first = array[0]; const second = array[1]; // good const [first, second, ...rest] = array; console.log(rest); // => [3, 4, 5]
Array.from
将类数组对象转成数组参考: Typed Arrays
const arrayLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 } // bad const array = Array.prototype.slice.call(arrayLike); // good const array = Array.from(arrayLike);
Array.from
对类数组对象进行遍历
Array.from(arrayLike[, mapFn[, thisArg]])
方法,参考
Array.from
const arrayLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 } // bad const array = [...arrayLike].map(mapFn); // good const array = Array.from(arrayLike, mapFn);
// bad - 当第一次迭代完成以后, acc 就变成了 undefined 了 [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; }); // good [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; return flatten; }); // bad messages.filter(msg => { const { subject, author } = msg; if (subject === 'ParcMG') { return author === 'MG'; } else { return false; } }); // good messages.filter(msg => { const { subject, author } = msg; if (subject === 'ParcMG') { return author === 'MG'; } return false; }); // bad [1, 2, 3].map(x => { const y = x + 1; return x * y; } // good [1, 2, 3].map(x => x * (x + 1));
[
与 ]
处断行// bad const array = [ [0, 1], [2, 3], [4, 5], [6, 7] ]; const objectArray = [{ id: 1, }, { id: 2, }]; const numberArray = [ 1, 2, ]; // good const array = [[0, 1], [2, 3], [4, 5], [6, 7]]; const objectArray = [ { id: 1, }, { id: 2, } ]; const numberArray = [1, 2]; const numberArray = [ 1, 2, ];
string
永远使用单引号 ''
:eslint
// bad const name = "Parc M.G"; const name = `Parc M.G`; // good const name = 'Parc M.G';
// bad const content = '《学而》是《论语》第一篇的篇名。《论语》中各篇通常都是以第\ 一章的前二三个字做为该篇的篇名。《学而》一篇包括16章,内容涉及诸多方面。其中重\ 点是「吾日三省吾身」;「节用而爱人,使民以时」;「礼之用,和为贵」以及仁、孝、\ 信等道德范畴。'; const content = '《学而》是《论语》第一篇的篇名。《论语》中各篇通常都是以第' + '一章的前二三个字做为该篇的篇名。《学而》一篇包括16章,内容涉及诸多方面。其中重' + '点是「吾日三省吾身」;「节用而爱人,使民以时」;「礼之用,和为贵」以及仁、孝、' + '信等道德范畴。'; // good const content = '《学而》是《论语》第一篇的篇名。《论语》中各篇通常都是以第\一章的前二三个字做为该篇的篇名。《学而》一篇包括16章,内容涉及诸多方面。其中重点是「吾日三省吾身」;「节用而爱人,使民以时」;「礼之用,和为贵」以及仁、孝、信等道德范畴。';
eslint
// bad function hello(name) { return '你好,' + name + '!'; } function hello(name) { return ['你好,', name, '!'].join(''); } function hello(name) { return `你好,${ name }!`; } // good function hello(name) { return `你好,${name}!`; }
eval()
eslint
eslint
// bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\this\' is "quoted"'; // best const foo = `'this' is "quoted"`;
eslint
使用函数声明,它的做用域会被提早,这意味着很容易在一个文件里面引用一个还未被定义的函数,这样大大伤害了代码的可读性和可维护性,若一个函数很大很复杂,那么应该考虑将该函数单独提取到一个文件中,抽离成一个模块,同时不要忘记给表达式显示的命名,这消除了由匿名函数在错误调用栈中产生的全部假设。
// bad function foo() { // ... } // bad const foo = function () { // ... } // good const foo = function foo() { // ... } // best const foo = function longUniqueMoreDescriptiveLexicalFoo() { // ... }
eslint
(function () { console.log('Welcome to the ParcMG world.'); }());
虽然运行环境容许你这样作,可是不一样环境的解析方式不同。
eslint
//bad for (var i=10; i; i--) { (function() { return i; })(); } while(i) { var a = function() { return i; }; a(); } do { function a() { return i; }; a(); } while (i); let foo = 0; for (let i = 0; i < 10; ++i) { // Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop setTimeout(() => console.log(foo)); foo += 1; } for (let i = 0; i < 10; ++i) { // Bad, `foo` is not in the loop-block's scope and `foo` is modified in/after the loop setTimeout(() => console.log(foo)); } foo = 100; // good var a = function() {}; for (var i=10; i; i--) { a(); } for (var i=10; i; i--) { var a = function() {}; // OK, no references to variables in the outer scopes. a(); } for (let i=10; i; i--) { var a = function() { return i; }; // OK, all references are referring to block scoped variables in the loop. a(); } var foo = 100; for (let i=10; i; i--) { var a = function() { return foo; }; // OK, all references are referring to never modified variables. a(); }
注意:在
ECMA-262
中,块(block
) 的定义是:一系列语句,但函数声明不是一个语句,命名函数表达式是一个语句。// bad if (currentUser) { function test() { console.log('Nope.'); } } // good let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
arguments
命名参数arguments
的优先级高于高于每一个函数做用域自带的 arguments
对象,这会致使函数自带的 arguments
值被覆盖。
// bad function foo(name, options, arguments) { // ... } // good function foo(name, options, args) { // ... }
arguments
,使用 ...rest
代替eslint
...
明确出你想用那个参数,同时,rest
是一个真数组,而不是一个类数组的arguments
// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }
// really bad function handleThings(options) { options = options || {}; } // still bad function handleTings(options) { if (options === void 0) { options = {}; } } // good function handleThings(options = {}) { }
// bad let v = 1; const count = function count(a = v++) { console.log(a); } count(); // => 1 count(); // => 2 count(3); // => 3 count(); // => 3 // maybe const v = 1; const count = function count(a = v) { console.log(a); }
// bad function handleTings(options = {}, name) { // ... } // good function handleTings(name, options = {}) { // ... }
eslint
// bad var add = new Function('a', 'b', 'return a + b'); // still bad var subtract = Function('a', 'b', 'return a - b'); // good const subtract = (a, b) => a + b;
eslint
// bad const f = function(){}; const g = function (){}; const h = function() {}; // good const f = function a() {};
eslint
函数签名时定义的参数,在函数体内不容许被从新赋值(包含参数自己,若参数为对象,还包括该对象全部属性的值),
一个函数应该是没有任何反作用的。
// bad function f1 (obj) { obj.key = 1; }; function f2 (a) { a = 1; // ... } function f3 (a) { if (!a) { a = 1; } // ... } // good function f4(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; }; function f5(a) { const b = a || 1; // ... } function f6(a = 1) { // ... }
spread
操做符 ...
调用多变参数函数eslint
// bad const x = [1, 2, 3, 4, 5]; console.log.apply(console, x); // good const x = [1, 2, 3, 4, 5]; console.log(...x); // bad new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5])); // good new Date(...[2016, 8, 5]);
// bad function foo(bar, baz, quux) { // ... } // good 缩进不要太过度 function foo( bar, baz, quux, ) { // ... } // bad console.log(foo, bar, baz); // good console.log( foo, bar, baz, );
eslint
// bad [1, 2, 3].map(function (x) { const y = x + 1; return x * y; }); // good [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
return
eslint
// bad [1, 2, 3].map(number => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map(number => `A string containing the ${number}.`); // good [1, 2, 3].map((number) => { const nextNumber = number + 1; return `A string containing the ${nextNumber}.`; }); // good [1, 2, 3].map((number, index) => ({ [index]: number })); // 表达式有反作用就不要用隐式return function foo(callback) { const val = callback(); if (val === true) { // Do something if callback returns true } } let bool = false; // bad // 这种状况会return bool = true, 很差 foo(() => bool = true); // good foo(() => { bool = true; });
// bad ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod ) ); // good ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod ) ));
eslint
// 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; });
eslint
// 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; };
eslint
// bad (foo) => bar; (foo) => (bar); // good (foo) => bar; (foo) => (bar); (foo) => ( bar )
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
实现继承它是一种内置的方法来继承原型功能而不打破 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]; } }
this
实现方法链// bad Jedi.prototype.jump = function () { this.jumping = true; return true; }; Jedi.prototype.setHeight = function (height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20);
toString
方法class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }
eslint
// 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
// bad class Foo { bar() { return 1; } bar() { return 2; } } // good class Foo { bar() { return 1; } } // good class Foo { bar() { return 2; } }
import
/ export
// bad const Button = require('./Button'); module.exports = Button.es6; // ok import Button from './Button'; export default Button.es6; // best import { es6 } from './Button'; export default es6;
import
通配符// bad import * as Component from './Component'; // good import Component from './Component';
import
中 export
虽然一行是简洁的,有一个明确的方式进口和一个明确的出口方式来保证一致性。
// bad export { es6 as default } from './Component'; // good import { es6 } from './Component'; export default es6;
import
一次eslint
从同一个路径下import多行会使代码难以维护
// 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';
export
可变量eslint
变化一般都是须要避免,特别是当你要输出可变的绑定。虽然在某些场景下可能须要这种技术,但总的来讲应该导出常量。
// bad let foo = 3; export { foo } // good const foo = 3; export { foo }
export default
eslint
鼓励使用更多文件,每一个文件只作一件事情并导出,这样可读性和可维护性更好。
// bad export function foo() {} // good export default function foo() {}
import
应该放在全部其它语句以前eslint
// 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';
webpack
,不容许在 import
中使用 webpack loader
语法eslint
一旦用 Webpack 语法在import
里会把代码耦合到模块绑定器。最好是在webpack.config.js
里写webpack loader
语法
// 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';
eslint
用JavaScript高级函数代替
for-in
、for-of
- 这强调了咱们不可变的规则。 处理返回值的纯函数比反作用更容易。
- 用数组的这些迭代方法:
map()
、every()
、filter()
、find()
、findIndex()
、reduce()
、some()
……- 用对象的这些方法
Object.keys()
、Object.values()
、Object.entries
去产生一个数组, 这样你就能去遍历对象了。
const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach(num => sum += num); sum === 15; // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15; // bad const increasedByOne = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // good const increasedByOne = []; numbers.forEach(num => increasedByOne.push(num + 1)); // best (keeping it functional) const increasedByOne = numbers.map(num => num + 1);
generator
eslint
它在es5上支持的很差
若是必定要用,那么必定须要注意一点:function
与 *
是同一律念关键字, *
并非 function
的修饰符, function*
是一个与 function
彻底不同的独特结构。
// bad function * foo() { // ... } // bad const bar = function * () { // ... } // bad const baz = function *() { // ... } // bad const quux = function*() { // ... } // bad function*foo() { // ... } // bad function *foo() { // ... } // very bad function * foo() { // ... } // very bad const wat = function * () { // ... } // good function* foo() { // ... } // good const foo = function* () { // ... }
.
号eslint
这条,涉及一个曾经阿里出过一个看似简单,实则很难的面试题,你就算猜对一个,你也不必定能说出原理:
a.b.c.d和a'b'['d'],哪一个性能更高
到这里,忽然想起这个梗,有兴趣的能够翻看一下 这里。
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
// bad const binary = Math.pow(2, 10); // good const binary = 2 ** 10;
const
或者 let
,不使用 var
eslint
// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
const
或者 let
eslint
扯蛋的理由:这种方式很容易去声明新的变量,你不用去考虑把;调换成,,或者引入一个只有标点的不一样的变化。真正的理由:作法也能够是你在调试的时候单步每一个声明语句,而不是一下跳过全部声明。
// bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z';
在同一个块中,全部的 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;
// bad function checkName(hasName) { const name = getName(); if (hasName === 'test') { return false; } if (name === 'test') { this.setName(''); return false; } return name; } // good function checkName(hasName) { if (hasName === 'test') { return false; } // 在须要的时候分配 const name = getName(); if (name === 'test') { this.setName(''); return false; } return name; }
eslint
连接变量分配会隐匿建立全局变量
// bad (function example() { // JavaScript 将这一段解释为 // let a = ( b = ( c = 1 ) ); // let 只对变量 a 起做用; 变量 b 和 c 都变成了全局变量 let a = b = c = 1; }()); console.log(a); // undefined console.log(b); // 1 console.log(c); // 1 // good (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // undefined console.log(b); // undefined console.log(c); // undefined // `const` 也是如此
++
、--
)eslint
根据 eslint
文档,一元增量和减量语句受到自动分号插入的影响,而且可能会致使应用程序中的值递增或递减的无声错误。 使用 num + = 1
而不是 num ++
或 num ++
语句来表达你的值也是更有表现力的。 禁止一元增量和减量语句还会阻止您无心地预增/预减值,这也会致使程序出现意外行为。
// bad let 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 let 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;
eslint
若是赋值语句超出了 max-len 配置,那么给值前面加上括号。
// bad const foo = superLongLongLongLongLongLongLongLongFunctionName(); // bad const foo = 'superLongLongLongLongLongLongLongLongString'; // good const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // good const foo = 'superLongLongLongLongLongLongLongLongString';
eslint
// bad var some_unused_var = 42; // 写了没用 var y = 10; y = 5; // 变量改了本身的值,也没有用这个变量 var z = 0; z = z + 1; // 参数定义了但未使用 function getX(x, y) { return x; } // good function getXPlusY(x, y) { return x + y; } var x = 1; var y = a + 2; alert(getXPlusY(x, y)); // 'type' 即便没有使用也能够能够被忽略, 由于这个有一个 rest 取值的属性。 // 这是从对象中抽取一个忽略特殊字段的对象的一种形式 var { type, ...coords } = data; // 'coords' 如今就是一个没有 'type' 属性的 'data' 对象
var
var
声明会将变量声明提高到做用域的最前面,可是他的值却只有在运行到代码行时才会被赋值,永远都使用 const
与 let
,了解 时效区(Temporal Dead Zones) 的相关知识,也还要知道为何 typeof 再也不安全。
// 咱们知道这个不会工做,假设没有定义全局的notDefined function example() { console.log(notDefined); // => throws a ReferenceError } // 在你引用的地方以后声明一个变量,他会正常输出是由于变量做用域上升。 // 注意: declaredButNotAssigned的值没有上升 function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // 解释器把变量声明提高到做用域最前面, // 能够重写成以下例子, 两者意义相同 function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // 用 const, let就不同了 function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; }
var
的状况同样function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function var anonymous = function () { console.log('anonymous function expression'); }; }
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'); }; }
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
===
与 !==
,而不是 ==
与 !=
eslint
if
条件语句的强制 toBoolean
if
条件语句的强制 toBoolean
老是遵循如下规则:
Objects
老是计算成 true
Undefined
老是计算 成 false
Null
老是计算成 false
Booleans
计算成它自己的布尔值Numbers
+0
、-0
或者 NaN
老是计算成 false
true
Strings
false
true
注意:NaN
是不等于NaN
的,请使用isNaN()
检测。
if ([0] && []) { // true // 数组(即便是空数组)是对象,对象会计算成true } console.log(NaN === NaN) // => false console.log(isNaN(NaN)) // => true
// bad if (isValid === true) { // ... } // good if (isValid) { // ... } // bad if (name) { // ... } // good if (name !== '') { // ... } // bad if (collection.length) { // ... } // good if (collection.length > 0) { // ... }
switch
的 case
与 default
分句中使用大括号建立语法声明区域eslint
语法声明在整个 switch
的代码块里均可见,可是只有当其被分配后才会初始化,他的初始化时当这个 case
被执行时才产生。 当多个 case
分句试图定义同一个事情时就出问题了
// 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
// bad const foo = maybe1 > maybe2 ? "bar" : value1 > value2 ? "baz" : null; // better const maybeNull = value1 > value2 ? 'baz' : null; const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // best const maybeNull = value1 > value2 ? 'baz' : null; const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
// 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
开发者须要以最显而易见的方式明确本身的意图与逻辑
// bad const foo = a && b < 0 || c > 0 || d + 1 === 0; // bad const bar = a ** b - 5 % d; // bad // 别人会陷入(a || b) && c 的迷惑中 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;
eslint
// 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
与 if
的关闭大括号在同一行eslint
// bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); }
if
语句中的 return
eslint
if
语句中老是须要用 return
返回,那么后续的 else
就不须要写了if
块中包含 return
,它后面的 else if
也包含了 return
,那就应该把 else if
的 return
分到多个 if
语句块中去。// 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; } }
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(); }
// bad !isRunning && startRunning(); // good if (!isRunning) { startRunning(); }
/** ... */
// bad // make() 基于传入的 `tag` 名返回一个新元素 // // @param {String} 标签名 // @return {Element} 新元素 function make(tag) { // ... return element; } // good /** * make() 基于传入的 `tag` 名返回一个新元素 * @param {String} 标签名 * @param {Element} 新元素 */ function make(tag) { // ... return element; }
//
将单行注释放在被注释区域上面。若是注释不是在第一行,那么注释前面就空一行
// bad const active = true; // is current tab // good // 当前激活状态的 tab const active = true; // bad function getType() { console.log('fetching type...'); // 设置默认 `type` 为 'no type' const type = this._type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // 设置默认 `type` 为 'no type' const type = this._type || 'no type'; return type; } // also good function getType() { // 设置默认 `type` 为 'no type' const type = this._type || 'no type'; return type; }
eslint
// bad //当前激活的 tab const active = true; // good // 当前激活的 tab const active = true; // bad /** *make() 基于传入的 `tag` 名返回一个新元素 *@param {String} 标签名 *@param {Element} 新元素 */ function make(tag) { // ... return element; } // good /** * make() 基于传入的 `tag` 名返回一个新元素 * @param {String} 标签名 * @param {Element} 新元素 */ function make(tag) { // ... return element; }
FIXME
与 TODO
当你的注释须要向注释阅读者或者代码的后继开发者明确的表述一种指望时,应该积极使用 FIXME
以及 TODO
前缀,这有助于其余的开发理解你指出的须要从新访问的问题,也方便本身往后有时间的时候再次回顾当时没有解决或者计划去作而没有作的事情。
FIXME
:这里有一个问题,如今尚未影响大局,可是更但愿解决这个问题或者找到一个更优雅的方式去实现TODO
:计划在这里去实现某些功能,如今尚未实现// 使用 FIXME: class Calculator extends Abacus { constructor() { super(); // FIXME: 不该该在此处使用全局变量 total = 0; } } // 使用 TODO: class Calculator extends Abacus { constructor() { super(); // TODO: total 应该应该从一个参数中获取并初始化 this.total = 0; } }
eslint
// bad function foo() { ∙∙∙∙const name; } // bad function bar() { ∙const name; } // good function baz() { ∙∙const name; }
eslint
// 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', });
eslint
在控制语句( if
, while
等)的圆括号前空一格。在函数调用和定义时,参数列表和函数名之间不空格。
// bad if(isJedi) { fight (); } // good if (isJedi) { fight(); } // bad function fight () { console.log ('Swooosh!'); } // good function fight() { console.log('Swooosh!'); }
eslint
// bad const x=y+5; // good const x = y + 5;
eslint
// bad function doSmth() { var foo = 2; }
// good function doSmth() { var foo = 2; }\n
eslint
// 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 if (foo) { return bar; } return baz; // good if (foo) { return bar; } return baz; // bad const obj = { foo() { }, bar() { }, }; return obj; // good const obj = { foo() { }, bar() { }, }; return obj; // bad const arr = [ function foo() { }, function bar() { }, ]; return arr; // good const arr = [ function foo() { }, function bar() { }, ]; return arr;
eslint
// bad function bar() { console.log(foo); } // also bad if (baz) { console.log(qux); } else { console.log(foo); } // good function bar() { console.log(foo); } // good if (baz) { console.log(qux); } else { console.log(foo); }
eslint
// 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
// bad const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // good, 逗号分隔符仍是要空格的 const foo = [1, 2, 3]; console.log(foo[0]);
eslint
// bad const foo = {clark: 'kent'}; // good const foo = { clark: 'kent' };
eslint
为了确保代码的人类可读性与可维护性,代码行应避免超过必定的长度
// bad const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // bad $.ajax({ method: 'POST', url: 'https://parcmg.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // good const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // good $.ajax({ method: 'POST', url: 'https://apis.parcmg.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.'));
eslint
// bad function foo() {return true;} if (foo) { bar = 0;} // good function foo() { return true; } if (foo) { bar = 0; }
,
前不要空格,,
后须要空格eslint
// bad var foo = 1,bar = 2; var arr = [1 , 2]; // good var foo = 1, bar = 2; var arr = [1, 2];
eslint
// bad obj[foo ] obj[ 'foo'] var x = {[ b ]: a} obj[foo[ bar ]] // good obj[foo] obj['foo'] var x = { [b]: a } obj[foo[bar]]
eslint
// bad func (); func (); // good func();
key
与 value
之间要有空格eslint
// bad var obj = { "foo" : 42 }; var obj2 = { "foo":42 }; // good var obj = { "foo": 42 };
eslint
eslint
// bad var x = 1; var y = 2; // good var x = 1; var y = 2;
eslint
// bad const story = [ once , upon , aTime ]; // good const story = [ once, upon, aTime, ]; // bad const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // good const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', };
eslint
就算项目有可能运行在旧版本的浏览器中,可是像Babel
这样的转换器都会在转换代码的过程当中删除这些多余逗号,因此,大胆使用它,彻底不会有反作用产生,相反的,他能让咱们更方便的给对象或者多行数组增长、删除属性或者元素,同时,还能让咱们的git diffs
更清洁。
// bad - 没有结尾逗号的 git diff const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // good - 有结尾逗号的 git diff const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], };
// bad const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // good const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // bad function createHero( firstName, lastName, inventorOf ) { // does nothing } // good function createHero( firstName, lastName, inventorOf, ) { // does nothing } // good (在一个 "rest" 元素后面,绝对不能出现逗号) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // bad createHero( firstName, lastName, inventorOf ); // good createHero( firstName, lastName, inventorOf, ); // good (在一个 "rest" 元素后面,绝对不能出现逗号) createHero( firstName, lastName, inventorOf, ...heroArgs )
eslint
当 JavaScript 遇到没有分号结尾的一行,它会执行 自动插入分号 Automatic Semicolon Insertion 这一规则来决定行末是否加分号。若是JavaScript在你的断行里错误的插入了分号,就会出现一些古怪的行为。当新的功能加到JavaScript里后, 这些规则会变得更复杂难懂。显示的结束语句,并经过配置代码检查去捕获没有带分号的地方能够帮助你防止这种错误。
// bad (function () { const name = 'Skywalker' return name })() // good (function () { const name = 'Skywalker'; return name; }()); // good, 行首加分号,避免文件被链接到一块儿时当即执行函数被当作变量来执行。 ;(() => { const name = 'Skywalker'; return name; }());
String
进行字符类型转换eslint
// => this.reviewScore = 9; // bad const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string" // bad const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() // bad const totalScore = this.reviewScore.toString(); // 不保证返回string // good const totalScore = String(this.reviewScore);
Number
进行数字类型转换eslint
使用 parseInt
转换 string
一般都须要带上基数。
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);
若是你感受 parseInt
知足不要你的需求,想使用移位进行运算,那么你必定要写明白,这是由于 性能问题,同时,你还须要注意,数字使用 64 位 表示的,但移位运算经常返回的是 32 位的整形,移位运算对于大于 32 位的整数会致使一些 意外行为,最大的32位整数是 2,147,483,647
。
// good /** * parseInt是代码运行慢的缘由 * 用Bitshifting将字符串转成数字使代码运行效率大幅增加 */ const val = inputValue >> 0; 2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647
const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // best const hasAge = !!age;
eslint
// bad function q() { // ... } // good function query() { // ... }
eslint
// bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {}
eslint
// 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', });
eslint
JavaScript 没有私有属性或私有方法的概念。尽管前置下划线一般的概念上意味着“private”,事实上,这些属性是彻底公有的,所以这部分也是你的API的内容。这一律念可能会致使开发者误觉得更改这个不会致使崩溃或者不须要测试。 若是你想要什么东西变成“private”,那就不要让它在这里出现。
// bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // good this.firstName = 'Panda';
this
用箭头函数或函数绑定——Function#bind
// bad function foo() { const self = this; return function () { console.log(self); }; } // bad function foo() { const that = this; return function () { console.log(that); }; } // good function foo() { return () => { console.log(this); }; }
export
模块名以及 import
模块名一致// file 1 contents class CheckBox { // ... } export default CheckBox; // file 2 contents export default function fortyTwo() { return 42; } // file 3 contents export default function insideDirectory() {} // in some other file // bad import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // bad import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // good import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ supports both insideDirectory.js and insideDirectory/index.js
export default
一个函数时、函数名小驼峰式,文件与函数名一致function makeStyleGuide() { // ... } export default makeStyleGuide;
export
一个结构体、类、单例、函数库或者对象时,使用大驼峰式const Helpers = { guid: () => return uuid(), }; export default Helpers;
名字是给人读的,不是为了适应电脑的算法
// bad import SmsContainer from './containers/SmsContainer'; // bad const HttpRequests = [ // ... ]; // good import SMSContainer from './containers/SMSContainer'; // good const HTTPRequests = [ // ... ]; // best import TextMessageContainer from './containers/TextMessageContainer'; // best const Requests = [ // ... ];
const
定义的, 保证不能被改变通常咱们都将项目的全局参数使用这种 全大写+下划线分隔的常量 来定义一些系统配置参数导出,好比const LIST_VIEW_PAGE_SIZE = 10
能够表示列表页每次加载10条数据;若是导出项目是一个对象,那么必须保证这个对象的全部属性都是不可变的,同时,它的属性再也不是全大写,而是使用小写驼峰式。
// bad const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file'; // bad export const THING_TO_BE_CHANGED = 'should obviously not be uppercased'; // bad export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables'; // --- // allowed but does not supply semantic value export const apiKey = 'SOMEKEY'; // better in most cases export const API_KEY = 'SOMEKEY'; // --- // bad - unnecessarily uppercases key while adding no semantic value export const MAPPING = { KEY: 'value' }; // good export const MAPPING = { key: 'value' };
因为 JavaScript 的 getters/setters
是有反作用的,并且会让他人在查看代码的时候难以理解,后期也会难以维护,因此不推荐使用访问器函数,若是非要使用,可使用本身实现的 getVal()
与 setVal()
。
// bad class Dragon { get age() { // ... } set age(value) { // ... } } // good class Dragon { getAge() { // ... } setAge(value) { // ... } }
isVal()
或者 hasVal()
。// bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; }
get()
与 set()
,那么它们二者必须同时使用class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } }
当你须要向事件附加数据时,将数据封装成为一个对象,而不是使用原始值,这会使得之后能够很方便的增长附加值的字段。
// bad $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingID) => { // do something with listingID });
而是:
// good $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // do something with data.listingID });
$
前缀// bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar'); // good const $sidebarBtn = $('.sidebar-btn');
// bad function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); }
$('.sidebar ul')
或者子父级查询 $('.sidebar > ul')
find
方法// bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide();