JavaScript 入門

JS 関数への引数渡しと値の参照

関数に引数を渡すことで、再利用可能かつ動的なコードを作成できます。引数を利用すれば、関数を呼び出すたびにその振る舞いをカスタマイズでき、関数をより汎用的で様々な状況に適応可能なものにできます。

本セクションでは、関数へ効果的に引数を渡す方法を検討し、プログラミングの核心的な原則をしっかりと理解することを目指します。

1. 実引数 (Arguments) と 仮引数 (Parameters) の理解

実引数 (Arguments) は、関数を呼び出す際に実際に渡す値です。
仮引数 (Parameters) は、関数定義時の括弧内にリストされている変数です。

仮引数は、将来渡される実引数のプレースホルダー(置き場所)として機能します。

比喩で考えてみましょう。自動販売機(関数)を想像してください。マシンにあるボタンが仮引数(例:「A1」「B2」「C3」)です。ボタンを押す(関数を呼び出す)とき、具体的な選択という実引数を提供します。すると、マシンはそのボタンに応じた商品を出力します。

// 引数を持つ関数の定義:
// name と greeting は仮引数 (Parameters)
function greet(name, greeting) {
  console.log(greeting + ", " + name + "!");
}

// 引数を持つ関数の呼び出し:
// "アリス" と "こんにちは" は1回目の呼び出しで渡される実引数 (Arguments)
greet("アリス", "こんにちは"); // 出力: こんにちは, アリス!

// "ボブ" と "おはよう" は2回目の呼び出しで渡される実引数 (Arguments)
greet("ボブ", "おはよう"); // 出力: おはよう, ボブ!

上の例では:

  • namegreetinggreet 関数の 仮引数 (Parameters) です。
  • "アリス" と "こんにちは" は、関数が最初に呼び出された時に渡された 実引数 (Arguments) です。
  • "ボブ" と "おはよう" は、再度呼び出された時に渡された 実引数 (Arguments) です。

2. 引数として様々なデータ型を渡す

JavaScript は動的型付け言語(Dynamic Typed Language)であるため、関数を定義する際に引数のデータ型を明示的に宣言する必要はありません。あらゆるデータ型を引数として渡すことが可能です。

function displayInfo(name, age, isStudent) {
  console.log("名前: " + name);
  console.log("年齢: " + age);
  console.log("学生かどうか: " + isStudent);
}

displayInfo("チャーリー", 20, true);
// 出力:
// 名前: チャーリー
// 年齢: 20
// 学生かどうか: true

displayInfo("ダナ", 25, false);
// 出力:
// 名前: ダナ
// 年齢: 25
// 学生かどうか: false

オブジェクトや配列も引数として渡すことができます。

function printAddress(address) {
  console.log("住所: " + address.street + ", " + address.city + ", " + address.country);
}

const myAddress = {
  street: "メインストリート 123号",
  city: "エニイシティ",
  country: "アメリカ"
};

printAddress(myAddress); // 出力: 住所: メインストリート 123号, エニイシティ, アメリカ

function sumArray(numbers) {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  return sum;
}

const numberList = [1, 2, 3, 4, 5];
const total = sumArray(numberList);
console.log("合計: " + total); // 出力: 合計: 15

3. 実引数の数 vs. 仮引数の数

JavaScript では、関数を呼び出す際、定義された仮引数の数よりも多い、あるいは少ない実引数を渡すことが許容されています。

引数が少なすぎる場合: 渡されなかった仮引数には undefined が割り当てられます。

function describePerson(name, age, occupation) {
  console.log("名前: " + name);
  console.log("年齢: " + age);
  console.log("職業: " + occupation);
}

describePerson("イヴ", 30);
// 出力:
// 名前: イヴ
// 年齢: 30
// 職業: undefined

引数が多すぎる場合: 定義より多く渡された実引数は、関数内部の arguments オブジェクトからアクセス可能です(ただし、これは古い機能であり、現在は後述する「レストパラメータ(Rest Parameters)」の使用が推奨されています)。

function displayFirstTwo(a, b) {
  console.log("1番目: " + a);
  console.log("2番目: " + b);
  console.log("引数の総数: " + arguments.length);
}

displayFirstTwo(1, 2, 3, 4, 5);
// 出力:
// 1番目: 1
// 2番目: 2
// 引数の総数: 5

4. 値渡し vs. 参照渡し

