JavaScript 代碼簡潔之道

想必你們都有接手過老舊項目,它的代碼風格怎麼樣?可能大部分同窗都會無力吐槽吧。筆者認爲一份代碼的好壞,先無論其實現如何,至少其 可讀性 得保證,才能算得上好代碼。有些尚未意識到 可讀性代碼 所帶來好處的同窗,可能常常會遇到上週才寫的代碼,今天打開項目一看,臥槽,這個 💩 同樣的代碼是我寫的嗎?css

筆者的 代碼潔癖 有點嚴重,因此平時對本身寫的代碼也有比較高的要求,至少在 可讀性 上算是比較能保證。下面將分享一些代碼 可讀性簡潔性 方面的一些技巧。前端

命名

相信你們比較多的吐槽就是變量的命名,以致於出現相似 CodeIf 這種輔助聲明變量名的神器,因此平時在項目中如何作到好的命名呢。git

其實大多數的變量賦值後都不會再改變(從新賦值),因此筆者會優先使用 const 而不是 let,確實沒法避免須要賦值屢次才用 letgithub

常量都應該命名

// Bad
static({
    maxAge: 28800000, // what ???
});

// Good
const EIGHT_HOURS_IN_MILLISECOND = 28800000;
// or
const EIGHT_HOURS_IN_MILLISECOND = 8 * 60 * 60 * 1000;
static({
    maxAge: EIGHT_HOURS_IN_MILLISECOND,
});

// Bad
$(document).on('keydown', event => {
    // 27 是什麼鬼???
    if(event.keyCode === 27) {
        // do something ...
    }
});

// Good
$(document).on('keydown', event => {
    const KEY_ESC = 27;
    if(event.keyCode === KEY_ESC) {
        // do something ...
    }
});
複製代碼

有意義的命名

// Bad
const n = '大板慄';
const a = 18;

// Good
const name = '大板慄';
const age = 18;
複製代碼

減小命名冗餘

// Bad
const favoriteList = [];
const getCurrentUserData = () => {};

// Good
const favorites = [];
const getCurrentUser = () => {};
複製代碼

顧名思義

單看函數名就應該知道功能是什麼。編程

// Bad
function fetchAtls() {}

// Good
function fetchArticles() {}
// or
function getArticles() {}
複製代碼

函數

簡化代碼

利用三目運算符和 ES6+ 語法簡化代碼app

// Bad
$('.js-button').on('click', function() {
    var name = '開始';
    if ($(this).hasClass('again')) {
        name = '再玩一次';
    }
    app.track('按鈕', name);
});

// Good
$('.js-button').on('click', function() {
    const again = $(this).hasClass('again');
    app.track(again ? '再玩一次' : '開始');
});

// Bad
const celebrationDayChange = isSameDay => {
    if (isSameDay) {
        $dateSelect.addClass('hide');
    } else {
        $dateSelect.removeClass('hide');
    }
};

// Good
const celebrationDayChange = isSameDay => {
    const action = isSameDay ? 'addClass' : 'removeClass';
    $dateSelect[action]('hide');
};
複製代碼

函數參數

函數的參數儘可能簡短,若有 3 個以上建議直接換成對象的方式:ide

// Bad
function publish(title, author, except, content) {}
// 必須知道參數位置信息
publish('title', '大板慄', 'an excellent article', 'This is content.');

// Good
function publish({title, author, except, content}) {}
publish({
    title: 'title',
    author: '大板慄',
    except: 'an excellent article',
    content: 'This is content.',
});
複製代碼

設置對象默認屬性

// Bad
const options = {
    name: '大板慄',
    logo: 'Logo.png',
    theme: 'dark',
};
function createApp(options = {}) {
    options.name = options.name || 'App';
    options.logo = options.logo || 'Logo.png';
    options.theme = options.theme || 'light';
}

// Good
function createApp(options = {}) {
    options = Object.assign({
        name: 'App',
        logo: 'Logo.png',
        theme: 'light',
    }, options);
}
複製代碼

參數默認值

// Bad
function App(name) {
    name = name || 'App';
    // ...
}

// Good
function App(name = 'App') {
    // ...
}
複製代碼

函數式風格

