Clean Code之JavaScript代碼示例

譯者按: 簡潔的代碼能夠避免寫出過多的BUG。javascript

本文采用意譯,版權歸原做者全部java

引文

做爲一個開發者,若是你關心代碼質量,除了須要認真測試代碼可否正確執行之外,還要注重代碼的整潔(clean code)。一個專業的開發者會從未來本身或則他人方便維護的角度考慮如何寫代碼,而不只僅是機器可以讀懂。你寫的任何代碼有很大概率會被再次重構,但願將來重構代碼的那我的不會以爲這是一場災難。小程序

代碼的簡潔之道能夠被理解爲:代碼自我解釋(且註釋少),開發者友好(易於理解,修改和擴展)。微信小程序

想一想以前閱讀別人的代碼的時候,說過多少次下面的話?微信

"WTF is that?"

"WTF did you do here?"函數

"WTF is this for?"post

下面這張圖很形象地描述了這個狀況:測試

img

《Clean Code》的做者Robert C. Martin (Uncle Bob) 說過這樣的話.this

雖然爛代碼能夠運行,可是它不夠整潔,它會把整個開發團隊給整跪了

本文主要講 JavaScript 代碼的整潔之道。spa

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. 變量命名

變量命名儘可能直觀易懂,方便查找;並且其餘開發者也容易理解。

很差的命名方式:

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();
    // ...
    // ...
    // ...
    // ...
    // 這裏u到底指代什麼?
    register(u);
});

好的命名方式:

const users = ["John", "Marco", "Peter"];
users.forEach(user => {
    doSomething();
    doSomethingElse();
    // ...
    // ...
    // ...
    // ...
    register(user);
});

在某些環境下,不用添加冗餘的單詞來組合命名。好比一個對象叫user,那麼其中的一個名字的屬性直接用name,不須要再使用username了。

壞的命名方式:

const user = {
  userName: "John",
  userSurname: "Doe",
  userAge: "28"
};

...

user.userName;

好的命名方式:

const user = {
  name: "John",
  surname: "Doe",
  age: "28"
};

...

user.name;

3. 函數

請使用完整的聲明式的名字來給函數命名。好比一個函數實現了某個行爲,那麼函數名能夠是一個動詞或則一個動詞加上其行爲的被做用者。名字就應該表達出函數要表達的行爲。

壞的命名方式:

function notif(user) {
    // implementation
}

好的命名方式:

function notifyUser(emailAddress) {
    // implementation
}

避免使用過多參數。最好一個函數只有 2 個甚至更少的參數。參數越少,越容易作測試。

壞的使用方式:

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();
}

使用Objecg.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);

不要使用 true/false 的標籤(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}`);
}

不要污染全局。若是你須要對現有的對象進行擴展,不要在對象的原型鏈上定義函數。請使用 ES 的類和繼承。

壞的寫法:

Array.prototype.myFunc = function myFunc() {
    // implementation
};

好的寫法:

class SuperArray extends Array {
    myFunc() {
        // implementation
    }
}

好的代碼風格能夠避免不當心寫出有BUG的代碼,以防萬一,推薦使用Fundebug作線上實時BUG監控!

4. 判斷條件

避免使用否認的條件。

壞的寫法:

function isUserNotBlocked(user) {
    // implementation
}

if (!isUserNotBlocked(user)) {
    // implementation
}

好的寫法:

function isUserBlocked(user) {
    // implementation
}

if (isUserBlocked(user)) {
    // implementation
}

使用簡短的條件。這個要求看上去簡單,可是值得提醒。

壞的寫法:

if (isValid === true) {
    // do something...
}

if (isValid === false) {
    // do something...
}

好的寫法:

if (isValid) {
    // do something...
}

if (!isValid) {
    // do something...
}

若是你很肯定它的值不是undefined或則null,我建議你這麼作。

儘可能避免使用判斷條件,推薦說那個多態(polymorphism)或則繼承。

壞的寫法:

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();
    }
}

5. ES 類

類是 JavaScript 新推出的語法糖。建議使用類而不是用老式的直接定義函數的寫法。

壞的寫法:

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();

6. 其它

總的來講,你不能寫重複代碼,不能留下一堆再也不使用的函數,永遠不會執行的代碼(死代碼)。

在不少狀況下,你可能搞出重複代碼。好比你要實現兩個略微不一樣的功能,他們有不少相通的地方,可是因爲項目截止時間快到了,你不得不快速複製黏貼再稍微修改修改來實現。

對於死代碼,最好的作法就是你決定再也不使用它的那一刻就把它刪掉。時間過去過久,你甚至會忘記本身當初爲何定義它。下面這幅圖很形象地描述了這個狀況:

關於Fundebug

Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等衆多品牌企業。歡迎你們免費試用

版權聲明

轉載時請註明做者Fundebug以及本文地址:
https://blog.fundebug.com/2019/06/11/javascript-clean-code/

相關文章
相關標籤/搜索