更合理的方式写 JavaScript
原文看 这里 ,收录在此为便于查阅。
类型
-
1.1 基本类型:直接存取。
字符创
数值
布尔类型
null
undefined
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
-
1.2 复杂类型:通过引用的方式存取。
对象
数组
函数
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9,9
引用
-
2.1 对所有的引用类型使用
const
; 不要使用var
。为什么?这确保你无法对引用类型重新赋值,也不会导致出现 bug 或难以理解。
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
-
2.2 如果你一定需要可变动的引用,使用
let
替代var
。为什么?因为
let
是块级作用域,而var
是函数作用域。// bad var count = 1; if(true){ count += 1; } // good, use the let let count = 1; if(true){ count += 1; }
-
2.3 注意
let
和const
都是块级作用域。{ let a = 1; const b = 1; } console.log(a);//ReferenceError console.log(b);//ReferenceError
对象
-
3.1 使用字面值创建对象。
// bad const item = new Object(); // good const item = {};
-
3.2 如果你的代码在浏览器环境下执行,别使用 保留字 作为键值。但是在ES6模块或服务器端使用则没有问题。
// bad const person = { default: { name: 'peter' }, private: true } // good const person = { defaults: { name: 'peter' }, hidden: true }
-
3.3 使用同义字替换需要使用的保留字。
// bad const person = { class: 'info' } // bad const person = { kclass: 'info' } // good const person = { type: 'info' }
-
3.4 创建有动态属性名的对象时,使用可被计算的属性名称。
为什么?因为这样让你在一个地方定义所有的对象属性。
function getKey(k){ return `a key named ${k}`; } // bad const obj = { id: 5, name: 'peter' } obj[getKey['enabled']] = true; // good const obj = { id: 5, name: 'peter', [getKey['enabled']]: true; }
-
3.5 使用对象方法的简写。
// bad const atom = { value: 1, addValue: function(value){ return atom.value + value; } } // good const atom = { value: 1, addValue(value){ return atom.value + value; } }
-
3.6 使用对象属性值的简写。
const name = 'peter'; // bad const obj = { name: name } // good const obj = { name }
-
3.7 在对象属性声明前把简写的属性分组。
const name = 'name'; const age = 'age'; // bad const obj = { name, sex: '男', age, height: '170' } // good const obj = { name, age, sex: '男', height: '170' }
数组
-
4.1 使用字面值创建数组。
// bad const items = new Array(); // good const items = [];
-
4.2 向数组添加元素时使用 Array#push 替代直接赋值。
const names = []; // bad names[0] = 'peter'; // good names.push('peter');
-
4.3 使用拓展运算符
...
复制数组。const items = ['xiaoxin', 'xiaoqiang', 'xiaowanzi']; // bad const itemsCopy = []; for(let i = 0; i < items.length; i++){ itemsCopy[i] = items[i]; } // good const itemsCopy = [...items];
解构
-
5.1 使用解构存取和使用多属性对象。
为什么?因为解构能减少临时引用属性。
// bad function getFullName(user){ const fileName = 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}` }
-
5.2 对数组使用解构赋值。
cosnt arr = [1,2,3,4]; // bad const first = arr[0]; const second = arr[1]; //good const [first, second] = arr;
Strings
-
6.1 字符串使用单引号
''
。// bad const name = "peter"; // good const name = 'peter';
-
6.2 字符串超过80个字节使用字符串连接号连接。
// bad const info = 'American colleges and universities awarded about one million seven hundred thousand bachelor\'s degrees in the school year ending in twenty ten.'; // good const info = 'American colleges and universities awarded about one million seven hundred thousand bachelor\'s ' + 'degrees in the school year ending in twenty ten.'
-
6.3 程序化生成字符串时,使用模板字符串替代字符串连接。
// bad function sayHi(name){ return 'How are you, ' + name + '?'; } // good function sayHi(name){ return `How are you ${name}?` }
函数
-
7.1 使用函数声明代替函数表达式。
为什么?因为函数声明是可命名的,所以他们在调用栈中更容易被识别。此外,函数声明会把整个函数提升,而函数表达式只会把函数的引用变量名提升。
// bad const foo = function(){ } // good function foo(){ }
-
7.2 函数表达式:
//立即调用的函数表达式 (() => { console.log('Welcome to my home!'); })()
7.3 永远不要在一个非函数代码块(
if
,while
等)中声明一个函数,把那个函数赋给一个变量。浏览器允许你这么做,但它们的解析表现不一致。-
7.4 直接给函数的参数指定默认值,不要使用一个变化的函数参数。
// bad function handleThings(opts){ opts = opts || {}; } // still bad function handleThings(opt){ if(opts === void 0){ opts = {}; } } // good function handleThings(opts = {}){ }
箭头函数
-
8.1 当你必须使用函数表达式(或传递一个匿名函数)时,使用箭头函数符号。
为什么?因为箭头函数创造了新的
this
执行环境,通常情况下都能满足你的需求,而且这样的写法更为简洁。// bad [1, 2, 3].map(function (x) { return x * x; }) // good [1, 2, 3].map(x => { return x * x; })
-
8.2 如果一个函数适合用一行写出并且只有一个参数,那就把花括号、圆括号和
return
都省略掉。如果不是,那就不要省略。// good [1, 2, 3].map(x => x * x); // good [1, 2, 3].map((total, x) => { return total + x; })
构造函数
-
9.1 总是使用 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; } }
-
9.2 使用
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]; } }
-
9.3 方法可以返回 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);
-
9.4 可以写一个自定义的 toString() 方法,但要确保它能正常运行并且不会引起副作用。
class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }
模块
-
10.1 总是使用(
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;
-
10.2 不要使用通配符 import。
为什么?这样能确保你只有一个默认的 export。
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
-
10.3 不要从 import 中直接 export。
// bad // filename es6.js export { es6 as default } from './airbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
属性
-
11.1 使用
.
来访问对象的属性。const student = { name: 'peter', age: 27 } // bad const person = student['name']; // good const person = student.name;
-
11.2 当通过变量访问属性时使用中括号
[]
.const student = { name: 'peter', age: 27 } function getName(name){ return student[name]; } const name = getName('name');
变量
-
12.1 一直使用 const 来声明变量,如果不这样就会产生全局变量。
// bad name = 'peter'; // good const name = 'peter';
-
12.2 使用 const 声明每一个变量。
// bad const name = 'peter', age = 27; // good const name = 'peter'; const age = 27;
-
12.3 在你需要的地方给变量赋值,但请他它们放在一个合理的位置。
// bad function(hasName) { const name = getName(); if (!hasName) { return false; } this.setFirstName(name); return true; } // good function(hasName) { if (!hasName) { return false; } const name = getName(); this.setFirstName(name); return true; }
比较运算符 & 等号
13.1 优先使用
===
和!==
而不是==
和!=
。-
13.2 使用简写。
// bad if (name !== '') { } // good if (name) { } // bad if (name.length > 0) { } // good if (name.length) { }
逗号
-
14.1 行首逗号,不需要。
// 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', };
-
14.2 行尾逗号,需要。
为什么? 这会让
git diff
更干净。另外,像babel
这样的转译器会移除结尾多于的逗号,也就是说你不必担心老旧浏览器的结尾逗号问题。// bad const student = { name: 'peter', age: 27 } // good const student = { name: 'peter', age: 27, }
分号
-
15.1 使用分号
// bad (function() { const name = 'Skywalker' return name })() // good (() => { const name = 'Skywalker'; return name; })(); // good (防止函数在两个 IIFE 合并时被当成一个参数) ;(() => { const name = 'Skywalker'; return name; })();
类型转换
-
16.1 在语句开始时执行类型转换。
// => this.reviewScore = 9; // bad const totalScore = this.reviewScore + ''; // good const totalScore = String(this.reviewScore);
-
16.2 字符串
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);
-
16.3 对数字使用
parsetInt
转换,病带上类型转换的基数。// good /** * 使用 parseInt 导致我的程序变慢, * 改成使用位操作转换数字快多了。 */ const val = inputValue >> 0;
-
16.4 布尔
const age = 0; // bad const hasAge = new Boolean(age); // good const hasAge = Boolean(age); // good const hasAge = !!age;
命名规则
-
17.1 避免单字母命名。命名应具备描述性。
// bad function q() { // ...stuff... } // good function query() { // ..stuff.. }
-
17.2 使用驼峰式命名对象、函数和实例。
// bad const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // good const thisIsMyObject = {}; function thisIsMyFunction() {}
-
17.3 使用帕斯卡式命名构造函数或类。
// 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', });
-
17.4 使用下划线 _ 开头命名私有属性。
// bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; // good this._firstName = 'Panda';
-
17.5 别保存 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); }; }
-
17.6 如果你的文件只输出一个类,那你的文件名必须和类名完全保持一致。
// file contents class CheckBox { // ... } export default CheckBox; // in some other file // bad import CheckBox from './checkBox'; // bad import CheckBox from './check_box'; // good import CheckBox from './CheckBox';
-
17.7 当你导出默认的函数时使用驼峰式命名。你的文件名必须和函数名完全保持一致。
function makeStyleGuide() { } export default makeStyleGuide;
-
17.8 当你导出单例、函数库、空对象时使用帕斯卡式命名。
const AirbnbStyleGuide = { es6: { } }; export default AirbnbStyleGuide;