IORI online School

JavaScript、html、css の無料学習サイト

【JavaScript 中級講座】windowオブジェクトと即時関数とfilter()関数

[JavaScript 中級講座]windowオブジェクトと即時関数とfilter()関数

本講座の主題は即時関数である。 ただし、即時関数を学習する過程で見る、 windowオブジェクトも同じく重要である。

windowオブジェクト

001<script>
002//関数を定義
003function iori () {
004  alert ( "IORI" ) ;
005} ;
006//[IORI]
007window.iori();
008</script>

プログラムを実行すると「IORI」と表示する。

プログラムを解説する。 3行目で関数ioriを定義する。関数の処理は4行目である。 iori()関数を実行すると、「IORI」と表示する。

7行目で、windowオブジェクトのiori()関数を実行する。 このプログラムは問題なく動作する。 iori()関数が実行して、「IORI」と表示する。 ここで疑問が生じる。

  1. windowオブジェクトはどこから出てきたのか?
  2. windowオブジェクトに、なぜiori()関数が定義されているのか?

これらの疑問はもっともである。

まず、windowオブジェクトから解説する。

windowオブジェクト Webブラウザ全体を管理するオブジェクト。

windowオブジェクトはブラウザ全体を管理するオブジェクトである。 プログラマが定義するものではなく、 JavaScriptが事前に用意しているオブジェクトである。 windowオブジェクトのプロパティや関数は、 JavaScriptから自由に呼び出す事ができる。 実は、alert()関数はwindowオブジェクトの関数である。

001<script>
002//関数を定義
003function iori () {
004  alert ( "IORI" ) ;
005} ;
006//[IORI]
007window.iori();
008</script>

いま問題になっているのは、7行目である。 その前に、3行目をもう一度解説する。 3行目でiori()関数を定義している。 この時、iori()関数はwindowオブジェクトの関数になる。 iori()関数は何かオブジェクトの中に記述しているわけではない。 ただ、3行目で定義しているだけである。 オブジェクト内に記述されていない関数は、自動的にwindowオブジェクトの関数になる。

7行目でwindowオブジェクトのiori()関数を実行すると、問題なく関数は実行する。

hasOwnProperty()関数を利用してプロパティを持っているか確認する

001<script>
002//オブジェクトを定義
003const a = {};
004
005//[false]
006alert ( a.hasOwnProperty ( "iori" ) );
007
008//ioriプロパティを追加
009a.iori = "IORI"
010
011//[true]
012alert ( a.hasOwnProperty ( "iori" ) );
013</script>

プログラムを実行すると「false」「true」と表示する。

hasOwnProperty()関数を学習する。

hasOwnProperty()関数とは オブジェクトが特定のプロパティを持っているか確認する関数

hasOwnProperty()関数の利用方法 オブジェクト.hasOwnProperty( [プロパティ名] );

hasOwnProperty()関数を利用すると、オブジェクトがプロパティを持っているのか確認できる。

001<script>
002//オブジェクトを定義
003const a = {};
004
005//[false]
006alert ( a.hasOwnProperty ( "iori" ) );
007
008//ioriプロパティを追加
009a.iori = "IORI"
010
011//[true]
012alert ( a.hasOwnProperty ( "iori" ) );
013</script>

プログラムを解説する。3行目で空のオブジェクトを作成して、変数aへ代入する。 6行目でhasOwnProperty()関数を利用して、変数aが参照するオブジェクトが 「iori」プロパティを持っているか確認する。 変数aが参照するオブジェクトは空なので、「false」が表示する。

ここで疑問が生じる。

変数aが参照する空のオブジェクトが、なぜhasOwnProperty()関数を持っているの?

6行目で変数aが参照するオブジェクトのhasOwnProperty()関数を実行する。 変数aが参照するオブジェクトは空である。 なぜ6行目は動作するのだろうか?

ここでは解説しないが、 全てのオブジェクトは自動的にhasOwnProperty()関数を保持する。 これだけ、覚えてほしい。

