代码整洁之道使你的代码更易被人理解
简介
如果你不仅在乎代码是否正常运行,还关心代码本身的编写质量,那么可以说你在追求代码的整洁之道。
基于上述讨论,代码的整洁之道被定义为以不言自明,通俗易懂,且易于修改和拓展的方式进行代码编写。
甚至,错误的代码都能运行,但代码不整洁真的会让开发团队为之头疼。
本文以Javascript语言出发介绍代码整洁之道,但其中理论同样适用于其他编程语言。
1. 强类型检查
使用===
而不是==
0 == false // true
0 === false // false
2 == "2" // true
2 === "2" // false
// example
const val = "123";
if (val === 123) {
console.log(val);
// it cannot not be reached
}
if (val === "123") {
console.log(val);
// it can be reached
}
2. 变量命名
将你的变量以能够表述其使用意图的方式进行命名
按照这种方式,这些变量就能够变得可搜索并且使其具有将强的可读性。
不推荐👎
let daysSLV = 10;
let y = new Date().getFullYear();
let ok;
if (user.age > 30) {
ok = true;
}
推荐👍
const MAX_AGE = 30;
let daysSinceLastVisit = 10;
let currentYear = new Date().getFullYear();
...
const isUserOlderThanAllowed = user.age > MAX_AGE;
不要添加一些额外且不必要的词汇到你的变量名称中。
不推荐👎
let nameValue;
let theProduct;
推荐👍
let name;
let product;
不要忽略保存变量的上下文信息的需求
不推荐👎
const products = ["T-Shirt", "Shoes", "Watches", "Bags"];
products.forEach(p => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
// `p` 表示什么?
register(p);
});
推荐👍
const products = ["T-Shirt", "Shoes", "Watches", "Bags"];
products.forEach(product => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
register(product);
});
不要在变量中增加不必要的信息
不推荐👎
const product = {
productId: 1,
productName: "T-Shirt",
productPrice: 8.99,
productUnits: 12
};
...
product.productName;
推荐👍
const product = {
id: 1,
name: "T-Shirt",
price: 8.99,
units: 12
};
...
product.name;
对相同类型的变量使用同样的词汇
不推荐👎
getUserInfo();
getClientData();
getCustomerRecord();
推荐👍
getProduct();
3. 函数
使用具有描述性的函数名称。
鉴于函数通常代表某种特定的行为,因此函数名应采用一个动词或者短语,其能够充分表达函数背后的意图和参数的含义。
函数名即函数的作用
不推荐👎
function email(user) {
// implementation
}
推荐👍
function sendEmailUser(emailAddress) {
// implementation
}
避免参数数量过多
理想情况下,函数应该包含两个或者更少的参数。参数的数量越少,该函数越容易被测试
不推荐👎
function getProducts(fields, fromDate, toDate) {
// implementation
}
推荐👍
function getProducts({ fields, fromDate, toDate }) {
// implementation
}
getProducts({
fields: ['id', 'name', 'price', 'units],
fromDate: '2020-07-01',
toDate: '2020-07-22'
});
使用默认参数,而不是条件判断
不推荐👎
function createShape(type) {
const shapeType = type || "circle";
// ...
}
推荐👍
function createShape(type = "circle") {
// ...
}
避免在一个函数体中执行多个操作
不推荐👎
function notifyUsers(users) {
users.forEach(user => {
const userRecord = database.lookup(user);
if (userRecord.isVerified()) {
notify(user);
}
});
}
推荐👍
function notifyVerifiedUsers(users) {
users.filter(isUserVerified).forEach(notify);
}
function isUserVerified(user) {
const userRecord = database.lookup(user);
return userRecord.isVerified();
}
使用Object.assign来设置默认对象
不推荐👎
const shapeConfig = {
type: "circle",
width: 150,
height: null
};
function createShape(config) {
config.type = config.type || "circle";
config.width = config.width || 300;
config.height = config.height || 300;
}
createShape(shapeConfig);
推荐👍
const shapeConfig = {
type: "circle",
width: 150
// Exclude the 'height' key
};
function createShape(config) {
config = Object.assign(
{
type: "circle",
width: 300,
height: 300
},
config
);
...
}
createShape(shapeConfig);
不要将标志位作为参数,因为其使得函数的作用超出其本身预设的范围
不推荐👎
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}`);
}
切忌污染全局变量
如果你想要对已有对象进行拓展,请使用ES6 的Class类和继承语法,而不是在原生对象的原型链上创建拓展函数。
不推荐👎
Array.prototype.myFunction = function myFunction() {
// implementation
};
推荐👍
class SuperArray extends Array {
myFunc() {
// implementation
}
}
4. 条件判断
避免使用负条件(会使得逻辑复杂化)
不推荐👎
function isPostNotPublished(post) {
// implementation
}
if (!isPostNotPublished(post)) {
// implementation
}
推荐👍
function isPostPublished(user) {
// implementation
}
if (isPostPublished(user)) {
// implementation
}
使用简化的条件判断
这条规则似乎看起来微不足道,但却值得一提。在明确某个值不是undefined
或null
,以及在值为boolean
类型时,可使用该规则使代码更简洁。
不推荐👎
if (isValid === true) {
// do something...
}
if (isValid === false) {
// do something...
}
推荐👍
if (isValid) {
// do something...
}
if (!isValid) {
// do something...
}
在任何情况下避免使用条件判断
使用多态(polymorphism)和继承(inheritance)
不推荐👎
class Dog {
// ...
getBreed() {
switch (this.type) {
case "GermanShepherd":
return this.getStandardSize("GermanShepherd");
case "JackRussellTerrier":
return this.getStandardSize("JackRussellTerrier");
case "ShibaInu":
return this.getStandardSize("ShibaInu");
}
}
}
推荐👍
class Dog {
// ...
}
class GermanShepherd extends Dog {
// ...
getStandardSize() {
return this.standardSize;
}
}
class JackRussellTerrier extends Dog {
// ...
getSize() {
return this.standardSize;
}
}
class ShibaInu extends Dog {
// ...
getSize() {
return this.standardSize;
}
}
5. ES Classes
class类语法是Javascript的新语法糖。除了写法不一样,Class语法能够实现和之前使用原型(prototype)一样的效果,且使得代码更加简明。
不推荐👎
const Product = function(name) {
if (!(this instanceof Product)) {
throw new Error("Instantiate Product with `new` keyword");
}
this.name = name;
};
Product.prototype.getSize = function getSize() { /**/ };
const Tshirt = function(name, color) {
if (!(this instanceof Tshirt)) {
throw new Error("Instantiate Tshirt with `new` keyword");
}
Product.call(this, name);
this.color = color;
};
Tshirt.prototype = Object.create(Product.prototype);
Tshirt.prototype.constructor = Tshirt;
Tshirt.prototype.printColor = function printColor() { /**/ };
推荐👍
class Product {
constructor(name) {
this.name = name;
}
getDiscount() {
/* ... */
}
}
class Tshirt extends Product {
constructor(name, color) {
super(name);
this.color = color;
}
getSize() {
/* ... */
}
}
使用链式方法
许多受欢迎的库,像jQuery和Lodash等采用这种模式,其能够使得代码变得不那么冗长。
在你所定义的类中,通过在每个函数结尾返回this
,你就可以在之后链入更多类函数。
不推荐👎
class Product {
constructor(name) {
this.name = name;
}
setPrice(price) {
this.price = price;
}
setUnits(units) {
this.units = units;
}
save() {
console.log(this.name, this.price, this.units);
}
}
const product = new Product("Bag");
person.setPrice(23.99);
person.setUnits(12);
person.save();
推荐👍
class Product {
constructor(name) {
this.name = name;
}
setName(name) {
this.name = name;
// Return this for chaining
return this;
}
setPrice(price) {
this.price = price;
// Return this for chaining
return this;
}
save() {
console.log(this.name, this.price, this.units);
// Return this for chaining
return this;
}
}
const product = new Product("T-Shirt")
.setName("Jeans")
.setAge(31.99)
.save();
6. 避免使用eval
eval
函数能够将字符串传递给Javascript编译器并将其按Javascript语句进行执行
简而言之,你在运行时传递的任何内容都会像在设计时添加的那样执行。
eval("alert('Hi');");
上述代码在执行时将弹出一个信息框,带有“Hi”
应该尽量避免使用Eval函数,因为它并不安全,其为恶意程序员提供了具有威胁性质的潜在载体。
7. 使用JSLint
JSLint等代码格式校验工具是编写规范代码的好帮手。关于这类工具的使用后续将展开讨论】
8. 整体上避坑
总结
翻译原文来自:medium.com/javascript-…