スタートページJavascript関数への引数渡し

関数への配列引数と配列戻り値


例0:スカラー変数での受け渡し

とりあえず、次のソースコードを見てください。表示される入力と出力の値はどうなるでしょうか?
 常識的(?)に、どちらも「100、1100」になると思うでしょう。では[実行]をクリックしてください。

    function func0(x, y) {
        x = x + 100;
        y = x + 1000;
        alert("x=" + x + ",y=" + y);
    }

    function main0() {
        var a = 0;
        var b;
        func0(a, b);
        alert("a=" + a + ",b=" + b);
    }

関数側の「x=100, y=1100」は思った通りですが、メイン側は「a=0, b=undefind」になりました。関数を呼び出す前と同じです。すなわち、、引数がスカラーの場合には、関数への渡しはできるが、関数からの戻しはできないのです。

ところが、下の例1のように、入力や出力が配列になると、うまく出力配列として戻しができるのです。また、入力配列が関数により壊されてしまうこともあります。
 これは、Javascriptが、引数の引き渡し規則がスカラーと配列では異なることに起因しています。その説明は「値渡し」や「参照渡し」など面倒なことになるので省略しますが、スカラーの場合は 関数0(10); のように、実引数をリテラルにすることができまるのに対して、配列の場合では、関数1( [10,20,30] ); のようなリテラルは使えず、必ず変数でなければならないという違いがあることが理由の一つです。

例1:一次元配列全体の受け渡し

次のプログラムでは、main1の行Cで配列aに値を与え、func1の行Aでxすなわちaの値を変更し、さらに行Bで配列yに値を与え、main1では配列yを配列bとして受け取ります。

    function func1(x, y) {
        for (var i = 0; i<x.length; i++) {
            x[i] = x[i] + 100;            // A:x[0]=100, x[1]=101, x[2]=102 になる
        }
        y[0] = x[0] + 1000;               // B:y{0} = 1100 になる
        y[1] = x[1] + 1000;               //   y{1} = 1101 になる
         // あえて i=2 については行わない        y{2} は存在しない
    }

    function main1() {
        var a = [0, 1, 2];                // C:a[0]=0, a[1]=1, a[2]=2
        var b = new Array();              // D:bもメイン側で定義する

        func1(a, b);                      // E:入力実引数配列、出力実引数配列に値が入る

        document.getElementById("output1").innerHTML
            = "a[0]=" + a[0] + ", b[0]=" + b[0] + "<br>"
            + "a[1]=" + a[1] + ", b[1]=" + b[1] + "<br>"
            + "a[2]=" + a[2] + ", b[2]=" + b[2];
    }

このように、意図した結果が得られました。メイン側と関数側で引数の受け渡しが正しく成立しています。
 しかし、次の事項に留意する必要があります。

部分配列の受け渡しはうまくいかない

関数に配列の全体を渡すのではなく、配列[i]~配列[k]の部分だけを渡して、関数側では配列[0]~配列[k-i]として処理を記述したいことがあります。
 例えば、配列aの a[i]~a{j] を抜き出した a.slice(i, j-1) を実引数として、
    関数( a.slice(i, j-1) );
としたとき、関数側では受け取れますが、関数側で a[k] を変更してもメイン側へ戻せないのです、これは例0でのスカラー引数と同じ現象です。
    関数(a, i, j);
として、配列全体を渡して、i, jを用いて関数内で処理をすることになります。

例2:二次元配列全体の受け渡し

二次元配列を引数とする場合も「例1」と同じです。ここでは、次のような処理をします(-は未定義)。
  元の入力配列(a)   関数内でaを変える  さらに配列yを作成して戻しbとする
   (Fで設定)     (Dで計算)        (Bで設定、Eで計算)    0  1  2  100 101 102   200 201 202
  10 11  -  110 111  -    210 211  -

    function func2(x, y) {
        var isize = x.length;                   // A:x(すなわち配列a)の行数
        for (i = 0; i < isize; i++) {
            y[i] = new Array();                 // B:ここで出力配列が二次元配列であると設定
            var jsize = x[i].length;            // C:各行の列数
            for (var j = 0; j < jsize; j++) {
                x[i][j] = x[i][j] + 100;        // D:配列[i][j]の変更
                y[i][j] = x[i][j] + 100;        // E:出力配列yの作成
            }
        }
    }

    function main2() {
        var a = new Array();                    // F:入力配列 a[i][j]の設定
        a[0] = [ 0,  1,  2];
        a[1] = [10, 11];
        var b = new Array();                    // G:出力配列 b の設定 二次元であることは明示しない

        func2(a, b);                            // H:関数呼び出し。ここで出力bにyが入る

        var msg = "";                           // 以下は単なる表示処理
        for (var i = 0; i < a.length; i++) {
            for (var j = 0; j <a[i].length; j++) {
                msg += " a[" + i + "][" + j + "] =" + a[i][j];
            }
            msg += "<br>";
        }
        for (i = 0; i < b.length; i++) {        // I:出力配列bの行数
            for (j = 0; j <b[i].length; j++) {
                msg += " b[" + i + "][" + j + "] =" + b[i][j];
            }
            msg += "<br>";
        }
        document.getElementById("output2").innerHTML = msg;
    }

Hで関数を呼び出した直後で、bに値が入っています。b[i][j] を変数として用いることができます。

ここでは、main1 では「出力配列bが二次元配列であることはわかっているが、その行数と列数はわかっていない」としました。それで、
  ・Gでは単なる配列宣言だけにした。
  ・二次元配列であり、行数はBで設定した。
  ・各行の列数は、Eで設定した。
  ・行数はIの b.length、列数は b{i}.length で入手した。
という手順にしました。

事前にbの行数がわかっているならば、Gの直後に、
   for (i=0, i<行数; i++) {
      b[i] = new Array();
   }
と記述して、Bを削除することができます。

「bが二次元配列か、一次元配列かわからない」場合は深刻です。一次元配列のときはIでの b.length は行数ではなく要素数になっています。b[i][j]の表記は文法エラーになってしまいます。二次元配列か、一次元配列かの適切な判定方法を私は知りません。

  

関数の配列戻り値

  function func() {
    var y = 100;
    return y;
  }
  function main() {
    b = func(a);
  }
とすると b=100 になります。
 このように戻り値yはスカラーであるのが一般的ですが、戻り値を配列にすることもできます。
 例1と同じ処理を、出力配列を引数ではなく、関数戻り値にしました。

    function func3(x) {
        for (var i = 0; i<x.length; i++) {
            x[i] = x[i] + 100;
        }
        var y = new Array();        // A:出力配列は関数側で定義
        y[0] = x[0] + 1000; 
        y[1] = x[1] + 1000; 
        return y;                   // B:戻り値yは配列
    }

    function main3() {
        var a = [0, 1, 2];
        var b = func3(a);           // C:これで b[i] = y[i] になる
        document.getElementById("output3").innerHTML
            = "a[0]=" + a[0] + ", b[0]=" + b[0] + "<br>"
            + "a[1]=" + a[1] + ", b[1]=" + b[1] + "<br>"
            + "a[2]=" + a[2] + ", b[2]=" + b[2];
    }

例1と全く同じ結果になりました。
A:例1と異なり出力配列の引数がないので、関数側で配列を定義します。
B:retrnの形式は配列であってもスカラー変数のときと同じです。
C:これだけでyの持つ情報がすべてbに渡されます。配列サイズは b.length で得られます。
Bを、var b = new array;  b = func3(a); としても同じです。

戻り値y を二次元配列にすることもできます。Aの直後に y[i] = new array(); を記述すればよいのです。