001<script>
002//オブジェクトを定義
003const a = {};
004
005//[false]
006alert ( a.hasOwnProperty ( "iori" ) );
007
008//ioriプロパティを追加
009a.iori = "IORI"
010
011//[true]
012alert ( a.hasOwnProperty ( "iori" ) );
013</script>

9行目で変数aが参照するオブジェクトにioriプロパティを作成して、 文字列「IORI」を代入する。

変数 参照先
a {
 iori : "IORI"
}

オブジェクトにioriプロパティが生成され、文字列「IORI」が代入する。

008//ioriプロパティを追加
009a.iori = "IORI"
010
011//[true]
012alert ( a.hasOwnProperty ( "iori" ) );

12行目で、hasOwnProperty()関数を実行して、 変数aが参照するオブジェクトに「iori」プロパティが存在するか確認する。 オブジェクトに「iori」プロパティはあるので、「true」が表示する。

windowオブジェクトとhasOwnProperty()関数

hasOwnProperty()関数を利用して、windowオブジェクトのプロパティを確認する。

001<script>
002//[false]
003alert (
004  window.hasOwnProperty ( "iori" )
005  ) ;
006</script>

プログラムを実行すると「false」と表示する。

4行目でwindowオブジェクトのhasOwnProperty()関数を実行して、 「iori」プロパティが存在するか確認する。 「false」である。「true」だったら、「IORIって何者?」という話になるし、 個人的に驚く。

001<script>
002//関数を定義
003function iori () {
004  alert ( "IORI" ) ;
005} ;
006//[true]
007alert (
008  window.hasOwnProperty ( "iori" )
009  ) ;
010</script>

プログラムを実行すると「true」と表示する。

3行目でiori()関数を定義する。iori()関数はオブジェクトに入っていないので、 自動的にwindowオブジェクトの関数になる。 8行目で、windowオブジェクトにiori()関数が存在するか確認する。 「true」と表示する。

即時関数の必要性

即時関数の必要性について解説する。 まず、alert()を考える。 alert()関数は「誰でも、どこからでも利用できる」。 alert()関数はwindowオブジェクトの関数であり、 windowオブジェクトに定義された関数は どこからでも利用可能だからである。

さて、もう一つ考える事がある。 それは、「関数をオブジェクトに入れないと、 自動的にwindowオブジェクトの関数になる」という事である。

仮に、test()という関数を作成したと仮定する。 もう一度別の場所で、test()という名前で関数を作ると、どうなるだろう? windowオブジェクトにtestプロパティが2つ生成する事は無い。 先に作った関数を上書きしてしまう。 あるプログラマーがtest()関数を製造した後、 別のプログラマーがtest()関数を上書きして製造するという事態は、充分ありえる事である。

つまり、誰でも利用できるwindowオブジェクトの中に関数を定義する事は、 この上もなく危険である。

そのため、windowオブジェクト内に 関数を定義する事は、可能な限り避ける必要がある。 もっとはっきりいうと、 なるべくwindowオブジェクト内に関数を作らない方向でプログラミングする。

即時関数とは 関数の定義と実行を同時に行なう事。

ここから即時関数の解説に入る。 即時関数は、関数の定義と実行を同時に行うことである。 これにより、windowオブジェクトに関数を定義せずに処理を実行できる。 ・・・っというのが即時関数が持つ利点の一つである。 他にも即時関数を利用する場面はあるが、本講座ではwindowオブジェクトに しぼって解説した。

Webブラウザが読み込まれる前に実行したい処理がある場合、 即時関数を利用する。

即時関数を作る

即時関数は構造が難しい。ステップバイステップで記述する。

001<script>
002
003//step1
004function ( ) {
005  alert ( "IORI" ) ;
006};
007
008</script>

4行目で関数を定義した。関数名は無い。関数を実行すると、文字列「IORI」を表示する。 このプログラムは未完成なので、ファイルを実行してもエラーが発生する。

001<script>
002
003//step2
004( function ( ) {
005  alert ( "IORI" ) ;
006} ) ;
007
008</script>

