4.4 配列と引数の渡し方

(1/1)
陽奈が空中に浮かぶ配列を操作するイメージ
空中に浮かぶ配列を操作する陽奈(AI生成)
AI生成コンテンツ / AI-generated content
JavaScriptでは、関数の引数として配列を渡すことができる。数値と配列では、関数内で変更したときの見え方が異なる。

基本は値渡しである。配列の場合は、配列を参照する値が渡され、呼び出し元と関数が同じ配列を共有する。要素の変更と、変数への再代入を区別することが大切である。

(2026年9月16日)説明とサンプルを訂正し、インデックスのコラムとイラストを更新した。

目次

サンプル・プログラム

平均を求める

合計点・平均点
10人の点数と合計点・平均点
関数の引数として配列を渡すこともできる。
例として、テストの平均点を求めるプログラム "average1.html" を作ってみよう。
左図がプログラムの実行例である。

average1.html

   1: <!--
   2: /** average1.html
   3:  *  プログラミング入門 - 4.4 配列と引数の渡し方
   4:  *  合計点・平均点を求める.
   5:  *
   6:  * @copyright   (c)studio pahoo
   7:  * @author      パパぱふぅ
   8:  * @動作環境    JavaScript ES6(ES2015)
   9:  * @参考URL     https://www.pahoo.org/e-soul/webtech/js00/js00-04-04.html
  10: */
  11: -->
  12: <!DOCTYPE html>
  13: <html lang="ja">
  14: <head>
  15: <meta charset="UTF-8">
  16: <title></title>
  17: <script>
  18: // 初期値 ==================================================================
  19: const TITLE = '合計点・平均点';         //プログラム・タイトル
  20: const REFERENCE = 'https://www.pahoo.org/e-soul/webtech/js00/js00-04-04.html';
  21:                                         //参照サイト
  22: const WIDTH = 300;                      //表示幅(ピクセル)
  23: 
  24: // ユーザー定義関数 ========================================================
  25: /**
  26:  * ファイル更新日を返す
  27:  * @param   なし
  28:  * @return  String yyyy/mm/dd
  29: */
  30: function getLastModified() {
  31:     let now = new Date(document.lastModified);
  32: 
  33:     return now.getFullYear() + '/' + (now.getMonth() + 1) + '/' + now.getDate();
  34: }
  35: 
  36: /**
  37:  * 点数表を作成し,画面に表示する.
  38:  * @param   Array scores 点数表(0~100の数値を1個以上含む配列)
  39:  * @return  なし
  40: */
  41: function dispScoreTable(scores) {
  42:     const error = document.getElementById('error');
  43:     const table = document.getElementById('scoreTable');
  44:     error.textContent = '';
  45:     table.innerHTML = '';
  46: 
  47:     // 0~100点の数値が1個以上入った配列だけを受け付ける.
  48:     if (!Array.isArray(scores) || scores.length === 0) {
  49:         error.textContent = 'エラー:正しい点数表を用意してください.';
  50:         return;
  51:     }
  52:     let sum = 0;
  53:     for (let i = 0; i < scores.length; i++) {
  54:         const score = scores[i];
  55:         if (!Number.isFinite(score) || score < 0 || score > 100) {
  56:             error.textContent = 'エラー:点数には0~100の数値を指定してください.';
  57:             return;
  58:         }
  59:         sum += score;
  60:     }
  61: 
  62:     let html = '<tr><th>出席番号</th><th>点数</th></tr>';
  63:     for (let i = 0; i < scores.length; i++) {
  64:         html += `<tr><td>${i + 1}</td><td>${scores[i]}</td></tr>`;
  65:     }
  66:     html += `<tr><td>合計点</td><td>${sum}</td></tr>
  67: <tr><td>平均点</td><td>${sum / scores.length}</td></tr>`;
  68:     table.innerHTML = html;
  69: }
  70: 
  71: // メイン・プログラム ======================================================
  72: window.onload = function() {
  73:     //タイトル等をセット
  74:     document.title = TITLE;
  75:     document.getElementById('title').innerHTML = TITLE + '&nbsp;<span style="font-size:small;">' + getLastModified() + '版</span>';
  76:     document.getElementById('reference').innerHTML =`
  77: ※参考サイト&nbsp;<a href="${REFERENCE}">${REFERENCE}</a>
  78: `;
  79: 
  80:     //スタイルシートをセット
  81:     document.getElementById('help').style.width = WIDTH + 'px';
  82:     document.getElementById('scoreTable').style.width = WIDTH + 'px';
  83: 
  84:     //点数表
  85:     let scores = [75, 68, 81, 90, 57, 52, 96, 72, 68, 72];

まず、メイン・プログラム側で、10人のテストの点数を配列 scores に代入しておく。
この配列をユーザー定義関数 dispScoreTable に渡し、平均点を求めるとともに、点数一覧表を作成する。

average1.html

  36: /**
  37:  * 点数表を作成し,画面に表示する.
  38:  * @param   Array scores 点数表(0~100の数値を1個以上含む配列)
  39:  * @return  なし
  40: */
  41: function dispScoreTable(scores) {
  42:     const error = document.getElementById('error');
  43:     const table = document.getElementById('scoreTable');
  44:     error.textContent = '';
  45:     table.innerHTML = '';
  46: 
  47:     // 0~100点の数値が1個以上入った配列だけを受け付ける.
  48:     if (!Array.isArray(scores) || scores.length === 0) {
  49:         error.textContent = 'エラー:正しい点数表を用意してください.';
  50:         return;
  51:     }
  52:     let sum = 0;
  53:     for (let i = 0; i < scores.length; i++) {
  54:         const score = scores[i];
  55:         if (!Number.isFinite(score) || score < 0 || score > 100) {
  56:             error.textContent = 'エラー:点数には0~100の数値を指定してください.';
  57:             return;
  58:         }
  59:         sum += score;
  60:     }
  61: 
  62:     let html = '<tr><th>出席番号</th><th>点数</th></tr>';
  63:     for (let i = 0; i < scores.length; i++) {
  64:         html += `<tr><td>${i + 1}</td><td>${scores[i]}</td></tr>`;
  65:     }
  66:     html += `<tr><td>合計点</td><td>${sum}</td></tr>
  67: <tr><td>平均点</td><td>${sum / scores.length}</td></tr>`;
  68:     table.innerHTML = html;
  69: }

引数として受け取った配列の要素は、scores[i] のように取り出す。i は要素の位置を表すインデックス(添字)で、最初は0である。10人分なら0~9を使う。
length は配列の長さを表す。この例のように途中が抜けていない配列では、要素の個数と一致する。
forループで合計点を求め、人数で割って平均点を計算し、HTMLの table要素で点数表を表示する。合計は731点、平均は73.1点である。
このサンプルは0~100点の数値を1個以上含む配列を受け付ける。空配列や数値以外の値、範囲外の点数はエラーとして表示する。

数値を渡す:値渡し

さて、関数に渡された引数を、関数内で変更したらどうなるだろうか。

parameter1.html

  19: <script>
  20: // ユーザー定義関数 ========================================================
  21: /**
  22:  * 引数に+1する(その1)
  23:  * @param   Number a
  24:  * @return  Number a + 1
  25: */
  26: function plus1(a) {
  27:     let b = a + 1;
  28:     return b;
  29: }
  30: 
  31: /**
  32:  * 引数に+1する(その2)
  33:  * @param   Number a
  34:  * @return  Number a + 1
  35: */
  36: function plus2(a) {
  37:     a = a + 1;
  38:     document.getElementById('var3').innerHTML = 'a = ' + a.toString();
  39:     return a;
  40: }
  41: 
  42: // メイン・プログラム ======================================================
  43: window.onload = function() {
  44:     let a = 1;
  45:     document.getElementById('var1').innerHTML = 'A = ' + a.toString();
  46:     plus1(a);
  47:     document.getElementById('var2').innerHTML = 'A = ' + a.toString();
  48:     plus2(a);
  49:     document.getElementById('var4').innerHTML = 'A = ' + a.toString();
  50: }
  51: </script>

ユーザー定義関数 plus1 は、引数 a に1を足した値をローカル変数 b に代入し、戻り値として返す。呼び出し元では戻り値を受け取っていないため、元の変数は変わらない。実行結果は次のとおりである。
A = 1
A = 1
a = 2
A = 1
表示の大文字 A は呼び出し元の値、小文字 a は関数内の値を区別するためのラベルである。プログラム中の変数名はいずれも小文字の a である。

ユーザー定義関数 plus2 は、関数内の引数 a に1を足して再代入する。関数内では2になるが、呼び出し元の変数 a は plus2 の実行後も1のままである。

数値の引数には、その値が渡される。関数内で引数に再代入しても、呼び出し元の変数は書き換わらない。これが値渡しである。結果を呼び出し元で使うには、戻り値を受け取り、たとえば a = plus1(a) と代入する。

配列を渡す:同じ配列を共有する

配列を渡す場合も値渡しだが、渡されるのは同じ配列を参照する値である。配列全体のコピーが自動で作られるわけではない。プログラム "parameter2.html" の実行結果は次のとおりである。
A = 1,2,3,4,5
A = 1,2,3,4,5
a = 1,2,3,5,5
A = 1,2,3,5,5

parameter2.html

  19: <script>
  20: // ユーザー定義関数 ========================================================
  21: /**
  22:  * 配列引数に+1する(その1)
  23:  * @param   Array  a 配列
  24:  * @param   Number n 要素番号
  25:  * @return  Number a[n] + 1
  26: */
  27: function plus3(a, n) {
  28:     let b = a[n] + 1;
  29:     return b;
  30: }
  31: 
  32: /**
  33:  * 配列引数に+1する(その2)
  34:  * @param   Array  a 配列
  35:  * @param   Number n 要素番号
  36:  * @return  Number a[n] + 1
  37: */
  38: function plus4(a, n) {
  39:     a[n] = a[n] + 1;
  40:     document.getElementById('var4').innerHTML = 'a = ' + a.toString();
  41:     return a[n];
  42: }
  43: 
  44: // メイン・プログラム ======================================================
  45: window.onload = function() {
  46:     let a = [1, 2, 3, 4, 5];
  47:     let n = 3;
  48:     document.getElementById('var1').innerHTML = 'A = ' + a.toString();
  49:     plus3(a, n);
  50:     document.getElementById('var3').innerHTML = 'A = ' + a.toString();
  51:     plus4(a, n);
  52:     document.getElementById('var5').innerHTML = 'A = ' + a.toString();
  53: }
  54: </script>

ユーザー定義関数 plus3 は、配列 a のインデックス n の要素に1を足し、その結果をローカル変数 b に入れて返す。元の配列の要素には代入しないので、呼び出し元の配列は変わらない。

ユーザー定義関数 plus4 は、a[n] = a[n] + 1 として、共有している配列の要素を書き換える。n = 3 なので、変更するのは先頭から4個目の要素である。4が5になり、呼び出し元からも変更が見える。

このように、同じオブジェクトを共有する引数の渡し方を共有渡しと呼ぶこともある。「参照渡し」と説明されることもあるが、呼び出し元の変数そのものを関数内から再代入できる方式とは異なる。配列の要素を書き換えることと、別の配列を引数に再代入することは区別しよう。

配列をシャッフルする

トランプのようなカードゲームでは、カードを配列に入れてから順番をシャッフルする。関数と呼び出し元が同じ配列を共有する性質を使えば、渡された配列の順番を直接入れ替えられる。

shuffle.html

  36: /**
  37:  * 指定した配列をシャフルする.
  38:  * @param   Array arr シャフルしたい配列
  39:  * @return  なし
  40: */
  41: function shuffle(arr) {
  42:     for (let i = arr.length; i > 1; i--) {
  43:         let k = Math.floor(Math.random() * i);
  44:         [arr[k], arr[i - 1]] = [arr[i - 1], arr[k]];
  45:     }
  46: }

配列をシャッフルする
配列の2つの要素を交換する例
このサンプルはフィッシャー–イェーツ法である。未確定の範囲から1要素をランダムに選び、その範囲の末尾と交換する。末尾を確定して範囲を1つ縮め、残り1要素になるまで繰り返す。長さN(1以上)なら交換処理はN−1回である。
Math.random() は0以上1未満の数を返す。未確定の要素数 i を掛け、Math.floor() で切り捨てると、0~i−1のインデックスになる。各段階で均等に選べれば、すべての並び方が同じ確率になる。
shuffle は元の配列を変更する。元の順番を残したいときは const copy = cards.slice() でコピーを作り、shuffle(copy) とする。このコピーは浅いコピーであり、要素がオブジェクトなら、そのオブジェクトまでは複製されない。

読みやすいプログラム:コメント

ノートPCの内側の液晶画面に並ぶ意味不明の文字を見て困る紺乃
意味不明の文字が並ぶノートPCの画面を見て困る紺乃(AI生成)
AI生成コンテンツ / AI-generated content
JavaScriptにおけるコメントの書き方は、「2.10 演算の優先順位、コメント - コメント」で紹介した。

コメントは、プログラムの意図を読み手に伝えるために書く。適切な変数名や関数名を付けたうえで、コードだけでは分かりにくい理由や前提を補うとよい。
JavaScriptもC言語も高級言語であり、仕様書をそのまま書けばプログラムになるわけではない。
読みやすいコメントの目安は、何をしているかだけでなく、なぜそうするかを伝えることである。コードをそのまま言い換えるだけのコメントは減らし、初心者が迷う箇所には説明を加える。コードを変更したときは、コメントも見直して食い違いを防ごう。

●コメントに記載する内容
  1. プログラム使用上の注意、制約条件など。
  2. プログラム使用にあたって準備すること、実施することなど。
  3. 参照しているライブラリ、フレームワーク、APIなどの情報。
  4. プログラムの著作権情報。
  5. コメントで補足しないと分からないような複雑な箇所。
●コメントに記載する必要がないもの
  1. 名前から意味が明らかな変数やプロパティへの重複した解説。ただし、単位や制約などは補足する。
  2. プログラムを読めば分かる内容。ただし、読み手のスキルに応じてコメントを付加すること。
  3. プログラムの更新履歴。ただし、バージョン管理システムを利用していない場合はコメントとして記述すること。
本連載では、初心者を対象にしている関係で、プログラムを読めば分かる内容でもコメントを記載している。また、Git などのバージョン管理システムを使っていないので、ファイルの末尾に更新履歴を付け加えている。 本連載では、クラスや関数の冒頭に、次のような書式で解説を付けている。
/**
* (1行目)機能概要【必須】
* (2行目)機能詳細,制約事項など【省略可能】
* @param{空白}変数の型{空白}変数名{空白}内容‥‥引数1の説明【省略可能】
* @param{空白}変数の型{空白}変数名{空白}内容‥‥引数2の説明【省略可能】
* (1行に1引数を記述する.)
* @return{空白}変数の型‥‥戻り値の説明【省略可能】
*/
たとえば、次のようなコメントである。
/**
* 点数表を作成し,画面に表示する.
* @param Array scores 点数表
* @return なし
*/
コメントからJavaScriptのドキュメントを自動作成する JSDoc に近い書式であるが、他言語でも利用できるよう汎用性を持たせており、当サイトの構築ツール(非公開)を使って下記のような一覧表を作成できるようになっている。
average1.html 関数/メソッド一覧
関数/メソッド 機能 詳細
getLastModified ファイル更新日を返す
dispScoreTable 点数表を作成し,画面に表示する.

読みやすいプログラム:引数の変更は意図を明確に

紺乃が陽奈にプレゼントを手渡すイラスト
紺乃から陽奈へプレゼントを手渡す(AI生成)
AI生成コンテンツ / AI-generated content
関数に渡したデータが予想外に変わると、不具合の原因を探しにくくなる。元の配列を変更する関数なのか、新しい配列を返す関数なのかを明確にすることが大切である。必要がなければ、引数をその場で変更せず、結果を戻り値で返すとよい。
ただし、引数の変更が常に悪いわけではない。shuffle のように元の配列を並べ替える関数も役立つ。その場合は「元の配列を変更する」と説明に明記し、呼び出す側が分かるようにしておく。
また、a = 2 のような代入について「必ず別のメモリ領域を確保する」と考える必要はない。ここでは、JavaScriptの決まりとして観察できる動作を理解しよう。

コラム:完全数を求める

レオンハルト・オイラー
レオンハルト・オイラー
自分自身を除く正の約数の和が、元の数と等しくなる正の整数を完全数(perfect number)と呼ぶ。
6の約数は1、2、3、6である。自分自身を除くと1+2+3=6となるので、6は完全数である。小さいものから6、28、496、8128が続く。
まずは候補の整数ごとに約数の和を調べてみよう。ただし、完全数を求めるには1個ずつ調べる方法しかないわけではない。後半では、偶数の完全数を効率よく求める定理を使う。
約数の和から判定するサンプルが "perfectNumbers1.html" である。探索範囲は、冒頭の定数 RANGE_MIN と RANGE_MAX で指定し、両端を含めて調べる。
約数は組で求められる。28なら2と14、4と7が組になるので、候補の平方根まで調べればよい。平方数の平方根は同じ約数を2回足さないようにする。元のサンプルにあった「1から候補の直前まで調べる」処理は、この方法に改めた。

perfectNumbers1.html

  23: const RANGE_MIN = 2;                    //探索範囲の最小値
  24: const RANGE_MAX = 100000;               //探索範囲の最大値

perfectNumbers1.html

  38: /**
  39:  * 完全数かどうかを判定する.
  40:  * @param   Number num 判定する値
  41:  * @return  Boolean true:完全数である / false:完全数ではない
  42: */
  43: function isPerfectNumber(num) {
  44:     if (!Number.isSafeInteger(num) || num < 2) return false;
  45:     let sum = 1;
  46: 
  47:     // 約数を組で探す.たとえば28なら2と14,4と7が組になる.
  48:     for (let i = 2; i <= Math.sqrt(num); i++) {
  49:         if (num % i === 0) {
  50:             sum += i;
  51:             const pair = num / i;
  52:             if (pair !== i) sum += pair;
  53:         }
  54:         if (sum > num) return false;
  55:     }
  56:     return sum === num;
  57: }

perfectNumbers1.html

  59: /**
  60:  * 完全数を求める.
  61:  * @param   Number min 探索範囲の最小値
  62:  * @param   Number max 探索範囲の最大値
  63:  * @return  Array: 完全数の配列
  64: */
  65: function findPerfectNumbers(min, max) {
  66:     // この入門用サンプルは安全な整数の範囲だけを扱う.
  67:     if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
  68:         throw new RangeError('探索範囲には安全な整数を指定してください.');
  69:     }
  70:     if (min > max) return [];
  71:     let perfectNumbers = [];        // 完全数を格納する配列
  72: 
  73:     for (let i = Math.max(2, min); i <= max; i++) {
  74:         if (isPerfectNumber(i)) {
  75:             perfectNumbers.push(i);
  76:         }
  77:     }
  78: 
  79:     return perfectNumbers;
  80: }

