- 原文地址:JavaScript Clean Code - Best Practices
- 原文做者:Milos Protic
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:xilihuasi
- 校對者:smilemuffie、Xuyuey
若是你不僅是擔憂你的代碼是否能生效,還會關注代碼自己及其如何編寫,那你能夠說你有在關注簡明代碼並在努力實踐。專業的開發者會面向其將來和其餘人而不只是爲了機器編寫代碼。你寫的任何代碼都不會只寫一次,而是會待在那等待將來維護代碼的人,讓他痛苦不堪。但願那個將來的傢伙不會是你。javascript
基於上述狀況,簡明代碼能夠被定義爲代碼以不言自明,易於理解且易於更改或擴展的方式編寫。前端
回想一下有多少次你接手別人工做時的第一印象是下面幾個 WTF 問題之一?java
「這 TM 是啥?」android
「你 TM 在這幹了啥」ios
「這 TM 是幹啥的?」git
有一個很火的圖片描繪了上述場景。github
Robert C. Martin (Bob 叔叔) 的一句名言應該會啓發你思考你的方式。編程
即便是糟糕的代碼也能運行。可是若是代碼不夠簡明,它會讓開發組織陷入困境。後端
在本文中,重點將放在 JavaScript 上,可是原則能夠應用於其餘編程語言。編程語言
使用 ===
而不是 ==
// 若是處理不當,它會在很大程度上影響程序邏輯。就像,你期待向左走,但因爲某些緣由,你向右走了。
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);
// 會執行
}
複製代碼
變量命名要直接代表其背後的意圖。這種方式方便代碼搜索而且易於他人理解。
糟糕示例:
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 users = ["John", "Marco", "Peter"];
users.forEach(u => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
// 這裏有 WTF 場景:`u` TM 是啥?
register(u);
});
複製代碼
良好示例:
const users = ["John", "Marco", "Peter"];
users.forEach(user => {
doSomething();
doSomethingElse();
// ...
// ...
// ...
// ...
register(user);
});
複製代碼
不要添加沒必要要的上下文。
糟糕示例:
const user = {
userName: "John",
userSurname: "Doe",
userAge: "28"
};
...
user.userName;
複製代碼
良好示例:
const user = {
name: "John",
surname: "Doe",
age: "28"
};
...
user.name;
複製代碼
使用長而具備描述性的名稱。考慮到它表明某種行爲,函數名稱應該是暴露其背後意圖的動詞或者短語,參數也是如此。它們的名稱應該代表它們要作什麼。
糟糕示例:
function notif(user) {
// implementation
}
複製代碼
良好示例:
function notifyUser(emailAddress) {
// implementation
}
複製代碼
避免使用大量參數。理想狀況下,函數參數不該該超過兩個。參數越少,函數越易於測試。
糟糕示例:
function getUsers(fields, fromDate, toDate) {
// implementation
}
複製代碼
良好示例:
function getUsers({ fields, fromDate, toDate }) {
// implementation
}
getUsers({
fields: ['name', 'surname', 'email'],
fromDate: '2019-01-01',
toDate: '2019-01-18'
});
複製代碼
使用默認參數代替條件語句。
糟糕示例:
function createShape(type) {
const shapeType = type || "cube";
// ...
}
複製代碼
良好示例:
function createShape(type = "cube") {
// ...
}
複製代碼
一個函數應該只作一件事。禁止在單個函數中執行多個操做。
糟糕示例:
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: "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);
複製代碼
良好示例:
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);
複製代碼
不要使用標誌變量做爲參數,由於這代表函數作了它不該該作的事。
糟糕示例:
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}`);
}
複製代碼
不要污染全局變量。若是你要擴展一個已存在的對象,使用 ES 類繼承而不是在原生對象的原型鏈上建立函數。
糟糕示例:
Array.prototype.myFunc = function myFunc() {
// implementation
};
複製代碼
良好示例:
class SuperArray extends Array {
myFunc() {
// implementation
}
}
複製代碼
避免使用否認條件。
糟糕示例:
function isUserNotBlocked(user) {
// implementation
}
if (!isUserNotBlocked(user)) {
// implementation
}
複製代碼
良好示例:
function isUserBlocked(user) {
// implementation
}
if (isUserBlocked(user)) {
// implementation
}
複製代碼
使用條件語句簡寫。這可能不那麼重要,可是值得一提。僅將此方法用於布爾值,而且肯定該值不是 undefined
和 null
。
糟糕示例:
if (isValid === true) {
// do something...
}
if (isValid === false) {
// do something...
}
複製代碼
良好示例:
if (isValid) {
// do something...
}
if (!isValid) {
// do something...
}
複製代碼
儘量避免條件語句,使用多態和繼承。
糟糕示例:
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();
}
}
}
複製代碼
良好示例:
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();
}
}
複製代碼
類是 JavaScript 中的新語法糖。一切都像以前使用原型同樣如今只不過看起來不一樣,而且你應該喜歡它們賽過 ES5 普通函數。
糟糕示例:
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() { /**/ };
複製代碼
良好示例:
class Person {
constructor(name) {
this.name = name;
}
sayHello() {
/* ... */
}
}
class Student extends Person {
constructor(name, school) {
super(name);
this.school = school;
}
printSchoolName() {
/* ... */
}
}
複製代碼
使用方法鏈。諸如 jQuery 和 Lodash 之類的不少庫都使用這個模式。這樣的話,你的代碼就會減小冗餘。在你的類中,只用在每一個函數末尾返回 this
,而後你就能夠在它上面鏈式調用更多的類方法了。
糟糕示例:
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();
複製代碼
良好示例:
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();
複製代碼
通常來講,你應該盡力不要重複本身的工做,意思是你不該該寫重複代碼,而且不要在你身後留下尾巴好比未使用的函數和死代碼。
出於各類緣由,你最終可能會遇到重複的代碼。例如,你有兩個大體相同只有些許不一樣的東西,它們不一樣的特性或者時間緊迫使你單首創建了兩個包含幾乎相同代碼的函數。在這種狀況下刪除重複代碼意味着抽象化差別並在該層級上處理它們。
關於死代碼,碼如其名。它是在咱們代碼庫中不作任何事情的代碼,在開發的某個階段,你決定它再也不有用了。你應該在代碼庫中搜索這些部分而後刪除全部不須要的函數和代碼塊。我能夠給你的建議是一旦你決定再也不須要它,刪除它。否則你就會忘了它的用途。
這有一張圖代表你當時可能會有的感覺。
這只是改進代碼所能作的一小部分。在我看來,這裏所說的原則是人們常常不遵循的原則。他們有過嘗試,但因爲各類緣由並不老是奏效。可能項目剛開始代碼仍是整潔的,但當截止日期快到了,這些原則一般會被忽略,被移入「待辦」或者「重構」部分。在那時候,客戶寧願讓你遇上截止日期而不是寫簡明的代碼。
就這樣!
感謝閱讀,下篇文章見。
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。