【JavaScript】なぜオブジェクトや配列を関数で生成するのか?|代入と変更による書き換えを防止する方法(変数 = () => { return })

javascript-prograshi(プロぐらし)-kv JavaScript
記事内に広告が含まれていることがあります。

JavaScriptでは単なるオブジェクトや配列などの値を持つ変数を生成するときに、通常のイコールで結んだ式ではなく、あえて、関数を使ってオブジェクトや配列などの値を生成することがあります。

ここではなぜ、オブジェクトや配列を関数で生成するのかについてその理由をまとめています。


オブジェクトや配列を関数で生成するとは?

最初に、オブジェクトや配列を関数で生成するとはどういったことを指しているのか補足しておきます。

通常

通常、オブジェクトや配列を変数に代入するときは以下のようにします。

obj = {a:1, b:2}
arr = [1, 2]


関数で生成する

関数でオブジェクトや配列を生成すると以下のようになります。

obj = () => { return { a:1, b:2 } }
arr = () => { return [1, 2] }
補足

JavaScriptでは処理中の式と戻り値が1つだけの場合、処理の{}とreturnを省略することができます。(※ただし、オブジェクトの場合は{ }が処理のカッコと勘違いされるため省略できません)

arr = () => { return [1, 2] }

 ↓↑ 同じ

arr = () => [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}

タイトルとURLをコピーしました