// Bad
function querify(object = {}) {
    const keys = Object.keys(object);
    let result = '';
    for(let i = 0; i < keys.length; i++) {
        result += `&${keys[i]}=${object[keys[i]]}`;
    }
    result = result.slice(1);
    return result;
}

// Good
function querify(object = {}) {
    const keys = Object.keys(object);
    const result = keys.reduce((prev, current) => {
        prev += `&${current}=${object[current]}`;
        return prev;
    }, '').slice(1);
    return result;
}
複製代碼

避免使用 switch

// Bad
function reducer(state = 0, {type}) {
    switch (type) {
        case 'INCREASE':
            return state + 1;
        case 'DECREASE':
            return state - 1;
        default:
            return state;
    }
}

// Good
function reducer(state = 0, {type}) {
    const mapping = {
        'INCREASE': state + 1,
        'DECREASE': state - 1,
    };
    const effective = Object.keys(mapping).includes(type);
    return effective ? mapping[type] : state;
}
複製代碼

封裝 if 判斷條件

下面是筆者寫的 自動爲背景圖片添加寬高 的 PostCSS 插件中的 代碼片斷函數

// Bad
if (/background[^:]*.*url[^;]+/gi.test(ruleString)) {
    const [originURL, URL] = getImageURL(ruleString);
    // ...
}

// Good
const hasBackground = rule => /background[^:]*.*url[^;]+/gi.test(rule);
if (hasBackground(ruleString)) {
    const [originURL, URL] = getImageURL(ruleString);
    // ...
}
複製代碼

只作一件事

函數是最小可複用單元,因此若是一個函數作了多個事情的話,表明這個函數難以被複用,So, just do one thing.工具

// Bad
function notify(users) {
    users.map(user => {
        const record = DB.find(user);
        if (record.isActive()) {
            sendMessage(user);
        }
    });
}

// Good
// judge activation only
function isActive(user) {
    const record = DB.find(user);
    return record.isActive();
}
function notify(users) {
    users.filter(isActive).forEach(sendMessage);
}
複製代碼

不用「否認」語法命名函數

// Bad
const isNotSupport = () => {};
const canNotUpdate = () => {};

// Good
const isSupport = () => {};
const canUpdate = () => {};
複製代碼

使用 ES6+ 語法

解構

// Bad
const first = items[0];
const second = items[1];
const name = me.name;
const age = me.age;

// Good
const [first, second] = items;
const { name, age } = me;

// Bad
function App(options) {
    track({
        name: options.name,
        version: options.version,
        env: options.env,
    });
}

// Good
// 解構函數參數
function App({name, version, env}) {
    track({
        name: name,
        version: version,
        env: env,
    });
}

// Best
function App({name, version, env}) {
    // 使用對象簡寫
    track({ name, version, env });
}
複製代碼

模板字符串

// Bad
const greeting = name => 'Hello ' + name + '!';

// Good
const greeting = name => `Hello ${name}!`;
複製代碼

箭頭函數

// Bad
function greeting(name) {
    return `Hello ${name}!`;
}

// Good
const greeting = name => `Hello ${name}!`;
複製代碼

棄用回調函數

// Bad
fetchCurrentUser((error, currentUser) => {
    if(error) throw Error;
    fetchArticles(currentUser.id, (error, articles) => {
        if(error) throw Error;
        // articles here...
    });
});

// Good
fetchCurrentUser
.then(currentUser => currentUser.id)
.then(fetchArticles)
.then(articles => {
    // articles here...
})
.catch(() => {
    throw Error;
});

// Best
try {
    const currentUser = await fetchCurrentUser();
    const articles = await fetchArticles(currentUser.id);
    // articles here...
} catch() {
    throw Error;
}
複製代碼

總結

長而具備描述性的名稱,要比短而使人費解的名稱好,也比描述性的長註釋更好,好的代碼是 自注釋 的,不要擔憂變量名太長(打包工具壓縮後都同樣)。post

代碼不僅是寫給機器看的,也是寫給人看的,因此從如今開始編寫 可讀性強易維護 的代碼吧。

最後友情提醒你們 代碼千萬行,註釋第一行。編程不規範,同事兩行淚。

follow

更多幹貨請關注公衆號「前端小專欄:QianDuanXiaoZhuanLan」
相關文章
相關標籤/搜索