既定値が働くのは undefined のときだけです。null では働きません。可変長引数の受け取り方、オブジェクトで受けて名前を付ける書き方、length が数えない引数までを実行して確かめます。
= で既定値を書けます。働くのは undefined のときだけです。
example.js
function greet(name = 'あなた', mark = '!') {
return name + mark;
}
console.log(greet());
console.log(greet('あ'));
console.log(greet(undefined, '?'));
出力書き換えて実行できます
あなた!
あ!
あなた?
javascript
function greet(name = 'あなた') {
return name + '!';
}
console.log(greet(null));
null!
undefined を渡すと渡していないのと同じ扱いになります。
だから「2番目だけ指定したい」ときに undefined を書けます。
null では働かない
example.js
function greet(name) {
return (name ?? 'あなた') + '!';
}
console.log(greet(null));
console.log(greet(undefined));
console.log(greet(''));
出力書き換えて実行できます
あなた!
あなた!
!
example.js
function sum(...nums) {
return nums.reduce((total, n) => total + n, 0);
}
console.log(sum());
console.log(sum(1));
console.log(sum(1, 2, 3));
出力書き換えて実行できます
0
1
6
null は「値として渡された」ので、既定値に置き換わりません。
null も既定値にしたいなら ?? を使います。
example.js
function head(first, ...rest) {
return [first, rest];
}
console.log(head(1, 2, 3));
console.log(head(1));
出力書き換えて実行できます
[ 1, [ 2, 3 ] ]
[ 1, [] ]
example.js
function show({ name, city = '不明' } = {}) {
return `${name}/${city}`;
}
console.log(show({ name: 'あ', city: '東京' }));
console.log(show({ name: 'い' }));
console.log(show());
出力書き換えて実行できます
あ/東京
い/不明
undefined/不明
空文字はそのまま通ります。|| にすると空文字も置き換わるので注意してください。
→ なぜ typeof null は "object" なのか
何個でも受け取る
... を付けた引数(残余引数)は、残り全部を配列で受けます。
javascript
function show({ name }) {
return name;
}
console.log(show());
TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.
example.js
function f(a, b = 1, ...c) {}
function g(a, b) {}
console.log(f.length);
console.log(g.length);
出力書き換えて実行できます
1
2
渡されなければ空配列です。undefined にはなりません。
前に固定の引数を置けます。残余引数は最後にしか書けません。
example.js
function add(a, b) {
return a + b;
}
console.log(add(1, 2, 3, 4));
console.log(add(1));
出力書き換えて実行できます
3
NaN
example.js
function add(a = 0, b = 0) {
return a + b;
}
console.log(add(1));
出力書き換えて実行できます
1
古い書き方の arguments は、アロー関数では使えず、配列でもありません。新しく書くなら残余引数を使ってください。
オブジェクトで受ける
引数が3つを超えたら、オブジェクトで受けるほうが読みやすくなります。
example.js
function makeTag(name, label = name.toUpperCase()) {
return `${name}:${label}`;
}
console.log(makeTag('a'));
console.log(makeTag('a', 'B'));
出力書き換えて実行できます
a:A
a:B
example.js
function push(value, list = []) {
list.push(value);
return list;
}
console.log(push(1));
console.log(push(2));
出力書き換えて実行できます
[ 1 ]
[ 2 ]
- 呼び出し側から「何を渡しているか」が読めます
- 順番を覚えなくてよくなります
- あとから項目を足しても、既存の呼び出しが壊れません
= {} を付けているのは、引数そのものを省けるようにするためです。
付けないと、何も渡さなかったときに落ちます。
javascript
function addTax(price) {
price = price * 1.1;
return price;
}
const original = 100;
console.log(addTax(original));
console.log(original);
110.00000000000001
100
javascript
function rename(user) {
user.name = '変えた';
return user;
}
const user = { name: 'あ' };
rename(user);
console.log(user);
{ name: '変えた' }
→ 入れ子のオブジェクトを安全に扱う
length は全部を数えない
example.js
function rename(user, name) {
return { ...user, name };
}
const user = { name: 'あ' };
console.log(rename(user, '変えた'));
console.log(user);
出力書き換えて実行できます
{ name: '変えた' }
{ name: 'あ' }
length が数えるのは、既定値と残余引数が出てくる前までです。
f は3つ書いてあるのに 1 です。
「この関数は何個の引数を取るか」の答えとしては使えません。
引数の個数に応じて処理を変えたいなら、残余引数で受けて nums.length を見てください。
余分に渡しても落ちない
多すぎても少なすぎても例外になりません。
足りない分は undefined になり、計算に使うと NaN になります。
「呼び方を間違えても動いてしまう」ので、既定値を書いておく価値があります。
既定値には式が書ける
前の引数を使えます。 評価されるのは呼ばれたときなので、
毎回新しい値になります。
ただし後ろの引数は使えません。 まだ決まっていないからです。
よくある間違い
既定値にオブジェクトを書いて共有されると思う
毎回新しい配列が作られます。 前の呼び出しの結果は残りません。
他の言語では既定値が1度だけ評価され、共有されてしまうものがあります。JavaScript は呼ばれるたびに評価します。
引数を書き換える
数値は複製されるので元は変わりません。ですがオブジェクトは変わります。
受け取ったオブジェクトを書き換えないでください。
新しいものを返すほうが、呼び出し側から見て安全です。
まとめ
- 既定値が働くのは
undefinedのときだけ。null では働かない
null も含めるなら ??
...rest は残り全部を配列で受ける。無ければ空配列
- 引数が多いならオブジェクトで受ける。
= {} を忘れない
length は既定値の手前まで。引数の個数ではない
- 多すぎても少なすぎても落ちない。足りない分は
undefined
- 受け取ったオブジェクトを書き換えない