「译」JavaScript 整洁代码 - 最佳实践
介绍
如果您关心代码本身以及代码的编写方式,而不必担心它是否起作用,那么您可以说您正在练习并关心干净的代码。专业的开发人员将为将来的自己和“其他人”编写代码,而不仅仅是为机器编写代码。您编写的任何代码都永远不会被编写一次,而是会坐下来等待未来的家伙,使他痛苦不堪。希望那个未来的家伙不会是你。
基于此,干净的代码可以定义为以不言自明,易于人类理解,易于更改或扩展的方式编写的代码。
问自己,当第一印象是“ WTF”问题之一时,您继续进行了几次别人的工作?
“这是什么鬼?”
“你在这里做了什么?”
“这是你的目的吗?”
这是表达上述观点的热门图片。
罗伯特·C·马丁(鲍勃叔叔)的一句话,应该使您思考自己的做法。
即使是糟糕的代码也能发挥作用。但是如果代码不干净,就会使开发者陷入困惑。
在本文中,重点将放在JavaScript上,但是这些原理可以应用于其他编程语言。
现在开始阅读整洁代码的最佳实践
1. 强类型检查
使用 === 代替 ==
// If not handled properly, it can dramatically affect the program logic. It's like, you expect to go left, but for some reason, you go right.
0 == false // true
0 === false // false
2 == "2" // true
2 === "2" // false
// example
const value = "500";
if (value === 500) {
console.log(value);
// it will not be reached
}
if (value === "500") {
console.log(value);
// it will be reached
}
2. 变量
命名您的变量,使其能够揭示其意图。这样,当人们看到它们后,它们就变得可搜索并且更易于理解。
Bad:
let daysSLV = 10;
let y = new Date().getFullYear();
let ok;
if (user.age > 30) {
ok = true;
}
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();
// ...
// ...
// ...
// ...
// Here we have the WTF situation: WTF is `u` for?
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") {
// ...
}
一个功能应该做一件事。避免在一个函数中执行多个动作。
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.height || 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}`);
}
不要污染全局变量。如果需要扩展现有对象,请使用ES类和继承,而不要在本机对象的原型链上创建函数。
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
}
使用条件速记。这可能是微不足道的,但值得一提。如果您确定该值不是 undefined 或 null,则仅对布尔值使用此方法。
Bad:
if (isValid === true) {
// do something...
}
if (isValid === false) {
// do something...
}
Good:
if (isValid) {
// do something...
}
if (!isValid) {
// do something...
}
尽可能避免附加条件。请改用多态和继承。
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. ES Classes
类是 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 等许多库都使用此模式。结果,您的代码将不再那么冗长。在您的类中,只需 this 在每个函数的结尾处返回即可,您可以将更多的类方法链接到该函数上。
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” 部分。在那时,您的客户希望您在截止日期之前完成,而不是编写干净的代码。