原文地址:JavaScript Clean Code - Best Practices
原作者:Milos Protic
介绍
如果你关心代码的本身以及编写方式,而只是担心代码是否有效的,你可以说你实践并且关心代码的规范性。专业的开发人员总是会考虑到自己便于将来还能可读和其他的团队人员,并不是仅仅考虑到运行的结果。你写的任何代码并不是只写一次,总会有其他的开发人员来做你写的代码,如果你写的不规范,而他则会痛苦不堪。希望这个人不是你。
总体来说,规范性的代码可以定义为不言自明,易于人理解且易于更改或者扩展的编写方式。
当你接到别人写的代码,第一个印象就是“这个代码什么玩意儿”,那你问自己,你还会有多少次愿意继续做别人写的代码呢?
"WTF is that?"
"WTF did you do here?"
"WTF is this for?"
(这个就不翻译了,自己感受下)
下图就是代表以上所述内容的流行图片
引用Robert C.Martin的一句话,这话应该会让你思考编写方式。
Even bad code can function. But if the code isn’t clean, it can bring a development organization to its knees.
在本文中,重点将在于js,这些原则也能用于其他的编程语言。
1、强类型检查
=== 代替 ==
0 == false // true
0 === false // false
2 == "2" // true
2 === "2" // false
// 例子
const value = "500";
if (value === 500) {
console.log(value);
// 没打印出来
}
if (value === "500") {
console.log(value);
// 打印出来了
}
2、变量
- 给变量命名时,应该要使变量名具有代表意图的象征,使人易于搜索并且容易理解。
Bad:
let daysSLV = 10;
let y = new Date().getFullYear();
let ok;
if (user.age > 30) {
ok = true;
}
//本人解释:y,ok这些什么玩意儿呢,30又是什么意思呢?
Good:
const MAX_AGE = 30; //哦,是最大的年龄
let daysSinceLastVisit = 10;
let currentYear = new Date().getFullYear();//哦,是当前年份
...
const isUserOlderThanAllowed = user.age > MAX_AGE;
- 不要在变量名中增加没必要额外的单词
Bad:
let nameValue;
let theProduct;
Good:
let name;
let product;
- 不要强制记忆变量名的上下文
Bad:
const users = ["John", "Marco", "Peter"];
users.forEach(u => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
// 这u什么玩意儿呢?
register(u);
});
Good:
const users = ["John", "Marco", "Peter"];
users.forEach(user => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
register(user);
});
- 变量名不要加上下文重复的单词
Bad:
const user = {
userName: "John",
userSurname: "Doe",
userAge: "28"
};
...
user.userName;
Good:
const user = {
name: "John",
surname: "Doe",
age: "28"
};
...
user.name;
3、函数
- 函数名应该是动词或者短语,代表某种行为,描述它们在做什么
Bad:
function notif(user) {
// implementation
}
Good:
function notifyUser(emailAddress) {
// implementation
}
- 避免使用大量的参数,理想的情况就是用两个或者更少的参数。参数越少,测试就越容易
Bad:
function getUsers(fields, fromDate, toDate) {
// implementation
}
Good:
function getUsers({ fields, fromDate, toDate }) {
// implementation
}
getUsers({
fields: ['name', 'surname', 'email'],
fromDate: '2019-01-01',
toDate: '2019-01-18'
});
- 函数应该使用默认参数,而不是条件语句
Bad:
function createShape(type) {
const shapeType = type || "cube";
// ...
}
Good:
function createShape(type = "cube") {
// ...
}
(这个的原因,可能有些人不明白的,在此放链接阮一峰es6入门-函数参数的默认值)
- 一个函数应该做一件事,避免在一个函数中执行多个操作。
Bad:
function notifyUsers(users) {
users.forEach(user => {
const userRecord = database.lookup(user);
if (userRecord.isVerified()) {
notify(user);
}
});
}
Good:
function notifyVerifiedUsers(users) {
users.filter(isUserVerified).forEach(notify);
}
function isUserVerified(user) {
const userRecord = database.lookup(user);
return userRecord.isVerified();
}
- 使用Object.assign设置默认对象。
Bad:
const shapeConfig = {
type: "cube",
width: 200,
height: null
};
function createShape(config) {
config.type = config.type || "cube";
config.width = config.width || 250;
config.height = config.width || 250;
}
createShape(shapeConfig);
Good:
const shapeConfig = {
type: "cube",
width: 200
// Exclude the 'height' key
};
function createShape(config) {
config = Object.assign(
{
type: "cube",
width: 250,
height: 250
},
config
);
...
}
createShape(shapeConfig);
- 不要使用标志记作为参数,因为它们告诉你该函数正在做的比它应该做的更多。
Bad:
function createFile(name, isPublic) {
if (isPublic) {
fs.create(`./public/${name}`);
} else {
fs.create(name);
}
}
Good:
function createFile(name) {
fs.create(name);
}
function createPublicFile(name) {
createFile(`./public/${name}`);
}
- 不要污染全局变量。如果需要扩展现有对象,请使用ES6类和继承,而不是在现有对象的原型链上创建函数
Bad:
Array.prototype.myFunc = function myFunc() {
// implementation
};
Good:
class SuperArray extends Array {
myFunc() {
// implementation
}
}
4、条件语句
- 避免负面条件
Bad:
function isUserNotBlocked(user) {
// implementation
}
if (!isUserNotBlocked(user)) {
// implementation
}
Good:
function isUserBlocked(user) {
// implementation
}
if (isUserBlocked(user)) {
// implementation
}
- 使用条件语句尽量短点。这可能是微不足道的,但值得一提。此方法仅用于布尔值,并且如果您确定该值不是未定义的或为null。
Bad:
if (isValid === true) {
// do something...
}
if (isValid === false) {
// do something...
}
Good:
if (isValid) {
// do something...
}
if (!isValid) {
// do something...
}
- 尽可能避免switch分支,请改用多态和继承。
Bad:
class Car {
// ...
getMaximumSpeed() {
switch (this.type) {
case "Ford":
return this.someFactor() + this.anotherFactor();
case "Mazda":
return this.someFactor();
case "McLaren":
return this.someFactor() - this.anotherFactor();
}
}
}
Good:
class Car {
// ...
}
class Ford extends Car {
// ...
getMaximumSpeed() {
return this.someFactor() + this.anotherFactor();
}
}
class Mazda extends Car {
// ...
getMaximumSpeed() {
return this.someFactor();
}
}
class McLaren extends Car {
// ...
getMaximumSpeed() {
return this.someFactor() - this.anotherFactor();
}
}
5、Es6类
- 类是JavaScript中的新语法糖,跟原型对象一样,只是它现在看起来不同,你应该更喜欢它们而不是ES5的使用构造函数。
Bad:
const Person = function(name) {
if (!(this instanceof Person)) {
throw new Error("Instantiate Person with `new` keyword");
}
this.name = name;
};
Person.prototype.sayHello = function sayHello() { /**/ };
const Student = function(name, school) {
if (!(this instanceof Student)) {
throw new Error("Instantiate Student with `new` keyword");
}
Person.call(this, name);
this.school = school;
};
Student.prototype = Object.create(Person.prototype);
Student.prototype.constructor = Student;
Student.prototype.printSchoolName = function printSchoolName() { /**/ };
Good:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
/* ... */
}
}
class Student extends Person {
constructor(name, school) {
super(name);
this.school = school;
}
printSchoolName() {
/* ... */
}
}
- 使用方法链接,许多库如jQuery和Lodash都使用这种模式。因此,您的代码将不那么冗长。在你的类中,只需在每个函数的末尾返回它,你就可以将更多的类方法链接到它上面。
Bad:
class Person {
constructor(name) {
this.name = name;
}
setSurname(surname) {
this.surname = surname;
}
setAge(age) {
this.age = age;
}
save() {
console.log(this.name, this.surname, this.age);
}
}
const person = new Person("John");
person.setSurname("Doe");
person.setAge(29);
person.save();
Good:
class Person {
constructor(name) {
this.name = name;
}
setSurname(surname) {
this.surname = surname;
// Return this for chaining
return this;
}
setAge(age) {
this.age = age;
// Return this for chaining
return this;
}
save() {
console.log(this.name, this.surname, this.age);
// Return this for chaining
return this;
}
}
const person = new Person("John")
.setSurname("Doe")
.setAge(29)
.save();
6、常规的避免
一般来说,尽量不要重复自己,就是说不应该编写重复的代码,也不要在你身后留下尾巴,比如未使用的函数和死代码。
由于各种原因,你最终可能会有重复的代码。比如,你可能有两个略有不同的东西,它们有许多共同之处,有时期限不足的迫使你创建包含几乎同样代码的单独函数。
关于死代码,这正如它的名字。它没有做任何事情,因为在某些开发阶段,你已经决定不再用它,您应该在代码库中搜索这些不需要的函数和代码块并删除。 我建议你,一旦你决定不再需要它,就马上删除它, 以防你以后可能会忘记它的用途。
这只是改进代码所能做的小部分。 在我看来,这里所说的原则是人们经常不遵循的原则。 他们尝试但总是因各种原因而没成功。 也许项目一开始时,代码是整洁干净,但在最后期限时,原则经常被忽略并转移到“TODO”或“REFACTOR”部分。 到那时,您的客户宁愿让您满足截止日期,而不是规范的代码。
到此为止!
感谢你阅读并且希望在下一篇文章看到你。