perfectNumbers1.html

  95:     // 完全数を求める
  96:     console.time('計算時間');           // 計算時間の計測開始
  97:     const perfectNumbers = findPerfectNumbers(RANGE_MIN, RANGE_MAX);
  98:     let ss = '計算範囲:' + RANGE_MIN.toLocaleString() + '~' + RANGE_MAX.toLocaleString() + '<br><br>';
  99:     console.timeEnd('計算時間');        // 計算時間をconsoleに表示する
 100: 
 101:     // 完全数を表示する
 102:     for (let i = 0; i < perfectNumbers.length; i++) {
 103:         ss += perfectNumbers[i].toLocaleString() + '<br>';
 104:     }
 105:     document.getElementById('results').innerHTML = ss;

メイン・プログラムでは console.timeメソッドと console.timeEndメソッドを使って計算時間をコンソールに出力するようにした。

10万以下の完全数は6、28、496、8128の4個であり、5個目は33550336である。平方根までの約数探索にしても、探索範囲を広げれば時間がかかり、その間は画面操作が応答しにくくなる。
2026年(令和8年)9月16日に確認した GIMPSの一覧には、既知のメルセンヌ素数が52個載っている。それぞれから偶数の完全数が1個得られる。最大のものの指数は136279841である。メルセンヌ素数の桁数と、それから作る完全数の桁数は異なる。
ユークリッド・オイラーの定理を使うと、偶数の完全数を効率よく求められる。ユークリッドは次の形で完全数が得られることを示し、レオンハルト・オイラーは偶数の完全数がすべてこの形になることを証明した。
$$ P = 2^{p - 1} \times (2^p - 1) $$
整数 $ p \geq 2 $ に対し、$ 2^p - 1 $ が素数(メルセンヌ素数)なら、$ P $ は偶数の完全数になる。このとき $ p $ も素数である。ただし、$ p $ が素数でも、$ 2^p - 1 $ が素数になるとは限らない。

