好的代码不仅仅是能够跑起来的代码,更是能够被其他人轻松浏览、重用和重构的代码,因为代码除了实现性能外,大部分的工夫都是要被你或是团队其余成员保护的。
尽管本文次要专一于编写洁净整洁的 JavaScript ES6 代码,并且不与任何框架相干,然而上面将要提到的绝大多数示例也实用于其余语言,另外,上面的示例也次要是从 Robert C. Martin 的书 Clean Code 中所驳回的倡议,也不意味着要严格遵守。
变量
应用有意义的名字
变量的命名应该是描述性并且应该是有意义的,教训法令是大多数 JavaScript 变量应该应用 驼峰命名法
(camelCase)。
// 谬误 ❌
const foo = "JDoe@example.com";
const bar = "John";
const age = 23;
const qux = true;
// 正确 ✅
const email = "John@example.com";
const firstName = "John";
const age = 23;
const isActive = true
留神,布尔类型的变量名通常像是在答复问题,例如:
isActive
didSubscribe
hasLinkedAccount
防止增加不必要的上下文
在特定的对象或类中不须要加冗余的上下文
// 谬误 ❌
const user = {
userId: "296e2589-7b33-400a-b762-007b730c8e6d",
userEmail: "JDoe@example.com",
userFirstName: "John",
userLastName: "Doe",
userAge: 23,
};
user.userId;
// 正确 ✅
const user = {
id: "296e2589-7b33-400a-b762-007b730c8e6d",
email: "JDoe@example.com",
firstName: "John",
lastName: "Doe",
age: 23,
};
user.id;
防止硬编码
确保申明有意义且可搜寻的常量,而不是间接应用一个常量值,全局变量倡议应用 蛇形命名法
(SCREAMING_SNAKE_CASE)
// 谬误 ❌
setTimeout(clearSessionData, 900000);
// 正确 ✅
const SESSION_DURATION_MS = 15 * 60 * 1000;
setTimeout(clearSessionData, SESSION_DURATION_MS);
函数
应用描述性的命名
函数名能够很长,长到足以形容它的作用,个别函数中都含有 动词
来形容它所做的事件,然而返回布尔值的函数是个例外,个别是一个答复“是”或者“否”的问题模式,另外函数命名也应该是驼峰命名法。
// 谬误 ❌
function toggle() {// ...}
function agreed(user) {// ...}
// 正确 ✅
function toggleThemeSwitcher() {// ...}
function didAgreeToAllTerms(user) {// ...}
应用默认参数
间接应用默认值比短路语法或者在函数中退出判断语句更加简洁,值得注意的是,短路语法实用于所有被认为是 false
的值,例如 false
、null
、undefined
、''
、""
、0
和 NaN
,而默认参数仅替换 undefined
。
// 谬误 ❌
function printAllFilesInDirectory(dir) {
const directory = dir || "./";
// ...
}
// 正确 ✅
function printAllFilesInDirectory(dir = "./") {// ...}
限度参数个数
这一条有争议,函数的参数应该 不多于 2 个
,意思是函数的参数为 0 个 1 个或者 2 个,如果须要第三个参数的话阐明:
- 函数须要拆分
- 能够把相干参数聚合成对象传递
// 谬误 ❌
function sendPushNotification(title, message, image, isSilent, delayMs) {// ...}
sendPushNotification("New Message", "...", "http://...", false, 1000);
// 正确 ✅
function sendPushNotification({title, message, image, isSilent, delayMs}) {// ...}
const notificationConfig = {
title: "New Message",
message: "...",
image: "http://...",
isSilent: false,
delayMs: 1000,
};
sendPushNotification(notificationConfig);
不要在一个函数中做太多事件
原则上一个函数只做一件事,这一准则能够很好地帮忙咱们升高函数的体积和复杂度,也能更好的测试、调试和重构,一个函数的代码行数是判断这个函数是否做太多事件的一个指标,个别倡议一个函数长度小于 20~30 行。
// 谬误 ❌
function pingUsers(users) {users.forEach((user) => {const userRecord = database.lookup(user);
if (!userRecord.isActive()) {ping(user);
}
});
}
// 正确 ✅
function pingInactiveUsers(users) {users.filter(!isUserActive).forEach(ping);
}
function isUserActive(user) {const userRecord = database.lookup(user);
return userRecord.isActive();}
防止应用 flag 变量
flag 变量意味着函数能够被进一步简化
// 谬误 ❌
function createFile(name, isPublic) {if (isPublic) {fs.create(`./public/${name}`);
} else {fs.create(name);
}
}
// 正确 ✅
function createFile(name) {fs.create(name);
}
function createPublicFile(name) {createFile(`./public/${name}`);
}
不要反复本人(DRY)
反复的代码不是一个好的信号,你复制粘贴了 N 次,下次这部分代码批改的时候你就要就要同时批改 N 个中央。
// 谬误 ❌
function renderCarsList(cars) {cars.forEach((car) => {const price = car.getPrice();
const make = car.getMake();
const brand = car.getBrand();
const nbOfDoors = car.getNbOfDoors();
render({price, make, brand, nbOfDoors});
});
}
function renderMotorcyclesList(motorcycles) {motorcycles.forEach((motorcycle) => {const price = motorcycle.getPrice();
const make = motorcycle.getMake();
const brand = motorcycle.getBrand();
const seatHeight = motorcycle.getSeatHeight();
render({price, make, brand, seatHeight});
});
}
// 正确 ✅
function renderVehiclesList(vehicles) {vehicles.forEach((vehicle) => {const price = vehicle.getPrice();
const make = vehicle.getMake();
const brand = vehicle.getBrand();
const data = {price, make, brand};
switch (vehicle.type) {
case "car":
data.nbOfDoors = vehicle.getNbOfDoors();
break;
case "motorcycle":
data.seatHeight = vehicle.getSeatHeight();
break;
}
render(data);
});
}
防止副作用
在 JavaScript 中,首选的模式应该是函数式而不是命令式,换句话说,要保障函数的纯正性,副作用能够批改共享的状态和资源,会导致代码的不稳固和难以测试,排查问题也会特地辣手,所有的副作用应该集中管理。如果须要批改全局状态能够定义一个对立的服务去批改。
// 谬误 ❌
let date = "21-8-2021";
function splitIntoDayMonthYear() {date = date.split("-");
}
splitIntoDayMonthYear();
// Another function could be expecting date as a string
console.log(date); // ['21', '8', '2021'];
// 正确 ✅
function splitIntoDayMonthYear(date) {return date.split("-");
}
const date = "21-8-2021";
const newDate = splitIntoDayMonthYear(date);
// Original vlaue is intact
console.log(date); // '21-8-2021';
console.log(newDate); // ['21', '8', '2021'];
另外,如果一个可变的对象作为一个函数的参数传递进去,返回这个参数的时候应该是这个参数的克隆对象而不是间接把这个对象批改后返回。
// 谬误 ❌
function enrollStudentInCourse(course, student) {course.push({ student, enrollmentDate: Date.now() });
}
// 正确 ✅
function enrollStudentInCourse(course, student) {return [...course, { student, enrollmentDate: Date.now() }];
}
并发
防止应用回调
回调函数 太乱了,所以 ES6 给咱们提供了 Promise 容许咱们应用链式的回调,当然 Async/Await 提供了更简洁的计划,能够让咱们写出更加线性的代码
// 谬误 ❌
getUser(function (err, user) {getProfile(user, function (err, profile) {getAccount(profile, function (err, account) {getReports(account, function (err, reports) {sendStatistics(reports, function (err) {console.error(err);
});
});
});
});
});
// 正确 ✅
getUser()
.then(getProfile)
.then(getAccount)
.then(getReports)
.then(sendStatistics)
.catch((err) => console.error(err));
// 正确 ✅✅
async function sendUserStatistics() {
try {const user = await getUser();
const profile = await getProfile(user);
const account = await getAccount(profile);
const reports = await getReports(account);
return sendStatistics(reports);
} catch (e) {console.error(err);
}
}
错误处理
解决抛出的异样和 rejected 的 Promise
正确的解决异样能够使咱们的代码更加的简装,也会更不便的排查问题。
// 谬误 ❌
try {// 可能出错的代码} catch (e) {console.log(e);
}
// 正确 ✅
try {// 可能出错的代码} catch (e) {
// 比 console.log 更适合
console.error(e);
// 告诉用户
alertUserOfError(e);
// 告诉服务器
reportErrorToServer(e);
// 应用自定义的异样解决
throw new CustomError(e);
}
正文
只为简单的逻辑增加正文
不要适度的增加正文,只须要为简单的逻辑增加即可。
// 谬误 ❌
function generateHash(str) {
// 哈希变量
let hash = 0;
// 获取字符串的长度
let length = str.length;
// 如果长度是空的就返回
if (!length) {return hash;}
// 遍历字符
for (let i = 0; i < length; i++) {
// 获取字符 code
const char = str.charCodeAt(i);
// 为 hash 赋值
hash = (hash << 5) - hash + char;
// 转换为 32 位的整型
hash &= hash;
}
}
// 正确 ✅
function generateHash(str) {
let hash = 0;
let length = str.length;
if (!length) {return hash;}
for (let i = 0; i < length; i++) {const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash; // 转换为 32 位的整型
}
return hash;
}
版本控制
齐全没有必要写代码的批改历史,版本治理(比方 git)曾经帮咱们做了这些事件。
// 谬误 ❌
/**
* 2021-7-21: Fixed corner case
* 2021-7-15: Improved performance
* 2021-7-10: Handled mutliple user types
*/
function generateCanonicalLink(user) {// const session = getUserSession(user)
const session = user.getSession();
// ...
}
// 正确 ✅
function generateCanonicalLink(user) {const session = user.getSession();
// ...
}
本文简短的探讨了一些能够进步 ES6 代码可读性的准则和办法,绝大多数准则能够利用到其余编程语言上,应用这些准则可能会比拟花工夫,然而久远来看它能够保障你代码的可读性可扩展性。