javaScriptのコピーについて、少しつまづいた所があったから、備忘録として記録する。
意外と今までなんとなくで対応していたなと思ったので、今回を機に色々調べてみた。
javaScriptの型について
javaScriptは動的型付け言語ですが、データに対してちゃんと型はある。
分類としては、プリミティブ型・オブジェクト型に分けることができる。
以下、例を書いておく。
プリミティブ型
- String
- Number
- BigInt
- Boolean
- Undefine
- Symbol
- Null
オプジェクト型
- Object
- Array
- function
- Date
など
プリミティブ型以外は全てオブジェクト型となるため、様々な例が考えられる。
プリミティブ型とオブジェクト型の違い
プリミティブ型とオブジェクト型の違いとして、値の格納方法がある。
プリミティブ型は、変数に値を直接保存する方法に対して、オブジェクト型は、変数にオブジェクトへの参照を保存している。
つまり、オブジェクト型は、変数にオブジェクトへの参照(値が保存されているメモリのポインタ)が格納されているため、コピーした際他の変数にも影響してしまう。
プリミティブ型
let person = 'name'
let new_person = person
new_person = 'new_name';
console.log(person, new_person)
//出力値
name new_name
値が直接コピーされるため、新たなメモリに保存し、変数同士が独立している。
オブジェクト型
let person = {name: 'name', age: 3};
let new_person = person
new_person.name = 'new_name';
console.log(person, new_person)
//出力値
{ name: 'new_name', age: 3 } { name: 'new_name', age: 3 }
参照先がコピーされているため、元の変数にも影響が出ていることがわかる。
シャローコピーとディープコピー
object型には二つのコピー方法がある。
まずはシャローコピーについて。
シャローコピー
シャローコピーとは、コピー元のオブジェクトと同じ参照をコピーするため、参照場所のデータを共有することになる。
そのため、コピーした値を変更するとコピー元にも影響が出る可能性がある。
let person = [
1,
{name: 'name2', age: 4},
{name: 'name3', age: 5}
];
let new_person =[...person]
//参照先のデータを変更
new_person[1].name = 'new_name'
console.log(person, new_person)
//出力値
[ 1, { name: 'new_name', age: 4 }, { name: 'name3', age: 5 } ]
[ 1, { name: 'new_name', age: 4 }, { name: 'name3', age: 5 } ]
次に、配列のネストされていない要素を変更すると、コピー元は変更されずに出力される。
let person = [
1,
{name: 'name2', age: 4},
{name: 'name3', age: 5}
];
let new_person =[...person]
//
new_person[0] = 3
console.log(person, new_person)
//出力値
[ 1, { name: 'name2', age: 4 }, { name: 'name3', age: 5 } ]
[ 3, { name: 'name2', age: 4 }, { name: 'name3', age: 5 } ]
つまり、シャローコピーは深さ1のみをコピーし、ネストされた値を共有しないためには、要素の中身も再起的にコピーすることで完全に独立したコピーを作成しなければならない。
ディープコピー
ディープコピーとは、参照場所のデータを共有しない、完全に独立したコピーとなる。
そのため、コピーした値を変更するとコピー元にも影響が出る可能性は基本的にはない。
let person = [
1,
{name: 'name2', age: 4},
{name: 'name3', age: 5}
];
let new_person = JSON.parse(JSON.stringify(person))
new_person[1].name= 'new_name'
console.log(person, new_person)
//出力値
[ 1, { name: 'name2', age: 4 }, { name: 'name3', age: 5 } ]
[ 1, { name: 'new_name', age: 4 }, { name: 'name3', age: 5 } ]
まとめ
今回はシャローコピーとディープコピーについてまとめてみた。
意外と基礎が疎かになっていることを最近発見してきているので、こういった知識をこれからなんとなくではなく、説明できるくらいには知識をつけていきたいと思う。