perfectNumbers2.html

  24: const RANGE_MIN = 2;                    //探索範囲の最小値
  25: const RANGE_MAX = 100000;               //探索範囲の最大値

perfectNumbers2.html

  39: /**
  40:  * 素数かどうかを判定する.
  41:  * @param   Number num 判定する値
  42:  * @return  Boolean True:素数である / False:素数ではない
  43: */
  44: function isPrime(num) {
  45:     if (!Number.isSafeInteger(num) || num < 2) {
  46:         return false;
  47:     }
  48:     for (let i = 2; i <= Math.sqrt(num); i++) {
  49:         if (num % i === 0) {
  50:             return false;
  51:         }
  52:     }
  53:     return true;
  54: }

perfectNumbers2.html

  56: /**
  57:  * メルセンヌ素数かどうかを判定する.
  58:  * @param   Number p 2の指数(2~53の整数)
  59:  * @return  Boolean True:メルセンヌ素数である / False:メルセンヌ素数ではない
  60: */
  61: function isMersennePrime(p) {
  62:     if (!Number.isSafeInteger(p) || p < 2 || p > 53) return false;
  63:     let mersenneNumber = Math.pow(2, p) - 1;
  64: 
  65:     return isPrime(mersenneNumber);
  66: }

perfectNumbers2.html

  68: /**
  69:  * 指定範囲の偶数の完全数を求める.
  70:  * @param   Number min 探索範囲の最小値
  71:  * @param   Number max 探索範囲の最大値
  72:  * @return  Array 偶数の完全数の配列
  73: */
  74: function findPerfectNumbers(min, max) {
  75:     // この入門用サンプルは安全な整数の範囲だけを扱う.
  76:     if (!Number.isSafeInteger(min) || !Number.isSafeInteger(max)) {
  77:         throw new RangeError('探索範囲には安全な整数を指定してください.');
  78:     }
  79:     if (min > max) return [];
  80:     const perfectNumbers = [];
  81:     for (let p = 2; ; p++) {
  82:         const num = Math.pow(2, p - 1) * (Math.pow(2, p) - 1);
  83:         // 素数判定より先に範囲を確認する.
  84:         if (!Number.isSafeInteger(num) || num > max) break;
  85:         if (num >= min && isMersennePrime(p)) {
  86:             perfectNumbers.push(num);
  87:         }
  88:     }
  89:     return perfectNumbers;
  90: }

