JavaScriptの関数を理解するために、最低限に関数の宣言、関数スコープ、グローバル/ローカル変数の操作、関数の巻き上げ、関数のクロージャ、関数の引数定義、関数の呼び出しなどを理解する必要がある
JavaScriptがオブジェクト指向言語と書かれていますが、実際にクラスの定義が見当たらず、代わりに関数(function)でオブジェクトのインスタンスを生成しています。また関数がオブジェクトのインスタンスを生成しなくてもそのままに呼び出して使用しています。
Javaプログラミングの経験があって、JavaScript関数についてあまり勉強しなくて、ある程度コーティングすることができましたが、Javaと比べて柔軟性と曖昧性の高いJavaScriptコードを自己流で書きました。小規模でデバッグがしやすく、いろいろを間違ってもうまく進んできました。
最近少し開発規模が大きくなり、JQueryも導入し、JavaScript関数について詳しく調査して、その特徴と注意点を纏めました。
関数(function)はある処理のブロックである
関数名がなくても宣言できる
- 以下の方法で関数宣言を行う
- 関数宣言(function文で関数を定義 ⇐ 関数を宣言して、後で利用する)
function myFunc( a, b ){ return a * b ; } console.log( myFunc( 4, 5) ); // 20が表示される
- 関数式(function演算子で関数を定義 ⇐ 関数を変数としての使用)
var x = function( a, b ){ return a * b ; }; var z = x( 3, 4 ) ; console.log( z ) ; // 12が表示される
- Functionのコンストラクタで宣言
var myFunc = new Function("a", "b", "return a * b"); var x = myFunc( 3, 4 ) ; console.log( x ) ; // 12が表示される
- 関数宣言(function文で関数を定義 ⇐ 関数を宣言して、後で利用する)
- 関数宣言の場合匿名でも関数の宣言ができる
(function () { // 関数名がない alert( "Hello!!" ) ; })(); // 自分自身を呼び出し、"Hello!!"が表示される
- 関数式での関数宣言の場合、関数名がその関数内にしか使えない(再帰呼び出し)
var func_var = function func_name(){ console.log( "Hello" ) ; // 関数内でその関数を呼び出し return func_name; // func_name()で記述すると、 //再帰呼び出しになり、メモリが //なくなるまでに「Hello」を表示される } // 変数の関数を呼び出し func_var(); // 関数名として呼び出し func_name(); // 当該関数名が定義されていないエラー発生
- Function()コンストラクタの宣言 ⇔ 関数宣言と関数式 との違い: Function()コンストラクタで宣言した関数が関数名がないこと
- 関数宣言 ⇔ 関数式 との違い: 巻き上げ(Hoisting)、関数名での呼び出し可能かのこと
匿名関数を実行させるための関数の自己呼び出し
- 関数名、変数名(関数式での宣言)で呼び出す
- 匿名関数に自己呼び出し(定義が完了時点で自動的に呼び出される) ⇒ もちろん通常の関数宣言、関数式の関数にも適応
- Functionオブジェクトのメッソド(call()、apply())で呼び出す
function my_Func( p1, p2 ){ console.log( p1+p2 ) ; } my_Func.call( this, 2, 3 ); // 5が表示される
function my_Func( p1, p2 ){ console.log( p1+p2 ) ; } var p_array = [5, 8] ; my_Func.apply( this, p_array ); // 13が表示される
call(),applyの違いは直接に引数をを渡すか、引数を配列に入れて渡すのこと
関数にあるローカル変数への直接にアクセスはできない
- ローカル変数の宣言は必ず「var」で宣言が必要となる -> 関数外であれば「var」がなくてもグローバル変数にはなる
var x = 10 ; // グローバル変数 function my_func( p ) { var x = p ; // 関数内のローカル変数 return console.log( "x:" + x ); } my_func( 100 ) ; // ローカル変数「x」が100になる console.log( x ); // グローバル変数「x」が10のままになる
- 関数内で「this」または「var」なしで宣言した変数はグローバル変数となる
var x = 10; // グローバル変数 function my_func( p ) { this.x = p ; // グローバル変数への参照 y = 20 ; // グローバル変数の宣言 return console.log ( "x:" + x ); } my_func( 100 ) ; // 関数内のグローバル変数「x」に100を代入する console.log( x ) ; // グローバル変数「x」が100になる console.log( y ) ; // グローバル変数「y」が20として表示される
- 関数外から関数内の変数へのアクセスはできない
function my_func( p ) { var x = p ; // 関数内のローカル変数 return x ; } var fun_var = my_func( 100 ) ; // ローカル変数「x」に100を100代入する console.log( fun_var.x ); // xのundefinedエラーにある
関数のローカル変数へのアクセスできるクロージャ(Closure)がある
- 関数の変数をカプセル化にして、関数内のインナー関数でアクセスすることができる
- 関数のクロージャにするため二つの要素が欠かせない
- ローカル関数を操作、返却するインナー関数が必要
- その関数を変数に渡して保持する必要がある
function my_func( base_counter ){ var counter = 0 ; // ローカル変数の宣言 return function count(){ // 返却インナー関数の宣言 counter ++ ; return counter + base_counter ; } } // 独立したクロージャ_1の作成 var my_counter_1 = my_func( 10 ) ; console.log( my_counter_1() ) ; // 11が表示される console.log( my_counter_1() ) ; // 12が表示される // 独立したクロージャ_2の作成 var my_counter_2 = my_func( 100 ) ; console.log( my_counter_2() ) ; // 101が表示される console.log( my_counter_2() ) ; // 102が表示される
- 同一関数のクロージャが独立なので、違う変数に渡して保持することができる
ローカルスコープ(Local Scope)の変数がグローバルスコープ(Global Scope)の変数への参照ができるスコープチェーン
- JavaScritが関数単位でのスコープとなる
- 関数内はローカルスコープ、関数外はグローバルスコープとなる
- グローバルとローカル変数の区別: 関数外⇒グローバル⇒ブラウザ所有、 関数内⇒ローカル⇒当該関数所有
- 関数のローカルスコープがその親のグローバルスコープの変数への参照が可能(スコープのチェーン)
関数がオブジェクトのコンストラクタになる
- JavaScriptが「class」の演算子はない
- JavaScriptのオブジェクトのインスタンスが関数で作成される
- 関数が「new」演算子でそのオブジェクトインスタンスのコンストラクタになる
function MyObject( p1 ){ this.a = 5; this.getValue = function() { return this.a + p1 ; } } ; var obj_1 = new MyObject(3) ; console.log( obj_1.getValue() ) ;
オブジェクトタイプの引数を関数に渡す場合に参照渡す(passed-by-referrence)となる
- 関数宣言時に引数定義がなくても、関数を呼び出す時に引数を渡すことができる
JavaScriptの関数が処理のプロセスとして利用され、関数内の各メンバーへのアクセスはできない。
関数の宣言がそのスコープ(Scope)内でどこでもできる仕組み:宣言の巻き上げ(Hoisting)
- Javascriptの変数宣言はグローバル/ローカル変数がある
- グローバルとローカルの区別: 関数外⇒グローバル⇒ブラウザ所有、 関数内⇒ローカル⇒当該関数所有
- ローカル変数の宣言は必ず「var」で宣言が必要となる -> 関数外であれば「var」がなくてもグローバル変数にはなる
- 関数内で「var」がない変数がグローバル変数に参照する。もしグローバル変数がない場合、ローカル変数になる
- 関数の変数を関数ブロックの前に宣言することができる(変数宣言の巻き上げ)
- ローカル/グローバル変数の宣言、参照、利用に注意が必要となる。でなければ大規模なプロジェクトで変数が見つからない、重複宣言、間違って参照してしまうことになる。