JavaScriptでは単なるオブジェクトや配列などの値を持つ変数を生成するときに、通常のイコールで結んだ式ではなく、あえて、関数を使ってオブジェクトや配列などの値を生成することがあります。
ここではなぜ、オブジェクトや配列を関数で生成するのかについてその理由をまとめています。
オブジェクトや配列を関数で生成するとは?
最初に、オブジェクトや配列を関数で生成するとはどういったことを指しているのか補足しておきます。
通常
通常、オブジェクトや配列を変数に代入するときは以下のようにします。
obj = {a:1, b:2}
arr = [1, 2]
関数で生成する
関数でオブジェクトや配列を生成すると以下のようになります。
obj = () => { return { a:1, b:2 } }
arr = () => { return [1, 2] }
なお、上記はアロー関数ですが、functionを使って記述すると以下のようになります。(どちらも同じです)
obj = function(){ return { a:1, b:2 } }
arr = function(){ return [1, 2] }
なぜオブジェクトや配列を関数で生成するのか?
なぜわざわざオブジェクトや配列を関数で生成するのかというと、異なる変数に代入したときにそれぞれを別物として扱うためです。
関数を使って生成することで、作成したオブジェクトや配列が各々、固有になります。
一方、通常の指定方法だと、異なる変数に代入しても、それぞれがイコールで結びついてしまいます。結果として、一つの要素を変更すると他の全ての変数の値も変更されてしまいます。
このような意図しない書き換えを防ぐために、各変数同士を依存関係にしたくない場合は関数を使います。
イコールで結ばれるとは?(依存関係にある状態)
依存関係にある状態、すなわち値がイコールで結ばれる状態とは以下のような状態です。
オブジェクトの場合
例えば、{a:1, b:2}という値をもつ変数「obj」をそれぞれ異なる変数「x」と「y」に代入します。
obj = {a:1, b:2}
x = obj
y = obj
このとき、「x」と「y」を比較すると、それぞれ同じ内容になっています。
x == y
//true
このため、「x」の値を書き換えると、「y」も自動的に書き換わってしまいます。
//xに{ c: 3 }を追加
x['c']=3
//yの中身を確認
console.log(y)
//出力
{a: 1, b: 2, c: 3}
xを変更しただけなのに、yのオブジェクトも変わってしまいます。
配列の場合
例えば、[1, 2]という値をもつ変数「arr」をそれぞれ異なる変数「x」と「y」に代入します。
arr = [1, 2]
x = arr
y = arr
このとき、「x」と「y」を比較すると、それぞれ同じ内容になっています。
x == y
//true
このため、「x」の値を書き換えると、「y」も自動的に書き換わってしまいます。
//xに3を追加
x.push(3)
//yの中身を確認
console.log(y)
//出力
[1, 2, 3]
xを変更しただけなのに、yのオブジェクトも変わってしまいます。
このような書き換えを防ぐために、関数を使ってオブジェクトや配列を生成する必要があります。
関数で生成するとどうなるか?(依存関係にない状態)
関数でオブジェクトや配列を生成すれば、それぞれが依存関係ではなくなります。
オブジェクトの場合
例えば、{a:1, b:2}という値を生成する関数「obj」をそれぞれ異なる変数「x」と「y」に代入します。
obj = () => { return { a:1, b:2} }
x = obj()
y = obj()
このとき、「x」と「y」を比較すると、異なる内容になっています。
x == y
//false
このため、「x」の値を書き換えても、「y」の値は置き換わりません。
//xに{ c: 3 }を追加
x['c']=3
//yの中身を確認
console.log(y)
//出力
{a: 1, b: 2}
置き換えた「x」の値は処理に合わせて変更されています。
//xの中身を確認
console.log(x)
//出力
{a: 1, b: 2, c: 3}
配列の場合
例えば、[1, 2]という値を生成する関数「arr」をそれぞれ異なる変数「x」と「y」に代入します。
arr = () => [1, 2]
x = arr()
y = arr()
このとき、「x」と「y」を比較すると、異なる内容になっています。
x == y
//false
このため、「x」の値を書き換えても、「y」の値は置き換わりません。
//xに3を追加
x.push(3)
//yの中身を確認
console.log(y)
//出力
[1, 2]
置き換えた「x」の値は処理に合わせて変更されています。
//xの中身を確認
console.log(x)
//出力
[1, 2, 3]
数値や文字列を関数で生成しても同じになる
なお、関数で生成したことで別々の要素として扱えるのは、オブジェクトと配列です。
数値や文字列は関数で生成したとしても、値が同じであれば同じものとしてみなされます。
数値の場合
num = () => 2
x = num()
y = num()
x == y
//
true
文字列の場合
str = () => "a"
x = str()
y = str()
x == y
//
true
通常のオブジェクトや配列を代入した変数の値を数値や文字列に変えた場合
通常の方法で変数に代入したオブジェクトや配列を代入した場合でも、変数に数値や文字列を代入しなおした場合は、それぞれの変数は異なる要素を指すことになるので、他の変数は置き換わりません。
あくまで、値となっているオブジェクトや配列が同じものを指しているということです。
obj = {a:1, b:2}
x = obj
y = obj
x == y
//true
上記の状態で、xに数値を代入します。
x = 1
console.log("x:", x)
console.log("y:", y)
//出力
x: 1
y: {a: 1, b: 2}