Text
Page: 1
頑なに再代入しない!
abetomo
ClearCode Inc.
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 2
はじめに
あくまで「頑なに再代
入しない!」について
のお話
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 3
内容
✓ 頑なに再代入しない!ワケ
✓ 再代入しない!コード例
✓ 再代入しない!デメリット検証
✓ 再代入しない!実践してみて
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 4
再代入のコード例
let price = 100
if (キャンペーンA期間中) {
price = price * 0.8
}
// ...
if (キャンペーンF期間中) {
// キャンペーンF期間中のみ税込価格から2割引
price = (price * 1.1) * 0.8
} else {
// ...
}
// ...
// `price` はいくつ?
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 5
頑なに再代入しない!のワケ
✓ 読みやすさ・メンテナンス性の向上
✓ バグの発生リスクを低減
✓ 保守性・変更への強さ
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 6
メンテナンス性の向上
✓ 変数の値が途中で変わることがないため、
「この時点で何の値か」を一目で把握でき
る
✓ 値の追跡が容易になり、コードの全体像や
処理の流れが理解しやすい
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 7
バグの発生リスクを低減
✓ 意図しない再代入によるバグや予期せぬ挙
動が発生しにくく、信頼性が高まる
✓ 複数箇所で値を変更する必要がなくなるた
め、バグを防ぎやすい
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 8
保守性・変更への強さ
✓ 変数の状態が関数内で常に不変なら、変更
やリファクタ時の影響調査が容易になる
✓ 意図しない影響範囲が狭くなり、スコープ
も明確になる
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 9
内容
✓ 頑なに再代入しない!ワケ
✓ 再代入しない!コード例
✓ 再代入しない!デメリット検証
✓ 再代入しない!実践してみて
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 10
再代入しない!コード例
✓ フラグの設定
✓ 参考: 配列から抽出
✓ 参考: 配列の加工
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 11
例: 再代入あり: フラグ設定
let flag = false
if (t % 2 === 0) {
flag = true
} else if (t % 7 === 0) {
flag = true
}
// ...
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 12
例: 再代入なし: フラグ設定
const flag = (() => {
if (t % 2 === 0) {
return true
}
if (t % 7 === 0) {
return true
}
return false
})()
// ...
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 13
参考: 素朴に配列から抽出
(正確には再代入してないけど…)
const data = Array.from({ length: 1000 }, (_, i) => i)
const length = data.length
const result = []
for (let i = 0; i < length; i++) {
if (data[i] % 2 === 0) {
result.push(data[i])
}
}
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 14
補足: Array.prototype.push()
✓ constで宣言したresultにpushしてた
よ?
✓ constは変数への再代入ができなくなるだ
け
✓ ArrayやObjectの要素は変更できる
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 15
補足: pushでエラー
次のコードは再代入なのでエラー
const data = []
data = [1]
// data = [1]
//
^
//
// TypeError: Assignment to constant variable.
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 16
参考: 配列から抽出(filter)
const data = Array.from({ length: 1000 }, (_, i) => i)
const result = data.filter((v) => v % 2 === 0)
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 17
参考: 素朴に配列の加工
(正確には再代入してないけど…)
const data = Array.from({ length: 1000 }, (_, i) => i)
const length = data.length
const result = []
for (let i = 0; i < length; i++) {
result.push(data[i] * 2)
}
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 18
参考: 配列の加工(map)
const data = Array.from({ length: 1000 }, (_, i) => i)
const result = data.map((v) => v * 2)
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 19
ここまでまとめ
✓ 再代入がないほうがわかりやすい!
✓ 途中で値を追加したりしないほうがわかり
やすい!
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 20
内容
✓ 頑なに再代入しない!ワケ
✓ 再代入しない!コード例
✓ 再代入しない!デメリット検証
✓ 再代入しない!実践してみて
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 21
再代入しないデメリット
やはり気になるのは実行速度。
遅いんだろうな、と思いつつ、どのくらい遅い
のか検証したことがなかったので、この機会
にざっくり検証してみました。
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 22
検証方法
✓ Node.js 24で検証
✓ Dockerイメージ node:24 を活用
✓ console.time() で測定
✓ 時間が短いほうが速い
✓ 5回実行して中央値
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 23
速度検証: フラグ
次の3パターンで測定する。
1. IIFE(即時実行関数式)
2. 関数をつくる
3. 再代入でフラグ設定
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 24
IIFE検証
const n = 100_000
console.time('no reassignment IIFE')
for (let t = 0; t < n; t++) {
const flag = (() => {
if (t % 2 === 0) {
return true
}
if (t % 7 === 0) {
return true
}
return false
})()
}
console.timeEnd('no reassignment IIFE')
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 25
関数実行で検証
console.time('no reassignment func call')
const checkValue = (num) => {
if (num % 2 === 0) {
return true
}
if (num % 7 === 0) {
return true
}
return false
}
for (let t = 0; t < n; t++) {
const flag = checkValue(t)
}
console.timeEnd('no reassignment func call')
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 26
再代入検証
console.time('reassignment')
for (let t = 0; t < n; t++) {
let flag = false
if (t % 2 === 0) {
flag = true
} else if (t % 7 === 0) {
flag = true
}
}
console.timeEnd('reassignment')
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 27
速度検証: フラグの結果
時間
IIFE11.51ms
関数実行1.99ms
再代入1.929ms
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 28
参考: 速度検証: filter
時間
素朴に抽出19.696ms
filter34.01ms
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 29
参考: 速度検証: map
時間
素朴に加工32.12ms
map21.378ms
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 30
ここまでまとめ
再代入しない、は遅
い…?
許容範囲?
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 31
他に再代入しないデメリット
オプジェクトのコピーなどの操作が増えるの
で、メモリの使用量など気になるところ。
いい感じの検証ができなかったので 発表時間
も限られるので今回の発表では割愛。
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 32
内容
✓ 頑なに再代入しない!ワケ
✓ 再代入しない!コード例
✓ 再代入しない!デメリット検証
✓ 再代入しない!実践してみて
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 33
実践したソフトウェア
node-lambda
AWS Lambdaにコードをdeployするソフト
ウェア。
Lambdaが始まった頃から開発されていて、私
自身も使っていた。
(その後はSAMに乗り換え。)
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 34
再代入しない実践してみて +
✓ やっぱり値が不変なので安心
✓ コードレビューもしやすい
✓ 関数をつくることに意識が向きやすい
✓ ユニットテストがしやすくなるメリットも
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 35
バグが少ない?
✓ 私の全PRは296件
✓ 2017年の春頃からなので8年分
✓ PRにbugが含まれるのは7件
✓ 7 / 296 = 2%
✓ タイトルにfixが含まれるPRは54件
✓ 54 / 296 = 18%
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 36
再代入しない実践してみて -
✓ 関数即時実行が慣れないと読みにくい、か
も
✓ ツールの特性上、実行速度にシビアになる
必要がなかった
✓ 実行速度が重要な場面では性能検証はしっかりした
方が良い
✓ ユニットテストでチェックとか
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 37
最後に実際のコード紹介
node-lambdaのコード
を紹介します。
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 38
実際の例: ファイル名の取得
const filename = (() => {
for (const extension of ['.js', '.mjs']) {
if (fs.existsSync(splitHandler[0] + extension)) {
return splitHandler[0] + extension
}
}
})()
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 39
実際の例: map
const paramsList = scheduleList.map((schedule) =>
Object.assign(schedule, { FunctionArn: functionArn }))
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 40
実際の例: isXXX
_isUseS3 (program) {
if (typeof program.deployUseS3 === 'boolean') {
return program.deployUseS3
}
return program.deployUseS3 === 'true'
}
_useECR (program) {
return program.imageUri != null && program.imageUri.length > 0
}
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 41
まとめ
私の頑なに再代入をしない思いのたけを書き
ました。
保守しやすくて良い点もありますし、書き方
によっては遅くなったりしますし、適切な場面
で適切に活用すると良いと思います!
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 42
おまけ: 再代入しない?
✓ let はない?
✓ 再代入?
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 43
let はない?
$ grep -r 'let ' node-lambda/{bin,lib} | wc -l
5
let がある…。
頑なに再代入しない!
Powered by Rabbit 4.0.1
Page: 44
再代入?
_lambdaFunctionConfiguration (params) {
const lambdaFunctionConfiguration = {
Events: params.Events,
LambdaFunctionArn: params.FunctionArn
}
if (params.Filter != null) {
// 再代入?
lambdaFunctionConfiguration.Filter = params.Filter
}
return lambdaFunctionConfiguration
}
頑なに再代入しない!
Powered by Rabbit 4.0.1