🦌
【JavaScript】値渡しと参照渡し / 値渡しの方法
値渡しと参照渡し説明
値渡し:
変数の値(実体)をコピーして渡す方法です。 変数の値をコピーして渡すため、関数内で引数の値を変更しても、呼び出し元の変数に影響を及ぼすことはありません。
参照渡し:
呼び出し先でも同じ実体を参照するように渡す方式。 渡された引数に変更を加えると呼び出し元にも同じように反映される。
値渡しの方法
1) 配列
値渡しの方法
const baseArray = [1, 2, 3, 4, 5]
const cloneArray = baseArray.slice() // ★
baseArray[0]=111
cloneArray[0]=222
console.log(baseArray); // Array [111, 2, 3, 4, 5]
console.log(cloneArray); // Array [222, 2, 3, 4, 5]
2) オブジェクト
値渡しの方法(1) 1階層目だけ値渡し
1階層は値渡しとなるが、2階層目以降は参照渡しとなる。
const baseObj = { a: 1, b: 2, c: {aa: 11, bb: 22} };
const cloneObj = Object.assign({}, baseObj) // ★
baseObj.d= 111
cloneObj.d = 222
console.log(baseObj); // { a: 1, b: 2, c: Object { aa: 11, bb: 22 }, d: 111 }
console.log(cloneObj); // { a: 1, b: 2, c: Object { aa: 11, bb: 22 }, d: 222 }
baseObj.c.cc = 111
cloneObj.c.dd = 222
// baseObj.c は参照によるコピーがされるため、baseObj.c = cloneObj.c となる。baseObj と cloneObj は同じ c を共有している。
console.log(baseObj); // Object { a: 1, b: 2, c: Object { aa: 11, bb: 22, cc: 111, dd: 222 }, d: 111 }
console.log(cloneObj); // Object { a: 1, b: 2, c: Object { aa: 11, bb: 22, cc: 111, dd: 222 }, d: 222 }
値渡しの方法(2) 2階層目以降も値渡し
完全なクローンを作り出せる。2階層目以降も値渡しができる。
const baseObj = { a: 1, b: 2, c: {aa: 11, bb: 22} };
const cloneObj = JSON.parse(JSON.stringify(baseObj)) // ★
baseObj.c.cc = 111
cloneObj.c.dd = 222
console.log(baseObj); // Object { a: 1, b: 2, c: Object { aa: 11, bb: 22, cc: 111 } }
console.log(cloneObj); // Object { a: 1, b: 2, c: Object { aa: 11, bb: 22, dd: 222 } }
参考資料
1) ログ出してチェック用コード
ログ出してチェックするようの参考資料
参考URL
[https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign:title]
上記URLで以下のコード実行することができ、logを確認することができます。
console.log("======オブジェクト======")
console.log("■ 参照渡 (1階層)")
const obj1 = { a: 1, b: 2 };
const obj2 = obj1;
obj1.c = 111
obj2.d = 222
// obj1 = obj2
console.log("補足:obj1 = obj2となる")
console.log("obj1",obj1);
console.log("obj2",obj2);
console.log("■ 値渡し(assign) /merge(1階層)")
const objAM1 = { a: 1, b: 2 };
const objAM2 = { b: 22, c: 3 };
const objAM3 = Object.assign(objAM1, objAM2);
objAM1.d = 111
objAM2.e = 222
objAM3.e = 333
// objAM1 = objAM3
console.log("補足:objAM1 = objAM3となる")
console.log("objAM1",objAM1);
console.log("objAM2",objAM2);
console.log("objAM3",objAM3);
console.log("■ 値渡し(assign) /clone(1階層)")
const objAC1 = { a: 1, b: 2 };
const objAC2 = Object.assign({}, objAC1);
objAC1.c = 111
objAC2.c = 222
// objAC1,objAC2 別々の値を保持
console.log("補足: objAC1,objAC2 別々の値を保持")
console.log("objAC1",objAC1);
console.log("objAC2",objAC2);
console.log("■ 値渡し(assign) /merge(複数階層)")
const objHAM1 = { a: 1, b: 2, c: {aa: 10, bb:20}};
const objHAM2 = { b: 22, d: 4, c: {bb: 200, cc:30}};
const objHAM3 = Object.assign(objHAM1, objHAM2);
objHAM1.c.zz=999
objHAM2.c.yy=999
objHAM3.c.xx=999
objHAM1.z=888
// * 謎な挙動
// objHAM1 = objHAM3
// objHAM1.c = objHAM2.c = objHAM3.c
console.log("補足(謎): objHAM1 = objHAM3 / objHAM1.c = objHAM2.c = objHAM3.c")
console.log("objHAM1",objHAM1);
console.log("objHAM2",objHAM2);
console.log("objHAM3",objHAM3);
console.log("■ 値渡し(assign) /clone(複数階層)")
let objHAC1 = {};
const objHAC2 = { a: 1, b: 2, c: {aa: 10, bb:20}};
objHAC1 = Object.assign({}, objHAC2);
objHAC1.b = 111
objHAC1.c.cc = 111
objHAC2.b = 222
objHAC2.c.dd = 222
// 1階層目は別々の値を保持, 2階層目以降は参照渡しとなる
console.log("補足:1階層目は別々の値を保持, 2階層目以降は参照渡しとなる")
console.log("objHAC1",objHAC1);
console.log("objHAC2",objHAC2);
console.log("■ 値渡し(JSON.parse(JSON.stringify)) /clone(複数階層)")
let objHJC1 = {};
const objHJC2 = { a: 1, b: 2, c: {aa: 10, bb:20}};
objHJC1 = JSON.parse(JSON.stringify(objHJC2));
objHJC1.b = 111
objHJC1.c.cc = 111
objHJC2.b = 222
objHJC2.c.dd = 222
// objHJC, objHJC2 別々の値を保持
console.log("補足:objHJC, objHJC2 別々の値を保持")
console.log("objHJC1",objHJC1);
console.log("objHJC2",objHJC2);
console.log("\n======配列======")
console.log("■ 参照渡")
const arr1 = [1, 2, 3, 4, 5]
const arr2 = arr1
arr1[4]=999
arr2[2]=100
console.log("arr1",arr1);
console.log("arr2",arr2);
console.log("■ 値渡し")
const arrS1 = [1, 2, 3, 4, 5]
const arrS2 = arrS1.slice()
arrS1[4]=999
arrS2[2]=100
console.log("arrS1",arrS1);
console.log("arrS2",arrS2);
2) 参考URL
Discussion
参照渡し でいう 参照は いわゆる変数のことなので変数基準で見てみてください。
これは参照渡しではないです。代入が反映されていません。ただのインスタンスの破壊的操作です。
プリミティブ値 が 参照型である保証は無いです。参照型の可能性があります。
プリミティブ値の実装として保証されるのは 関数を持たず、イミュータブルであるところまでです。