perfectNumbers2.html

 105:     // 完全数を求める
 106:     console.time('計算時間');           // 計算時間の計測開始
 107:     const perfectNumbers = findPerfectNumbers(RANGE_MIN, RANGE_MAX);
 108:     let ss = '計算範囲:' + RANGE_MIN.toLocaleString() + '~' + RANGE_MAX.toLocaleString() + '<br><br>';
 109:     console.timeEnd('計算時間');        // 計算時間をconsoleに表示する
 110: 
 111:     // 完全数を表示する
 112:     for (let i = 0; i < perfectNumbers.length; i++) {
 113:         ss += perfectNumbers[i].toLocaleString() + '<br>';
 114:     }
 115:     document.getElementById('results').innerHTML = ss;

"perfectNumbers2.html" は、この式で候補を作り、範囲内に入るものを調べる。指数 $ p $ が増えると候補の完全数も増えるため、上限を超えた時点で終了できる。10万以下では "perfectNumbers1.html" と同じ4個を返すが、こちらが探すのは偶数の完全数だけである。
偶数の完全数が無限に存在するかは未解決である。奇数の完全数は、1個でも存在するのかどうかが分かっていない。

2つのサンプルは、JavaScriptの Number型で安全に扱える整数の範囲を対象とする。探索範囲には Number.isSafeInteger() が trueになる整数を指定する。巨大な完全数を求める研究用のプログラムではない。
このように、数学的な性質を利用すると計算を減らせる場合がある。速さだけでなく、どの範囲の答えを求められる方法なのかも確認しよう。

