JavaScriptのコードをCleanに書く7つのTips


1.破壊的メソッドをなるべく使わない

破壊的メソッドとは、元の配列の要素を変えるメソッドです。以下の例ではconstで宣言した変数numbersが、pushメソッドにより更新されています。

bad

const numbers = [1, 2, 3]
numbers.push(4)
console.log(numbers) // [1, 2, 3, 4]

この場合、元の配列の要素を更新するのではなく、スプレッド構文を使って新しい変数に代入します。変数はなるべくイミュータブルにしておくと、意図しない不具合やプログラムの可読性や保守性が向上します。pushメソッド以外にも、sortやfillなど他の破壊的メソッドでも使えます。

good

const numbers = [1, 2, 3]
const newNumbers = [...numbers, 4]
console.log(newNumbers) // [1, 2, 3, 4]

2.配列の要素の存在チェックに比較演算子を使わない

配列の要素の存在チェックに、比較演算子を使うコードをよく見かけます。

bad

const numbers1 = [1, 2, 3]
if (numbers1.length >= 1) {
    console.log('OK1') // 実行される
}

const numbers2 = []
if (numbers2.length == 0) {
    console.log('OK2') // 実行される
}

これらは以下のように書き換えることが出来ます。要素がないことを表す場合は、論理否定(!)を使うだけです。このように書くだけで条件分岐がシンプルになるため、可読性が高まります。

good

const numbers1 = [1, 2, 3]
if (numbers1.length) {
    console.log('OK1') // 実行される
}

const numbers2 = []
if (!numbers2.length) {
    console.log('OK2') // 実行される
}

3.オブジェクトの特定のプロパティを代入するときは分割代入を使う

オブジェクトのプロパティを取得する場合、2行目のように書いているケースをよく見ます。

bad

const fruit = { id: 1, type: 'apple', price: 300 }
const type = fruit.type
const price = fruit.price
console.log(type, price) // apple 300

取得したいオブジェクトのプロパティ名を{}の中に指定することで、同じことができます。

good

const fruit = { id: 1, type: 'apple', price: 300 }
const { type, price } = fruit
console.log(type, price) // apple 300

4.同じ処理はコールバック関数を利用して一つにまとめる

以下の処理は、membersとfruitsの配列でidの存在チェックを行い、id一覧の配列を生成します。

bad

const members = [{id: 11, name: 'yamada'}, {id: 12, name: 'tanaka'}, {name: 'sasaki'}]
const fruits = [{name: 'orange'}, {id: 33, name: 'apple'}, {id: 34, name: 'banana'}]

const memberIds = members.filter(member => member.id).map(member => member.id)
const fruitIds = fruits.filter(fruit => fruit.id).map(fruit => fruit.id)

console.log(memberIds) // [ 11, 12 ]
console.log(fruitIds) // [ 33, 34 ]

よく見るとmembersとfruitsのidの取得は、メソッドチェインでfilterしてmapで要素を返しているだけです。同じような処理は、ひとつにまとめられます。

good

const members = [{id: 11, name: 'yamada'}, {id: 12, name: 'tanaka'}, {name: 'sasaki'}]
const groups = [{name: 'orange'}, {id: 33, name: 'apple'}, {id: 34, name: 'banana'}]

const getIds = (args) => args.filter(arg => arg.id).map(arg => arg.id)

const memberIds = getIds(members)
const fruitIds = getIds(groups)

console.log(memberIds) // [ 11, 12 ]
console.log(fruitIds) // [ 33, 34 ]

5.数値が多いときは短く書く方法を検討する

bad

const num = 10000
console.log(num) // 10000

JavaScriptでは、数値の間に文字列「e」を追加し、ゼロの数を指定することで数値を短くできます。

const num = 1e4
console.log(num) // 10000

6.ifやswitchなどの条件分岐をなるべく使わない

bad

const toColorCode = (value) => {
    let code = ''
    switch (value) {
        case 'blue':
            code = '#0000ff'
            break;
        case 'yellow':
            code = '#ffff00'
            break;
        case 'red':
            code = '#ff0000'
            break;
    }
    return code
}

console.log(toColorCode('yellow')) // #ffff00

このように書くと、コード量が減って読みやすくなります。

good

const toColorCode = (value) => {
    const color = {
        blue: '#0000ff',
        yellow: '#ffff00',
        red: '#ff0000'
    }
    return color[value]
}

console.log(toColorCode('yellow')) // #ffff00

7.関数の引数が多い場合、Patterns Objectを利用する

createTask関数の引数が4つあります。関数の呼び出し元だけを見ると「特になし」や「false」などが何を表すのか、関数の定義を見にいかないと分からないです。(初期化処理の例が悪いですが、サンプルなので大目に見てください)

const createTask = (title, description, dueDate, isDone) => {
  // 何かしらの処理
}

createTask('勉強する', '特になし',  '2022-08-01', false)

関数の引数をオブジェクトで定義することにより、呼び出し元にプロパティ名をつけられます。このようにすると、呼び出し元を見たときに引数に必要な値が分かりやすいですし、プロパティの順番を間違えずに済みます。

const createTask = ({title, description, dueDate, isDone}) => {
  // 何かしらの処理
}

createTask({
    title: '勉強する',
    description: '特になし',
    dueDate: '2022-08-01',
    isDone: false
})