JavaScriptの関数

関数の定義

関数を定義するには3つの方法がある。

  1. function文を用いる
  2. 関数リテラルを用いる
  3. Functionオブジェクトを用いる
関数リテラルによる関数定義

関数を値として扱えるので、varを使用して変数に代入出来る。
varで定義された変数に対して関数呼び出しを示す()を指定することで関数を呼び出せる。
オブジェクトのプロパティに関数を設定するときよく使用する。

var 変数名=function(引数,引数,…){
    複数の文
    return 戻り値;
};

var mul=function(x,y){
    return x*y;
}

var result=mul(2,3);
alert(result);

このように関数に名前が付いていないが関数リテラルでは名前をつける必要がない。
これを「無名関数」「匿名関数」と呼ぶ。

高階関数

関数の引数として関数を用いたり、関数の戻り値に関数を返すことを高階関数という。
コールバックやクロージャに使われる。

Functionオブジェクトによる関数定義

関数もオブジェクトの一種(Functionオブジェクト)なのでnew演算子を使って生成できる。

var 変数名=new Function('引数','引数',…'関数本体');

var add=new Function('x','y','return x+y;');

コンストラクタ関数(Functionコンストラクタ)に指定した最後の引数は関数の処理で
それ以前の引数は関数本体で利用する引数。引数も関数本体も文字列で指定する。
また、実行時に構文の解析やオブジェクトの生成がおこなわれるので関数本体を動的に変更出来るが
その分速度は遅くなる。

関数のネスト

関数の中に関数を定義できる。関数内で定義された関数は関数内からのみ呼び出せる。
内側の関数からは外側の変数を参照出来るが、外側の関数からは内側の変数を参照出来ない。

function a(){
    function b(v){
        alert(v);
    }
    return b;
}
var bf=a();    //aの戻り値はb関数
bf('hello');   //b関数を呼び出す

a()('hello');  //このように書いても同じ

関数の呼び出し

関数を呼び出すには2つの方法がある。

  1. 関数名を指定し呼び出す
  2. 関数定義と同時に呼び出す
関数定義と同時に呼び出す

無名関数の定義のうしろに引数を指定するための()を続けることで、定義と同時に呼び出せる。

function(引数,引数,…){関数の処理}(引数,引数,…);

var num=function(a,b){
    return a*b;
}(10,3);
alert(num);

関数の引数

JavaScriptでは引数に対してデータ型を指定出来ない。
引数にどのようなデータ型が指定されているかは、関数の側で判定する必要がある。
また同じ名前の関数は引数のデータ型や数が違っていても同じ関数として扱われ
後から定義した関数で上書きされる。
また引数がプリミティブ型のように値そのものを渡すものを「値渡し」
参照型のように値への参照を渡すものを「参照渡し」と呼ぶ。
値渡しは呼び出し元の変数が変更されることはないが
参照渡しは変更される可能性がある。

可変長引数

全ての関数にはargumentsというプロパティが定義されていて、このプロパティには関数の引数を配列形式で参照出来るArgmumentsオブジェクトを使用して参照出来る。
arguments配列には関数呼び出し時に指定された引数が指定された順番で格納されている。
関数定義よりも多い引数で関数を呼び出した場合もその引数にアクセス出来る。

var mul=function(){
    if(arguments.length==2){
        return arguments[0]*arguments[1];
    } else {
        return NaN;
    }
}
var result=mul(2,3);
alert(result);
var result2=mul(2);
alert(result2);
変数のスコープ

プログラムのトップレベルで「var 変数名」を定義されて変数はグローバル変数になる。
関数内で「var 変数名」と変数を定義するとローカル変数になる。
「var」を省略して変数を定義するとグローバル変数になるので注意が必要。
またJavaScriptにはブロックスコープの概念がないので関数内の全ての変数は関数内から参照出来る。

変数のスコープチェーン

入れ子にした関数それぞれにローカルスコープを持ち、変数名の解決のためにスコープチェーンと呼ばれるローカルスコープをたどるための情報を持っている。
もし自分自身のローカルスコープに変数がなければスコープチェーンをたどり親のローカルスコープから変数を探す。そこにもなければ親の親というように階層化されたローカルスコープを変数の名前が解決されるまでたどっていく。

グローバル変数

JavaScriptは起動されるとGlobalオブジェクトを生成し、共通変数や共通関数などをGlobalオブジェクトのプロパティにセットしそれらが使えるように準備をする。
グローバル変数を定義するということは、Globalオブジェクトのプロパティを定義することになる。

ローカル変数

JavaScriptは関数が呼ばれると関数の外からはアクセス出来ないActivationオブジェクトを生成し、ローカル変数に対するプロパティを追加しスコープチェーンに登録する。
スコープチェーンをたどる処理はこのActivationオブジェクトに登録されたプロパティをチェックしながら行われる。

再帰呼び出しとcalleeプロパティ

再帰呼び出しを行う場合は関数名を指定して呼び出すのが一般的だが、JavaScriptではArgumentsオブジェクトのcalleeプロパティに関数自身への参照が格納されているので、これで再帰呼び出しが行える。

function func(n){
    if(n==0){
        return 1;
    } else {
        return arguments.callee(n-1)*n; //nの階乗
      //return func(n-1)*n;
    }        
}
alert(func(5));  
引数を名前付きで指定する

関数呼び出し時に引数をオブジェクトで指定する方法がある。
オブジェクトリテラルを利用した、プロパティを定義したオブジェクトの生成方法

var 変数名 = { プロパティ名 : 値,…};
function func(obj){
    return obj.a * obj.b;
}
alert(func({a:10,b:2}));
クロージャ

クロージャとは「関数内で定義していない変数の参照先が、実行時の環境でなく関数を定義した環境によって決定される」ことをさす。
つまり同じ関数で使われている変数の参照先が、関数が定義された箇所によってことなる。

function closureOuter(){
    var i=0;
    function f(){
        i++;
        return i;
    };
    return f;
}
var closure=closureOuter();
document.write('1回目:'+closure()+'<br>');
document.write('2回目:'+closure()+'<br>');