メインコンテンツまでスキップ

値渡しと参照渡し

プログラミング言語によっては、引数の渡し方に値渡し(pass-by-value)と参照渡し(pass-by-reference)の2種類が存在するものがあります。

値渡し

値渡しは、変数が関数に渡るタイミングで、変数が別の変数にコピーされます。元々同じ変数でも、関数の呼び出し元と関数の内部処理では、独立した値になります。そのため、関数の処理で引数に値を代入しても、関数呼び出し元の変数に影響しません。次は、C言語の値渡しの例です。変数a1で初期化され、関数changeaが渡されます。changeでは引数に2を代入しますが、呼び出し元の変数a1のままです。

c
#include <stdio.h>
void change(int n) {
n = 2;
}
int main() {
int a = 1;
change(a);
printf("%d", a); //=> 1
}
c
#include <stdio.h>
void change(int n) {
n = 2;
}
int main() {
int a = 1;
change(a);
printf("%d", a); //=> 1
}

参照渡し

参照渡しは、関数呼び出し元の変数が関数の処理でも共有されます。もし、関数の処理で引数に値を代入すると、関数呼び出し元の変数も変化します。次のコードはC言語の参照渡しの例です。変数aは関数changeに参照渡しされます。changeは引数に2を代入すると、changeの呼び出し元の変数aの値も2になります。

c
#include <stdio.h>
void change(int *n) {
*n = 2;
}
int main() {
int a = 1;
change(&a);
printf("%d", a); //=> 2
}
c
#include <stdio.h>
void change(int *n) {
*n = 2;
}
int main() {
int a = 1;
change(&a);
printf("%d", a); //=> 2
}

JavaScriptは値渡し

JavaScriptではこのような参照渡しをする機能はありません。関数の引数はすべて値渡しになります。したがって、引数に値を代入する処理を関数に書いても、その影響は関数呼び出し元には影響しません。

js
function change(n) {
n = 2;
}
let n = 1;
change(n);
console.log(n);
1
js
function change(n) {
n = 2;
}
let n = 1;
change(n);
console.log(n);
1

ところが、オブジェクトについては少し特殊です。オブジェクトはどこでも参照になります。どういうことかというと、オブジェクトに別の変数名をつけても、オブジェクトが複製されて新たなオブジェクトができるのではなく、異なる変数名同士でひとつのオブジェクトを指すということです。たとえば、次の例のようにオブジェクト{ n: 1 }を変数xに代入し、さらにxを変数yに代入すると、xyは同じオブジェクトを参照します。もし、yのプロパティnを変更すると、xnも変化します。

js
const x = { n: 1 };
const y = x;
y.n = 2;
console.log(x);
{ n: 2 }
js
const x = { n: 1 };
const y = x;
y.n = 2;
console.log(x);
{ n: 2 }

ただし、yに別の値を代入した場合は、xyは共通のオブジェクトを参照しなくなり、yへの変更はxには影響しなくなります。

js
const x = { n: 1 };
let y = x;
y = { n: 2 }; // yに別オブジェクトを再代入
y.n = 3;
console.log(x);
{ n: 1 }
js
const x = { n: 1 };
let y = x;
y = { n: 2 }; // yに別オブジェクトを再代入
y.n = 3;
console.log(x);
{ n: 1 }

以上のようにJavaScriptでは、あるオブジェクトに別の変数をつけたとき、そのオブジェクトを共有するようになっています。共有されたオブジェクトはプロパティを変更した場合、他の変数にもその変更が影響します。この仕様は引数にも同じことが言えます。たとえば、次の例です。オブジェクト{ n: 1 }を変数x代入し、さらにxを関数changeの引数yに代入すると、xyは同じオブジェクトを参照します。yのプロパティを変更すると、その影響は関数呼び出し元のxのプロパティにも影響します。

js
function change(y) {
y.n = 2;
}
const x = { n: 1 };
change(x);
console.log(x);
{ n: 2 }
js
function change(y) {
y.n = 2;
}
const x = { n: 1 };
change(x);
console.log(x);
{ n: 2 }

引数の場合も、変数の再代入の仕様と同様に、yに別の値を代入した場合は、xyは同じオブジェクトを参照しなくなるため、yへの変更はxに影響しなくなります。

js
function change(y) {
y = { n: 2 };
y.n = 3;
}
const x = { n: 1 };
change(x);
console.log(x);
{ n: 1 }
js
function change(y) {
y = { n: 2 };
y.n = 3;
}
const x = { n: 1 };
change(x);
console.log(x);
{ n: 1 }

以上をまとめると、JavaScriptの関数の引数は値渡しです。注意点として、オブジェクトは変数の場合と同様に、引数と呼び出し元の変数は同じオブジェクトを共有する仕様です。そして、もし引数のオブジェクトのプロパティを変更した場合、その変更は関数呼び出し元に影響します。

学びをシェアする

・JavaScriptの引数は値渡し
・JavaScriptにはC言語の参照渡しと同等の仕組みはない
・ただし、オブジェクトは値を共有する
・共有したオブジェクトを関数で変更した場合、呼び出し元にも影響する
・オブジェクトの共有は引数に限ったことではない

『サバイバルTypeScript』より

この内容をツイートする
  • 質問する ─ 読んでも分からなかったこと、TypeScriptで分からないこと、お気軽にGitHubまで🙂
  • 問題を報告する ─ 文章やサンプルコードなどの誤植はお知らせください。