関数全体を括弧で囲んだ。4行目の冒頭で括弧が始まり、 6行目の最後で括弧を閉じる。その後、セミコロンを記述する。 このプログラムは未完成である。

001<script>
002
003//step3
004( function ( ) {
005  alert ( "IORI" ) ;
006} ) () ;
007
008</script>

6行目で括弧を記述した。この括弧が「関数を実行する」という意味である。 プログラムを実行すると、「IORI」と表示する。 この関数は名前が無い。つまり、windowオブジェクトの関数ではない。

では、次へ進む。今度は引数有り即時関数を作る。

001<script>
002
003//step1
004function ( name ) {
005  alert ( name ) ;
006};
007
008</script>

まず、引数ありの関数を定義する。この関数には名前が無い。 引数はnameで受け取り、引数で受け取った値を表示する。

001<script>
002
003//step2
004( function ( name ) {
005  alert ( name ) ;
006} ) ;
007
008</script>

4行目から6行目まで、関数全体を括弧で囲った。

001
002<script>
003
004//step3
005( function ( name ) {
006  alert ( name ) ;
007} ) ( "IORI" );
008
009</script>

7行目で関数を実行する。関数は引数を受け取る。 こんかい、関数に文字列「IORI」を渡した。

001
002<script>
003
004//step3
005( function ( "IORI" ) {
006  alert ( name ) ;
007} ) ( "IORI" );
008
009</script>

プログラムを実行すると、このように引数nameに「IORI」が入る。

実践的な即時関数

即時関数は戻り値を戻す事ができる。 そのサンプルプログラムを見る。 その後、少しだけ実践的な即時関数をご紹介する。

001<script>
002//即時関数の戻り値を受け取る
003var a = ( function (){
004  
005  var x = 10 ;
006  var y = 20 ;
007
008  return x + y ;
009} )();
010
011//[30]
012alert ( a ) ;
013</script>

プログラムを実行すると「30」と表示する。

3行目で変数aを宣言して、即時関数の戻り値を受け取る。 即時関数は3行目から9行目までである。 5行目で変数xを宣言して「10」を代入し、 6行目で変数yを宣言して「20」を代入する。 この変数は即時関数の中に記述されているので、 グローバル変数にはならない。 8行目でxとyの値を加算して戻す。 これが即時関数の内容である。 9行目で括弧を記述して、即時関数を実行する。 変数xと変数yの値を加算して「30」を戻し、 変数aが戻り値を受け取る。 12行目で変数aの値を表示すると「30」と表示する。 解説は以上である。

こんかいのサンプルプログラムは、即時関数が値を戻した。 ポイントは即時関数内で定義した変数xと変数yが、 グローバル変数にならない点である。 グローバルスコープを汚染せずにプログラムを記述できる。

つぎのサンプルプログラムは、 即時関数は「関数」を戻す。

001<script>
002//即時関数の戻り値を受け取る
003var a = ( function ( ){
004  
005  var x = 10 ;
006  var y = 20 ;
007
008  //関数を戻す
009  return function () {
010    alert ( x + y ) ;
011  };
012} )( );
013
014//[30]
015a () ;
016</script>

プログラムを実行すると「30」と表示する。

3行目で変数aを宣言して、即時関数の戻り値を受け取る。 即時関数は3行目から12行目までである。 12行目で括弧を記述して、即時関数を実行する。 5行目で変数xを宣言して「10」を代入し、 6行目で変数yを宣言して「20」を代入する。 9行目でreturnキーワードを利用して、 戻り値を指定する。returnキーワードの後ろに、 functionキーワードがある。 つまり即時関数を実行すると、 ここで記述された関数が戻る。 関数の内容は後述する。 即時関数を実行すると、変数aに関数が代入される。

014//[30]
015a () ;

15行目で変数aに代入された関数を実行する。

003var a = ( function ( ){
004  
005  var x = 10 ;
006  var y = 20 ;
007
008  //関数を戻す
009  return function () {
010    alert ( x + y ) ;
011  };
012} )( );