JavaScript では、引数のデータ型によって、関数への渡し方が 値渡し (Passing by Value)参照渡し (Passing by Reference) に分かれます。

4.1 値渡し (Passing by Value)

プリミティブ型(数値 number、文字列 string、真偽値 boolean、null、undefined など)は、値渡しで行われます。

これは、引数の値の「コピー」が作成されて関数に渡されることを意味します。関数内部で引数を変更しても、関数外部の元の変数には影響しません。

function changeValue(x) {
  x = 10;
  console.log("関数内部: x = " + x); // 出力: 関数内部: x = 10
}

let y = 5;
changeValue(y);
console.log("関数外部: y = " + y); // 出力: 関数外部: y = 5

この例では、changeValue 関数内で x の値を書き換えても、外部の y の値は変わらず保持されます。

4.2 参照渡し (Passing by Reference)

オブジェクト(配列や関数を含む)は、参照渡しで行われます。

これは、関数がオブジェクトそのもののコピーではなく、メモリ上の元のオブジェクトを指し示す「参照(ポインタ)」を受け取ることを意味します。そのため、関数内部でオブジェクトのプロパティを変更すると、関数外部の元のオブジェクトにも影響が及びます。

function changeObject(obj) {
  obj.name = "ボブ";
  console.log("関数内部: obj.name = " + obj.name); // 出力: 関数内部: obj.name = ボブ
}

let myObj = { name: "アリス" };
changeObject(myObj);
console.log("関数外部: myObj.name = " + myObj.name); // 出力: 関数外部: myObj.name = ボブ

この例では、changeObject 関数が myObj オブジェクトの name プロパティを書き換えています。オブジェクトは参照渡しであるため、この変更は外部にも反映されます。

重要な注意点: 関数内部でオブジェクト自体を再代入した場合(例:obj = { name: "チャーリー" };)、それは関数のスコープ内で新しいオブジェクトを作成したことになり、関数外部の元のオブジェクトは変化しません。

function reassignObject(obj) {
  obj = { name: "チャーリー" };
  console.log("関数内部: obj.name = " + obj.name); // 出力: 関数内部: obj.name = チャーリー
}

let myOtherObj = { name: "アリス" };
reassignObject(myOtherObj);
console.log("関数外部: myOtherObj.name = " + myOtherObj.name); // 出力: 関数外部: myOtherObj.name = アリス

5. 実践的な例

関数への引数渡しの実用的な例をいくつか見てみましょう。

5.1 長方形の面積を計算する

function calculateArea(length, width) {
  const area = length * width;
  return area;
}

let rectangleLength = 10;
let rectangleWidth = 5;
let area = calculateArea(rectangleLength, rectangleWidth);
console.log("長方形の面積は: " + area); // 出力: 長方形の面積は: 50

5.2 名前のフォーマット

function formatName(firstName, lastName, format) {
  if (format === "FL") {
    return firstName + " " + lastName;
  } else if (format === "LF") {
    return lastName + ", " + firstName;
  } else {
    return "無効な形式";
  }
}

let formattedName1 = formatName("John", "Doe", "FL");
console.log(formattedName1); // 出力: John Doe

let formattedName2 = formatName("Jane", "Smith", "LF");
console.log(formattedName2); // 出力: Smith, Jane

let formattedName3 = formatName("Peter", "Jones", "XYZ");
console.log(formattedName3); // 出力: 無効な形式

5.3 在庫の更新

Eコマースアプリを構築していると仮定します。製品の在庫を更新する関数を作成できます。

let inventory = {
  "ProductA": 10,
  "ProductB": 5,
  "ProductC": 20
};

function updateInventory(productName, quantityChange) {
  if (inventory.hasOwnProperty(productName)) {
    inventory[productName] += quantityChange;
  } else {
    console.log("在庫内にその製品は見つかりませんでした。");
  }
}

updateInventory("ProductA", -3); // ProductA を3つ販売
console.log(inventory["ProductA"]); // 出力: 7

updateInventory("ProductC", 5); // ProductC を5つ補充
console.log(inventory["ProductC"]); // 出力: 25

updateInventory("ProductD", 10); // 存在しない製品の更新を試行
// 出力: 在庫内にその製品は見つかりませんでした。

この例では、製品名(文字列)と数量の変化(数値)を引数として updateInventory 関数に渡しています。この関数は inventory オブジェクトを直接変更します。これはオブジェクトが参照渡しされる性質を利用しています。