JavaScriptにおいて、関数や、オブジェクトのメソッドに引数を渡す際の扱い(Evaluation strategy)についてまとめておきます。
JavaScriptでは、関数呼び出し時に実引数のリファレンスが仮引数にコピーされて渡されと考えるとよいでしょう。この方式を「共有渡し」(call by sharing)あるいは「参照の値渡し」(call by value where value is the reference copy)などと呼びます。
他の言語でいうところの「値渡し」(call by value)や、「参照渡し」(call by reference)といった扱い方とは異なる点に注意してください。
イミュータブルな引数を渡す場合
数値や文字列(あるいはStringオブジェクト)のようなイミュータブルな値を引数として渡す場合、関数内で、引数の変数に別の値を再代入しても、呼び出しもとには反映されません。
次の例を見てみましょう。
function myFunc1(n) {
n = n + 10; // (1)
}
var num = 10;
myFunc1(num);
console.log(num); // (2) 10
この例では、関数myFunc()を呼び出す際に、数値が代入された変数numを引数として渡しています。こうすると実引数numのリファレンスが仮引数nのリファレンスにコピーされます(、同じ実体を指し示しています)。(1)で仮引数の変数nに10を足して、再び変数nに代入しています。こうすると関数内の変数nのリファレンスは新たに生成された数値を指し示すようになります。したがって、実引数のnumと仮引数のnは異なる実体を指し示すようになります。
myFuncを呼び出して(2)で変数numの値を表示すると、関数myFunc()の呼び出し前の値と同じ「10」が表示されます。つまり、関数内での変数への再代入は反映されていません。
ミュータブルな引数を渡す場合(変更が呼び出しもとに反映されないケース)
ミュータブルな値を引数として渡す場合も考え方同様です。関数内部で、引数として渡された変数に値を再代入して、リファレンス、つまり参照先を変更してしまった場合には、呼び出しもとの引数の値は変更されません。それぞれが異なる実体を指し示すようになるからです。次の例では、日付時刻を管理するDateオブジェクトのインスタンスを関数myFunc3()の引数に渡しています。Dateオブジェクトはミュータブルな型です。
function myFunc3(day) {
day = new Date() // (1)
}
var theDay = new Date(2016, 10, 1);
myFunc3(theDay);
console.log(theDay); // (2) Tue Nov 01 2016 〜
この場合 、(1)で新たなDateオブジェクトを生成し、それを引数の変数dayに代入していますのでリファレンスが変更されます。
関数呼び出し後に、(2)で引数として渡した変数theDayの値を表示すると結果はもとのままです。
ミュータブルな引数を渡す場合(変更が呼び出しもとに反映されるケース)
ただし、関数内で引数の変数に値を再代入せずに、変数の内部の値を変更するようなメソッドを実行したり、あるいはプロパティを変更すると、呼び出しもとの変数に変更が反映されます。
次の例はDateオブジェクトを関数myFunc2()の引数に渡し、内部の(1)でsetFullYear()メソッドで年を変更しています。
function myFunc2(day) {
day.setFullYear(1950); // (1)
}
var theDay = new Date(2016, 10, 1);
myFunc2(theDay);
console.log(theDay); // (2) Wed Nov 01 1950 〜
この場合、実引数theDayと仮引数dayのリファレンスは同じ実体を指し示すため、関数内でのsetFullYear()メソッドによる年の変更が変更が反映されれるわけです。(2)では年が「1950」になった値が表示されます。