変数aに入っている関数は、9行目から11行目までの関数である。 10行目で変数xと変数yの値を加算して表示する。 プログラムを実行すると、「30」と表示する。 解説は以上である。

つぎに、即時関数とfilter()関数を組み合わせる。

001<script>
002//配列
003var a = [ 3 , 5 , 7 ] ;
004
005//filter()関数を実行
006var res = a.filter ( ( function () {
007
008    var x = 2 ;
009    var y = 3 ;
010    
011    return function ( v , i , array ){
012      return v > x + y ;
013    }
014  })()
015);
016//[7]
017alert ( res.join ( " " ) ) ;
018</script>

プログラムを実行すると「7」と表示する。

3行目で配列を作成して、変数aへ代入する。

6行目で、配列のfilter()関数を実行する。 配列のfilter()関数を実行する時、引数に関数を渡す。

配列.filter (
   function ( element , i , array ) {
      処理
   }
);

本来、このように記述する。 大切な事は、「filter()関数の引数は、 関数」という事である。 こんかいfilter()関数の引数に、即時関数を渡す。 そして即時関数を実行し、戻り値として関数を戻す。

配列.filter (
   即時関数を実行して関数を戻す
);

filter()関数の引数の中で即時関数を実行して関数を戻すなら、 filter()関数の引数に関数を渡した事と同じ処理になる。

006var res = a.filter ( ( function () {
007
008    var x = 2 ;
009    var y = 3 ;
010    
011    return function ( v , i , array ){
012      return v > x + y ;
013    }
014  })()
015);

filter()関数の引数に渡す関数は、11行目から13行目までである。 プログラムは、次のプログラムと同じである。

003var a = [ 3 , 5 , 7 ] ;
004
005//filter()関数を実行
006var res = a.filter (
007    function ( v , i , array ){
008      var x = 2 ;
009      var y = 3 ;
010      return v > x + y ;
011    }
012);
013//[7]
014alert ( res.join ( " " ) ) ;

filter()関数の引数に、即時関数の戻り値が入る。

さて、filter()関数の動きを確認する。 filter()関数を実行すると、最初に配列の要素「3」が関数の引数へ渡される。

003var a = [ 3 , 5 , 7 ] ;
004
005//filter()関数を実行
006var res = a.filter (
007    function ( 3 , 0 , [3,5,7] ){
008      var x = 2 ;
009      var y = 3 ;
010      return v > x + y ;
011    }
012);

変数vの値は「3」である。10行目を実行すると、関数の戻り値は 「false」になる。つまり、「3」という値は無視される。

003var a = [ 3 , 5 , 7 ] ;
004
005//filter()関数を実行
006var res = a.filter (
007    function ( 5 , 0 , [3,5,7] ){
008      var x = 2 ;
009      var y = 3 ;
010      return v > x + y ;
011    }
012);

つぎに、変数vの値は「5」になる。10行目を実行すると、「false」になる。 値「5」は無視される。

003var a = [ 3 , 5 , 7 ] ;
004
005//filter()関数を実行
006var res = a.filter (
007    function ( 7 , 0 , [3,5,7] ){
008      var x = 2 ;
009      var y = 3 ;
010      return v > x + y ;
011    }
012);

次に配列の3番目の値「7」が変数vへ代入される。 10行目を実行すると、「true」である。 値「7」はfiler()関数の戻り値に代入される。 filter()関数を実行すると、変数resに戻り値が入る。 変数resは配列である。

インデックス
0 7

変数resの値を表示すると、「7」とダイアログに表示する。 解説は以上である。

まとめ

windowオブジェクト Webブラウザ全体を管理するオブジェクト。

hasOwnProperty()関数とは オブジェクトが特定のプロパティを持っているか確認する関数

hasOwnProperty()関数の利用方法 オブジェクト.hasOwnProperty( [プロパティ名] );

即時関数とは 関数の定義と実行を同時に行なう事。