クリーンコードの原則
- 他人が読みやすい
- コードの意味がわかりやすく、文章のように意味が伝わる。
- 読んでいて疲れない
- 変数、関数、クラス名が適切な名前
- 無駄な部分まで読む必要がない
- バグが混入しにくい
- 改修箇所が限定される
- 改修による既存コードへの影響が限定される
- 新規機能を追加しやすい
- 冗長な記述がない
- テストがしやすい
- コードの流れに一連の文脈が存在する
命名規則
# bad
const a = 300 / 60;
# good
const mintutes = 300;
const hours = minutes /60;
# bad
function calc(number) {
return number / 60;
}
# good
function convertMinutesToHours(minutes) {
return minutes / 60;
}
良い変数名
- 変数が保持するものを正確に説明
- 曖昧でなく明確に説明
- 短すぎず、長すぎないように
- 多くに人に意味がわかる
- 発音可能
識別子とケース
識別子:変数名、関数名、クラス名などの識別するためのもの
- ケバブケース
kebabu-case - スネークケース
snake_case - アッパースネークケース(コンスタントケース)
UPPER_SNAKE_CASE - パスカルケース
PascalCase - キャメルケース
camelCase
審議値の変数名
- is〜
isEmpty, isSet - 過去分詞系
done, finished, sent, put
変数名の接頭語
- get〜:値を返す、取得する
getInteger
- set〜:値を設定する
setName
- is〜:真偽値を返す
isEmpty
- has〜:持っているか
hasPosts
- needs〜:必要か
- can:〜できるか
canStartTime
- should:〜すべきか
** isExistsなど動詞が連続するような命名は避けて、Existsなど三人称をつける対応。
https://qiita.com/yskszk/items/5a7f99c974773f03a82a
変数名、プロパティ:(形容詞+)名詞
- themeColor
- customerType
- serviceName
関数、メソッド:動詞(+形容詞+名詞)
- getNumbers
- getUserName
- addActiveButton
クラス:(形容詞+)名詞
- Animals
- ScoreCounter
- PurchaseHistory
配列の末尾には複数名のsをつける
# bad
user.forEach
#good
users.forEach
# bad
animal.map
animals.map
ウソの変数名をつけない
# bad
# 配列ではなく、オブジェクトであり、リストでもないので誤った印象を与える。Listは配列の印象を与える。
const averageList = {
japanese: 30,
math: 10,
English: 30
}
# good
const AVERAGE_SCORE = {
japanese: 30,
math: 10,
English: 30
}
AVERAGE_SCORE.japanese
のように参照するので、その辺りも意識すると良い。値の保持ならキャメルケースでも良いかも。ただ、constであってもオブジェクトのプロパティは更新可能なので、定数化しても良いかもしれない。
同じ意味合いの単語は統一すること
以下は類似単語の例
start, begin
stop, end
modify, update, change
create, new, factory
find, search
done, finish, complete
deliver, dispatch, send
select, choose
show, display
反対の意味を持つ値は対比の名前をつける
prev/next
first/last
create/delete
source/destination
add/remove
show/hide
min/max
public/private
success/failure
意味のない単語を含めない
String
いらない
personNameString
Data
やInfo
はなくてもいい。
UserData
やUserInfo
あと個人的に気になるのが、
usersList
のように複数形にListをつける形。users
だけで十分意味は伝わるのでList
は不要。
変数名を省略するコツ
a11y: accessibility
i18n: internationalization
init: initialize
calc: calculation
idx: index
cstm: customer
srvc: service
btn: button
_privateMethod, _privateProp(最近は#で表すようになった。)
関数定義
- 関数は責務を小さく作る
- 関数内でなるべく条件分岐をかかない
- 関数はDRYで作成使用
たまたま共通しているからといって共通しないように、ドメインを意識できると良い。
引数が多いとオブジェクトにまとめる
目安は引数が3以上?
function storeUser(user){
// userの保存処理
}
const user = {
userId: 1,
name: "Tom",
age: 22,
hobby: "soccer",
};
storeUser(user);
引数の設定では論理和よりデフォルト引数を使う
以下は初級者は結構見逃しがちかも。
論理和では`falsy`かどうかが判断基準
引数のデフォルト値は`undefined`かどうかが判断基準
例えば、falsyである0
を使うと、
論理和の場合はfalsyとなり、100が設定されてしまう。
一方でデフォルト引数の場合は想定通り0を設定できる。
レアケースかもしれないが、一応頭の片隅には押さえておきたい。
function drawSquare(color, size ){
size = size || 100;
render('square', size, color);
}
# 0にすると想定通りにならず、100がセットされる。
draqSquare('#fff', 0)
以下のようにデフォルト引数を使用するとundefined
のみセットされるので想定通りとなる。
function drawSquare(color, size = 100 ){
size = size || 100;
render('square', size, color);
}
drawSquare('#fff')
draqSquare('#fff', 0)
オブジェクトを引数で渡す
これはよく考えると使ったことなかったなー。
function createUser ({age = -1, name= "no name", posts = []} = {} ) => {
const registerUser = {age, name, posts};
db.user.create(registerUser);
}
関数は使用順に上から下に書く(垂直フォーマット)
参照透過性
決まった入力(引数)に対して、必ず決まった出力を返す
副作用のない関数を分離することでテストをしやすくなる
渡ってきたオブジェクトの中身を書き換えるのは止める
以下のように引数を変えて返すような記述はだめ。
function sortDesc(itemIds){
itemIds.sort();
}
const itemIds = [4, 3, 2, 1]
sortDesc(itemIds);
新しい変数を用意してそれに格納する。
function sortDesc(itemIds){
const newItemIds = [...itemIds]
newItemIds.sort();
return newItemIds
}
const itemIds = [4, 3, 2, 1]
const sortedItemIds = sortDesc(itemIds);
# クラス
- クラスはなるべく小さく作る
class Cart {
addProduct(){}
addProductPrice(){}
addProductCoupon(){}
calculatePrice(){}
printReceipt(){}
}
class Product {
getPrice(){}
getCoupon(){}
}
class Cart {
add(){}
calculatePrice(){}
printReceipt(){}
}
クラスの情報はできる限り隠蔽する
意図しない変更を避けるためにクラス情報はできる限り隠蔽する。
以下の書き方だと外部から呼び出し可能。
modelNumber = '型番'
# 以下の書き方でもok.引数を取る場合はconstructorを使うこと。
constructor(){
this.modelNumber = '型番'
}
}
外部からの呼び出しを避けるために、変数の前に#
をつける。
# 以下のように書くとインスタンスを生成しても
# console.log(television.#modelNumber)のように呼び出せない。
# #を使う場合、constructorで初期化する場合にも`#hoge`で冒頭で宣言する必要あり。
#modelNumber = '型番'
# ただし、クラス内の関数で呼ばれているものについては、外部からでも参照可能。
displayModelNumber(){
console.log(television.#modelNumber)
}
# 変更や取得もクラス内にメソッドを定義して可能。
get modelNumber() {
return this.#modelNumber;
}
set modelNumber(modelNumber) {
this.#modelNumber = modelNumber
}
}
# この取得や変更のセッター、ゲッターの場合、以下のような書き方ができる。
console.log(television.modelNumber) # ゲッター
television.modelNumber = '12345' # セッター