コラム:値渡しと参照の共有

JavaScriptでは引数は値として渡される。配列などのオブジェクトの場合も、参照する値が渡される。
次の例で、配列の要素を変更する場合と、引数に別の配列を再代入する場合を比べよう。
function change(items) {
    items[0] = 9;
    items = [100, 200];
}
const original = [1, 2];
change(original);
console.log(original);    // [9, 2]
最初の代入は共有している配列の要素を変える。次の代入は、関数内の items が参照する先を変えるだけなので、呼び出し元の original は別の配列に置き換わらない。

ここで、const で宣言した変数がオブジェクトを参照している場合も確認しよう。

constObject.html

  20: <script>
  21: //ページのロード時に実行
  22: window.onload = function() {
  23:     // constで宣言したオブジェクト
  24:     const obj = {
  25:         a: 1,
  26:         b: 2,
  27:         c: 3
  28:     };
  29:     document.getElementById('obj').innerHTML = JSON.stringify(obj);
  30:     document.getElementById('objA1').innerHTML = obj.a;
  31:     // プロパティに代入
  32:     obj.a = 9;
  33:     document.getElementById('objA2').innerHTML = obj.a;
  34:     // 変数への再代入は失敗する.学習用に例外を捕まえて表示する.
  35:     try {
  36:         obj = { a: 0 };
  37:     } catch (error) {
  38:         document.getElementById('reassign').textContent =
  39:             'objへの再代入:' + error.name;
  40:     }
  41: }
  42: </script>

