JavaScriptにおける変数とメモリについて

2023/01/22に公開

今回は、変数とメモリの関係について解説していきます。

これらを理解していると、実装上のエラーなどを起こしにくくなるのでとても大事です。

JavaScriptにおける値の扱い方は、プリミティブ型とオブジェクト型で大きく違うのそれぞれ解説していきます。

JavaScriptにおける変数とメモリについて

プリミティブ型

まず、プリミティブ型の値について解説していきます。

プリミティブ型では、実際の値が格納されているメモリへの参照情報が変数に格納されます。

そしてその参照情報を元に、目当ての別のメモリの情報を取得できる訳です。

図にした方が分かりやすいかと思うので、以下のように図解しました。

つまり、この参照先情報を保持しているメモリを人間にも分かりやすいようにエイリアス化したものが「変数名」なのです。

オブジェクト型

次に、オブジェクト型の場合を見ていきます。

こちらはまず、変数にオブジェクトへの参照情報が格納されます。

そして、オブジェクトにはそれぞれのプロパティへの参照情報が格納されています。

さらに、プロパティにはそれぞれの値への参照情報が格納されています。

なので、それを元に変数と同じように、値へアクセスできる訳です。

文字だけだと何言ってるか分からないと思うので、こちらも図解しました。

値のコピーと再代入について

次に、変数をコピーした場合の挙動も見ていきます。

プリミティブ型

まず、プリミティブ型ですがコピーした場合は、参照先のメモリをコピーすることになります。

先ほどの図で言うと、y番地が2つに複製される感じです。

なので、値を更新しても次のようになります。

let a = 10
let b = a
b = 20
// 10, 20
console.log(a, b)

つまり、新しいメモリの参照情報を保持するように変更されるだけです。

オブジェクト型

次にオブジェクトですが、こちらはオブジェクトへの参照情報をコピーすることになります。

先ほどの図で言うと、b番地が複製される感じです。

なので、プロパティへの参照情報を書き換えた場合は、次のような結果になります。

let obj = { a: 10, b:20}
let obj2 = obj
obj.a = 20
// { a: 20, b:20}, { a: 20, b:20}
console.log(obj, obj2)

ただ、オブジェクトへの参照情報を書き換えた場合は、次のような結果になります。

let obj = { a: 10, b:20}
let obj2 = obj
obj2 = { a: 20, b:20}
// { a: 10, b:20}, { a: 20, b:20}
console.log(obj, obj2)

こちらの場合は、複製されたb番地の情報が全く書き換えられるためです。

ちなみに、constで宣言した場合は、オブジェクトへの参照がロックされるだけなので、プロパティへの参照などは書き換えることが可能です。

つまり、コードで説明すると次の通りです。

const obj = { a: 10, b:20}

// OK
obj.a = 20
// NG
obj = { a: 20, b:20}

比較

次に、値の比較について見ていきます。

こちらもプリミティブ型とオブジェクト型で挙動が変わってきます。

プリミティブ型

まず、プリミティブ型は参照してる値が同じ場合は、格納されているメモリが違くてもtrueとなります。

const a = 10
const b = 10

// true 値が同じなため
console.log(a === b)

けれど、オブジェクト型の比較は、飽くまでも参照先情報の比較となります。

なので、変数のエイリアスの元となっている参照先情報が同じな場合はtrueとなる訳です。

こちらもコードで見た方が分かりやすいと思うので、以下の通りです。

const obj = { a: 10, b:20}
let obj2 = obj

// true 参照先のメモリが同じなため
console.log(obj === obj2)

obj2 = { a: 10, b:20}
// false 値が同じでも参照先のメモリが違うため
console.log(obj === obj2)

関数の引数や分割代入の場合

次に、関数の引数や分割代入にした場合を見ていきます。

結論から言うと、次のコードの通りです。

const obj = {
  a: 10,
  b: 20
}

const func = (obj2) => {
  obj2.a = 20
  // { a:20, b:20}
  console.log(obj)
}

func(obj)

関数の引数の場合は、普通のコピーと同じ挙動となります。

けれど、分割代入をした場合は、値への参照情報をコピーするので次のような結果になります。

const obj = {
  a: 10,
  b: 20
}

const func = ({a, b}) => {
  a = 20
  // { a:10, b:20}
  console.log(obj)
}

なので、基本的に分割代入を使った方が安全なコードを書くことができます。

まとめ

今回はJavaScriptにおける変数とメモリの関係について解説してきました。

こちらを理解していると、コーディングの質が上がってくると思うので参考にしてください。

宣伝

0からエンジニアになるためのノウハウをブログで発信しています。
https://hinoshin-blog.com/

また、YouTubeでの動画解説も始めました。
YouTubeのvideoIDが不正ですhttps://www.youtube.com/channel/UCqaBUPxazAcXaGSNbky1y4g

インスタの発信も細々とやっています。
https://www.instagram.com/hinoshin_enginner/

興味がある方は、ぜひリンクをクリックして確認してみてください!

Discussion