Skip to content
Merged
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 50 additions & 20 deletions docs/reference/functions/pass-by-value.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,72 @@
# 値渡しと参照渡し

プログラミング言語によっては、引数の渡し方に値渡し(pass-by-value)と参照渡し(pass-by-reference)の2種類が存在するものがあります。
関数の実引数に変数を指定するとき、仮引数への渡し方に値渡し(pass-by-value)と参照渡し(pass-by-reference)の2種類があります(値渡しは更に細かく分けられます)。

- 実引数:関数を呼び出す側が引き渡す即値(123など)か変数
- 仮引数:関数側が引き受ける変数

参照渡しできないプログラミング言語もあります。

- 参照渡しできる言語:C++, C#, VB.NET, PHP, Swift など
- 参照渡しできない言語:C, Java, JavaScript, Python, Ruby など

## 変数の種類

変数には値型と参照型の2種類があります。

- 値型変数:変数に代入値が保持される(保持値=代入値)
- 参照型変数:変数に参照値が保持され、変数から代入値を参照する(保持値≠代入値)

参照値は、代入値がメモリ上のどこにあるのかを識別する値で、メモリアドレスやハッシュ値などが用いられます。
値型変数も参照型変数も値渡しと参照渡しができます。

## 値渡し

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

- 値型変数:代入値のコピーが渡される(コピー渡し)
- 参照型変数:参照値のコピーが渡され、参照先の値はコピーされずに共有される(参照の値渡し、共有渡し、値共有)

実引数と仮引数は、独立した変数、独立した値になります。そのため、関数内で仮引数に値を代入しても、関数呼び出し元に影響しません。
次のコードは、C/C++言語の値渡しの例です。変数`a`は`1`で初期化され、関数`change`に変数`a`の値が渡されます。関数`change`内で仮引数`n`に`2`を代入しても、呼び出し元の変数`a`は`1`のままです。

```c
#include <stdio.h>

void change(int n) {
n = 2;
void change(int n) { // 仮引数nは値型変数の値渡し
n = 2; // 仮引数に代入しても呼出し元に影響しない
}

int main() {
int a = 1;
change(a);
printf("%d", a); //=> 1
int a = 1; // 値型変数
change(a); // 実引数は値型変数a
printf("%d\n", a); //=> 1
}
```

## 参照渡し

参照渡しは、関数呼び出し元の変数が関数の処理でも共有されます。もし、関数の処理で引数に値を代入すると、関数呼び出し元の変数も変化します。次のコードはC言語の参照渡しの例です。変数`a`は関数`change`に参照渡しされます。`change`は引数に`2`を代入すると、`change`の呼び出し元の変数`a`の値も`2`になります。
参照渡しは、変数自体が渡されます。呼び出し元の変数を参照するようにして変数が共有されます(変数共有)。
実引数と仮引数は、同じ変数、同じ値になります。そのため、関数内で仮引数に値を代入すると、関数呼び出し元に影響します。
次のコードは、C++言語の参照渡しの例です(対比しやすいようにC言語の書き方に合わせています)。 変数`a`は`1`で初期化され、関数`change`に変数`a`自体が渡されます(呼び出し元の変数を参照・共有します)。関数`change`内で引数に`2`を代入すると、呼び出し元の変数`a`の値も`2`になります。

```c
#include <stdio.h>

void change(int *n) {
*n = 2;
void change(int &n) { // 仮引数nは値型変数の参照渡し
n = 2; // 仮引数に代入すると呼出し元に影響する
}

int main() {
int a = 1;
change(&a);
printf("%d", a); //=> 2
int a = 1; // 値型変数
change(a); // 実引数は値型変数a
printf("%d\n", a); //=> 2
}
```

## JavaScriptは値渡し

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

```js twoslash
function change(n) {
Expand All @@ -52,7 +78,9 @@ console.log(n);
// @log: 1
```

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

```js twoslash
const x = { n: 1 };
Expand All @@ -62,7 +90,7 @@ console.log(x);
// @log: { n: 2 }
```

ただし、`y`に別の値を代入した場合は、`x``y`は共通のオブジェクトを参照しなくなり、`y`への変更は`x`には影響しなくなります。
ただし、変数`y`に別の値を代入した場合は、変数`x`と変数`y`は同じオブジェクトを参照しなくなり、変数`y`のプロパティの変更は変数`x`には影響しなくなります。

```js twoslash
const x = { n: 1 };
Expand All @@ -73,7 +101,9 @@ console.log(x);
// @log: { n: 1 }
```

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

```js twoslash
function change(y) {
Expand All @@ -85,7 +115,7 @@ console.log(x);
// @log: { n: 2 }
```

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

```js twoslash
function change(y) {
Expand All @@ -102,8 +132,8 @@ console.log(x);

<PostILearned>

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