const は変数への再代入を禁止するが、参照しているオブジェクトの中身を固定するものではない。サンプルでは obj.a を1から9に変更できる。一方、obj = { a: 0 } と変数に再代入すると TypeErrorになる。サンプルではエラーを捕まえて画面に表示する。
この性質は引数の渡し方とは別の話であり、constで宣言した配列にも同じ考え方が当てはまる。

コラム:インデックスはなぜ0からはじまるのか

3個の箱が並んでいるとしよう。最初の箱は先頭から0個分、次は1個分、最後は2個分ずれている。この先頭からのずれ(オフセット)として考えると、最初のインデックスが0でも不思議ではない。

C言語のように同じ大きさの要素を連続したメモリに置く配列では、「先頭の住所+インデックス×1要素の大きさ」で位置を表せる。先頭なら加える分は0である。ただし、JavaScriptの Arrayが必ずこの形でメモリに並ぶ、という意味ではない。

0から始めると、長さNの配列を調べる範囲は「0以上N未満」になる。たとえば10個なら0~9であり、forループも次のように書ける。
for (let i = 0; i < scores.length; i++) {
    console.log(scores[i]);
}
途中の範囲も「2以上5未満」なら2、3、4の3個であり、端の差5−2で個数が分かる。ダイクストラも、このような区間の扱いやすさを論じた。

0始まりは言語の設計上の選択であり、CPUが強制しているわけではない。1始まりを採用する言語もある。「1始まりだと毎回引き算をするので必ず遅い」とも言えない。

参考サイト

(この項おわり)
header