js代码整洁之道(重构)
最近在重构项目代码,结合《重构》和《代码整洁之道》以及Airbnb JavaScript总结一下,重构一下我们的代码,让我们的代码更加整洁,精简。话不多说,燥起来吧!!
1. 尽量使用const定义变量,避免使用var
这可以确保你不能重新赋值变量,也可以避免导致错误和让人难以理解的代码
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
2. 如果要重新赋值定义变量,尽量使用let
let和const块级作用域,ver函数作用域,可以避免重复定义造成的错误
// bad
var count = 1;
if (true) {
count += 1;
}
// good
let count = 1;
if (true) {
count += 1;
}
3. object如果使用动态属性,尽量使用计算属性
一个地方可以定义所有的属性
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,
};
4. 使用object属性简写
const lukeSkywalker = 'Luke Skywalker';
// bad
const obj = {
lukeSkywalker: lukeSkywalker,
};
// good
const obj = {
lukeSkywalker,
};
5. 取值时使用解构
const userInfo = {
age:18,
name: '小明',
address: '广东深圳'
}
// bad
this.userName = userInfo.name;
this.useAge = userInfo.age;
this.useAddress = userInfo.address;
// good
const { age, name, address } = userInfo
this.userName = name;
this.useAge = age;
this.useAddress = address;
// Array
const list = [ '小明', '小红', '小花' ]
// bad
const xiaohong = list[1]; // 小红
// good
const [ , xh, ,] = list
const xiaohong = xh; // 小红
6. 使用语义化命名变量
// bad
const yyyymmdstr = moment().format("YYYY/MM/DD");
// good
const currentDate = moment().format("YYYY/MM/DD");
// bad
getUserInfo();
getClientData();
getCustomerRecord();
//good
getUser();
7. 使用常量命名
方便搜索和解释来源
// bad
// 86400000 怎么来的?
setTimeout(blastOff, 86400000);
// good
const MILLISECONDS_IN_A_DAY = 60 * 60 * 24 * 1000; //86400000;
setTimeout(blastOff, MILLISECONDS_IN_A_DAY);
8. 避免使用精神映射
// bad
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(l => {
doSomeThings(l); // l 是干什么的
});
//good
const locations = ["Austin", "New York", "San Francisco"];
locations.forEach(location => {
doSomeThings(location);
});
9. 使用默认参数代替条件
// bad
function createUser(name) {
const userName = name || "小明";
// ...
}
// good
function createUser(name = "小明") {
// ...
}
10. 避免过多的函数参数
// bad
function createMenu(title, body, buttonText, cancellable) {
// ...
}
createMenu("Foo", "Bar", "Baz", true);
// good
function createMenu({ title, body, buttonText, cancellable }) {
// ...
}
createMenu({
title: "Foo",
body: "Bar",
buttonText: "Baz",
cancellable: true
})
11. 函数应该只做一件事情
// bad
function emailClients(clients) {
clients.forEach(client => {
const clientRecord = database.lookup(client);
if (clientRecord.isActive()) {
email(client);
}
});
}
// good
function emailActiveClients(clients) {
clients.filter(isActiveClient).forEach(email);
}
function isActiveClient(client) {
const clientRecord = database.lookup(client);
return clientRecord.isActive();
}
12. 不要使用全局函数
可以避免使用相同的操作是发生冲突
// bad
Array.prototype.diff = function diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
};
// good
class SuperArray extends Array {
diff(comparisonArray) {
const hash = new Set(comparisonArray);
return this.filter(elem => !hash.has(elem));
}
}
13. 使用函数式编程代替命令式编程
// bad
const orderList = [
{
name: "可乐",
price: 5
},
{
name: "雪碧",
price: 10
}
];
let totalPrice = 0;
for(let i = 0; i < orderList.length;i++) {
totalPrice += orderList[i].price
}
// good
const orderList = [
{
name: "可乐",
price: 5
},
{
name: "雪碧",
price: 10
}
];
const totalPrice = orderList.reduce(
(total, item) => (total + item.price),0);
14. 把条件语句封装成函数
// bad
if (order.state === 'already' && isEmpty(orderList)){ }
// good
function getOrderInfo (){
return order.state === 'already' && isEmpty(orderList)
}
if(getOrderInfo()){ }
15. 以卫语句取代嵌套条件表达式
// bad
function getOrderPrice() {
let result;
if (price) {
result = getPrice();
} else {
if(adPrice){
result = getAdPrice();
}else{
if(stockPrice) {
result = getStockPrice();
}else {
result = getOtherPrice();
}
}
}
return result;
}
// good
function getOrderPrice() {
if (price) return getPrice();
if (adPrice) return getAdPrice();
if (stockPrice) getStockPrice();
return getOtherPrice();
}
16. 以管道取代循环
// bad
const names = [];
for (const i of listMans) {
if (i.obj === 'staff') {
names.push(i.nam);
}
}
// good
const names = listMans
.filter(list=>list.obj === 'staff')
.map(item=>item.name)
17. 多个返回值时尽量使用对象解构
// 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);
18. 返回string类型的时候使用字符串模板
// bad
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// good
function sayHi(name) {
return `How are you, ${name}?`;
}
19. 不要导出可变的绑定
// bad
let foo = 3;
export { foo };
// good
const foo = 3;
export { foo };
20. 如果导出单个模块,尽量使用默认导出
// bad
export function foo() {}
// good
export default function foo() {}
21. 获取属性的使用 [ ]
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
22. 多个或者条件,使用Array.includes(option)
// bad
function isShowMenu() {
const routerName = this.$route.name;
const list = ['collect', 'admin', 'order', 'other'];
return routerName === 'collect' || routerName === 'admin'
|| routerName === 'order' || routerName === 'other'
}
// good
function isShowMenu() {
const routerName = this.$route.name;
const list = ['collect', 'admin', 'order', 'other'];
return list.includes(routerName);
}
23. 避免使用不必要的三元表达式
// 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;
24. 提前让函数退出代替嵌套条件分支
if如果有执行ruturn,else就可以不要了
// 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;
}
}
25. 如果变量过长,尽量使用换行
// bad
if (foo === 123
&& bar === 'abc') {
doThing();
}
// good
if (foo === 123 && bar === 'abc') {
doThing();
}
// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
doThing();
}
// good
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
doThing();
}
26. 导出的文件名尽量和路劲文件名一致
// bad
import CheckBox from './checkBox';
import FortyTwo from './FortyTwo';
import InsideDirectory from './InsideDirectory';
// good
import CheckBox from './CheckBox';
import fortyTwo from './fortyTwo';
import insideDirectory from './insideDirectory';
27. 首字母缩写和首字母缩写应该总是全部大写,或者全部小写
// bad
import SmsContainer from './containers/SmsContainer';
// good
import SMSContainer from './containers/SMSContainer';
// best
import TextMessageContainer from './containers/TextMessageContainer';
28. 判断是否有属性是boolean,尽量使用isVal()或者hasVal
// bad
if (!dragon.age()) {
return false;
}
// good
if (!dragon.hasAge()) {
return false;
}
29. 合理使用循环
// bad
// 判断是什么浏览器
function getBrowser(){
const str = navigator.userAgent;
if (str.includes('QQBrowser')) {
return 'qq';
} else if (str.includes('Chrome')) {
return 'chrome';
} else if (str.includes('Safari')) {
return 'safri';
} else if (str.includes('Firefox')) {
return 'firefox';
} else if(explorer.indexOf('Opera') >= 0){
return 'opera';
} else if (str.includes('msie')) {
return 'ie';
} else {
return 'other';
}
};
// good
// 循环判断,将对应关系抽象为配置,更加清晰明确
function getBrowser(){
const str = navigator.userAgent;
const list = [
{key: 'QQBrowser', browser: 'qq'},
{key: 'Chrome', browser: 'chrome'},
{key: 'Safari', browser: 'safari'},
{key: 'Firefox', browser: 'firefox'},
{key: 'Opera', browser: 'opera'},
{key: 'msie', browser: 'ie'},
];
for (let i = 0; i < list.length; i++) {
const item = list[i];
if (str.includes(item.key)) {return item.browser};
}
return 'other';
}