かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript 分割代入な引数にデフォルト値を設定したい

オブジェクトを分割代入で引数にとる関数の引数にデフォルト値を設定する方法のメモ

引数を分割代入でとる関数

const greet = ({ name, msg }) => {
  console.log(`${msg}, ${name}!`);
};

greet({
  name: 'Ichigo',
  mgs: 'Hi',
});
// => "Hi, Ichigo!"

分割代入で引数を取ると、渡す引数の順番を考慮しなくて済むので便利です。
こんな関数の引数にデフォルト値を設定したい

分割代入な引数にデフォルト値を設定する

const greet = ({ name, msg = 'Hi' } = {}) => {
  console.log(`${msg}, ${name}!`);
};

greet({
  name: 'Aoi'
});
// => "Hi, Aoi!"

{key = default value} = {} の形でデフォルト値を設定できる
= {} を付けないと、引数を渡さずに関数を実行するとエラーになる
🙅‍♀️

const func = ({ foo = 'bar' }) => {
  console.log(foo);
};
func({foo: 'buzz'});
// => "buzz"
func();
// => TypeError: Cannot destructure property `foo` of 'undefined' or 'null'.

仕組みについて考えてみる

オブジェクトキーの省略形

ES2015 (ES6) でオブジェクトの key と同じ名前の変数名が value の時 : value を省略して書くことができる
obj = { prop: prop }obj = { prop } と書くことができる。

つまり、引数のデフォルト値になっている部分の

{ foo = 'bar' }
// 👇 の省略形
{ foo: foo = 'bar' }

オブジェクトの分割代入

分割代入される側 (変数になる側) のキーが渡されるオブジェクトの同じキーの値を参照している

const obj = { foo: 1, bar: 2 };
const { foo, bar, buzz } = obj;
// foo => 1, bar => 2, buzz => undefined
// 👇
foo = obj.foo; // => 1
bar = obj.bar; // => 2
buzz = obj.buzz; // => undefined

参照する値が undefined だと代入されない

const { foo = 'foo' } = { bar: 2 };
// foo => "foo"

{ foo = 'foo' }{ foo: foo = 'foo'} なので { foo = 'foo' } = { bar: 2 }

const obj = { bar: 2 };
const { foo: foo = 'foo' } = obj;
// foo => "foo"
// 👇
foo = obj.foo || 'foo' /* foo の value */ ;
// foo => "foo"

分割代入な引数のデフォルト値の仕組み

上記の挙動から変換すると次のようなイメージ

const greet = ({ name, msg = 'Hi' } = {}) => {
  console.log(`${msg}, ${name}!`);
};

greet({ name: 'Ichigo' });
// => "Hi, Ichigo!"

// 👇 挙動のイメージ

greet = ( { name, msg = 'Hi' } = {} ) => {}
// 👇関数の呼び出し greet({ name: 'Ichigo' });
greet = ( { name, msg = 'Hi' } = { name: 'Ichigo' } ) => {}
// 👇引数部分
var arg{ name, msg = 'Hi' } = obj{ name: 'Ichigo' };
name = obj.name || arg.name; // => "Ichigo"
msg  = obj.msg || arg.msg; // => 'Hi'
// 👇値が設定された変数が関数本体に渡される
(name="Ichigo", msg="Hi") => {
  console.log(`${msg}, ${name}!`);
}
// => "Hi, Ichigo!"

オブジェクトの分割代入はキーが変数名になるわけではない

const { foo: a = "bar" } = { foo: 1  };
foo; // => ReferenceError: foo is not defined
a; // => 1

// 👇
arg{ foo: a = "bar" } = obj{ foo: 1 }
a = obj[ 'foo' /* arg a のキー  */ ] || "bar"

つまり、{ foo } = { foo: 1 }のような分割代入される側のキーの名前が変数になるのではなく、省略されている変数が変数として出てきているという事だった。

const obj = { foo: 1 };
const { foo } = obj;
// 👇
arg{ foo: foo = undefined } = obj{ foo: 1 }
// 👇
foo(argの値の変数) = obj[ 'foo' /* arg の value foo のキーの "foo" */ ] || undefined;
foo; // => 1

異なる名前の変数に代入して既定値を設定する
プロパティに対して、1) オブジェクトから取り出して異なる名前の変数に代入することと、2) 取り出した値が undefined である場合に既定値を代入することの両方が可能です。

var {a: aa = 10, b: bb = 5} = {a: 3};
console.log(aa); // 3
console.log(bb); // 5
cf. 分割代入 - JavaScript | MDN

感想

オブジェクトの分割代入はキーが同じ名前の変数になっているのだと思っていたので、ミス理解を正せたので良かった。
理屈が解れば難しい構文ではなくなるので、基礎理解は大切ですね…
(基礎練習ばかりで試合に出たことがない人みたいになってきてるけど…)


[参考]

AIKATSU! ANION "NOT ODAYAKA" Remix

AIKATSU! ANION "NOT ODAYAKA" Remix

  • 発売日: 2019/03/30
  • メディア: MP3 ダウンロード

アイカツ!を見ると幸せになれる