04-関数

9146 ワード

関数
JavaScript言語は、関数を値として見なし、他の値(数値、文字列、ブール値など)と同じ位置にある.値が使えるところなら、関数が使えます.例えば、関数の値を変数やオブジェクトの属性に割り当てたり、パラメータとして他の関数に伝えたり、関数の結果として返したりすることができます.関数は実行可能な値だけで、特別なものはありません.JavaScriptには3つの関数タイプがあります.関数宣言、関数式、Functionコンストラクターから作成された関数です.
  • 関数宣言(FD)関数宣言:function関数名(パラメータ:オプション){関数体}
  • 特定の名前があります.
  • は、コンテキストに入る段階で
  • を作成する.
  • は変数オブジェクトに影響を及ぼします.
  • は、グローバル環境または関数内で宣言することができる(式またはコードブロック内では宣言できない)
  • .
  • は、関数
  • を定義する前に呼び出すことができる.
        //          
        function foo() {
          //             
          function innerFD() {}
        }
    
  • 関数式(FE)関数式:function関数名(オプション)(パラメータ:オプション){関数体}
  • 名前はオプション
  • です.
  • コード実行段階に
  • を作成する.
  • は変数オブジェクトに影響を与えません.
  • は常に式の位置にあります.
  • は、定義の前に呼び出されてはいけません.定義の段階の後でも呼び出されません.コード実行段階でのみ、関数名がない場合は関数式として呼び出されます.関数名がある場合は、コンテキストによって判断します.関数がグローバル環境や関数の中にある場合は、関数宣言です.表式の一部であれば、関数式です.関数の表現は、語句の最後にセミコロンを付けて文の最後を表します.関数の声明は末尾の大括弧の後でプラス記号の
  • を使いません.
        function foo(){} //     ,        
        var bar = function foo(){}; //      ,          
        new function bar(){}; //      ,    new       
        (function(){
           function bar(){} //     ,       
        })();
        //   、  、                  
        var foo = function _foo() {}; //      
        (function foo() {});//    (     ),
        [function bar() {}];//         
        1, function baz() {};//      
        !function () {}(); //      
    
    関数式は定義前には使用できません.変数オブジェクトにないので、段階を定義しても使用できません.
        console.log(foo); // foo is not defined
        (function foo() {});
        console.log(foo);  // foo is not defined
    
    変数オブジェクトが存在しない関数式には、変数オブジェクトに影響を与える変数(undefinedに初期化)を組み合わせてアクセスできます.
        console.log(foo); // undefined
        var foo = (function foo() {});
        console.log(foo);  // function foo() {}
    
    関数式宣言関数を使用する場合、関数の外部でのみ有効である関数名に関数名を付けるネーミング関数表現です.このような書き方の用途は2つあります.1つは、関数の内部で自身を呼び出すことができます.2つは、エラーの解消に便利です.
        (function foo(bar) {
          if (bar) {
            console.log(1);
            return;
          }
          foo(true); // 1     
        })();
        //    ,     
        foo(); // foo is not defined
    
  • Functionコンストラクタは、任意の数のパラメータをFunctionコンストラクタに渡すことができ、最後のパラメータだけが関数体として扱われ、他のパラメータはいずれも関数のパラメータであり、もしパラメータが一つであれば、このパラメータは関数体である.Functionコンストラクタはnewコマンドを使わずに返しても良いです.
  •     var add = new Function(
          'x',
          'y',
          'return x + y'
        );
        //    
        function add(x, y) {
          return x + y;
        }
    
  • 関数スコープは、変数が存在する範囲を意味します.Javascriptは二つの作用領域しかありません.一つはグローバルスコープで、変数はプログラム全体にずっと存在しています.すべての場所で読み取りできます.もう一つは関数のスコープで、変数は関数の内部だけに存在します.関数外で宣言した変数は大域変数です.関数の内部で読み込むことができます.関数内部で定義された変数は、外部から読み込めません.これを「局所変数」と呼びます.varコマンドに対しては、局所変数は関数内部でしか宣言できません.他のブロックで宣言(if、forなど)は、すべてグローバル変数です.関数自体も値であり、自分の役割領域(内部属性[[scope]]に保存)があり、その作用域は変数と同じで、その宣言時の作用域であり、その動作時の作用域とは関係がない.
  • //          ,              
        var a = 1;
        var x = function () {
          console.log(a);
        };
        function f() {
          var a = 2;
          x();
        }
        f() // 1
    // x.[[scope]] = globalContext.VO
    //   x    :x.scope = [AO,x.[[scope]]] = [AO,globalContext.VO]
    //     a,x.AO   a,        , globalContext.VO  a=1
    
    //             ,               
        function foo() {
          var x = 1;
          function bar() {
            console.log(x);
          }
          return bar;
        }
        var x = 2;
        var f = foo();
        f() // 1
    // foo       :foo.[[scope]] = globalContext.VO
    //   bar   foo    :bar.[[scope]] = foo.scope
    // foo   :foo.scope = [AO,foo.[[scope]]] = [AO,globalContext.VO]
    // bar   :bar.scope = [AO,bar.[[scope]]] = [AO,foo.AO,globalContext.VO]
    // bar      ,    x,bar.AO   x,        , foo.AO  x=1;
    //                          。
    
  • 関数パラメータが元のタイプの値(数値、文字列、ブール値)であれば、伝達方式は伝達値伝達であり、関数内でパラメータ値を変更しても、関数の外部に影響しません.
  •     var p = 2;
        function f(p) {
          p = 3;
        }
        f(p);
        p // 2
    
    関数パラメータは複合タイプの値(配列、オブジェクト、その他の関数)であり、伝達方式はアドレス伝達(着信関数の元の値のアドレス)であり、関数内部でパラメータを変更すると元の値に影響します.関数の内部で変更された場合、パラメータオブジェクトの属性ではなく、パラメータ全体を置き換えると、元の値には影響しません.
        var obj = {p: 1};
        function f(o) {
          o.p = 2;
        }
        f(obj);
        obj.p // 2
    //  o        obj  。
    //   ,   o       ,      o obj   ,             obj。
        var obj = [1, 2, 3];
        function f(o){
          o = [4, 5, 6]; //             
        }
        f(obj);
        console.log(obj); // [1, 2, 3]
    //      ,    o     obj        :o = obj
    //   obj           o, o       obj 
    //   o     ,o       ,    obj,    obj
    
    場合によっては、元のタイプの変数が必要であれば、転送の効果を取得し、グローバルオブジェクトの属性として作成することができます.
        var a = 1;
        function f(p) {
          window[p] = 2;
        }
        f('a');
        a // 2
    //   a       ,    window     ,           。
    
    同名のパラメータがある場合は、最後に現れた値をとります.
  • アーグメンントオブジェクトアーグメンツオブジェクトは、関数実行時のすべてのパラメータを含んでいます.アーグメンツ[0]は最初のパラメータです.アーグメンnts[1]は2番目のパラメータです.これに類推します.このオブジェクトは関数の内部にしかありません.argmentsオブジェクトはパラメータの読み取りに加えて、パラメータに値を割り当てることができます.行列に似ていますが、対象です.配列特有の方法(例えば、sliceとforEach)は、argmentsオブジェクト上で直接使用することができません.appy法により、argmentsをパラメータとして送ることができます.そうすると、argmentsに配列方法を使用させます.
  •     //   apply  
        myfunction.apply(obj, arguments).
        //           
        Array.prototype.concat.apply([1,2,3], arguments)
    
    アーグメンントを本物の配列に変えます.
        var args = Array.prototype.slice.call(arguments);
    
    argmentsオブジェクトはcalee属性を持ち、対応する元の関数を返します.アーグメンツ.calleeを通じて、関数自体を呼び出す目的に達することができます.
  • は、すぐに関数式(IIF)を呼び出します.Javascriptでは、一対の丸括弧()は演算子であり、関数名の後に付いて、その関数の呼び出しを表します.関数を定義した直後に関数を呼び出す必要がある場合があります.この場合、関数の定義の後に丸括弧を直接入れることはできません.これは文法的なエラーが発生します.Functionというキーワードは語句としても表現としてもよいからです.「表式」は単純な演算過程で、常に戻り値があります.「ステートメント」(statement)は、ある操作を実行するため、戻り値がありません.解析上の曖昧さを避けるために、JavaScriptエンジンは、Functionキーワードが行頭に現れたら、すべて文として解釈します.このため、JavaScriptエンジンは行頭がfunctionのキーワードであることを見てから、この段落は全部関数の定義だと思っています.括弧で終わるべきではないので、間違えました.
  •     //   
        function f() {}
        //    
        var f = function f() {}
        //         (),   
        function(){ /* code */ }();// SyntaxError: Unexpected token (
        //          (),  ()     ,    ,       
        function foo() {
            console.log(1)
        }(1)
        //             ,              ,       
    
    解決方法としては、Functionを行頭に表示させないで、エンジンに式を理解させることです.最も簡単な処理は、丸括弧の中に入れることです.括弧で始まると、エンジンは、関数定義の語句ではなく、後の付いているのが表示式であると考えられますので、エラーを回避します.これを「すぐに呼び出す関数式」IIIFEといいます.
        (function(){ /* code */ }());
        //   
        (function(){ /* code */ })();
        //  ,                。      ,      IIFE,      。
    
    関数定義を式で処理すると、同じ効果が得られます.newキーワードもこの効果が得られます.通常、匿名関数に対しては、この「即時実行関数式」のみを使用する.その目的は二つあります.一つは関数に名前を付ける必要がなく、グローバル変数を汚染しないことです.第二に、ブロックレベルの作用領域を模倣し、外部で読み取れないいくつかのプライベート変数をカプセル化することができる.
  • クローズドは、JavaScript言語では関数内部のサブ関数のみが内部変数を読み取ることができるので、クローズドを単に「関数の内部に定義された関数」と理解することができる.クローズドの最大の用途は二つあります.一つは関数内部の変数を読み取ることができます.もう一つはこれらの変数を常にメモリに保持させることです.すなわち、クローズドはその誕生環境を常に存在させることができます.
  • //              
        var foo = {};
        (function init() {
            var x = 10;
            foo.bar = function () { //    foo.bar
                console.log(++x);
            };
        })();
        foo.bar(); // 11
        foo.bar(); // 12
        console.log(x); // x is not defined         
        //      foo.bar() :
        // foo.bar().Scope: [AO, initContext.AO]
        //   x, initContext.AO   x=10,     ,x=11,   
        //      foo.bar() ,  x=11,     ,x=12,   
        //            (init)   。
    
        //         
        var data = [];
        for (var i = 0; i < 3; i++) {
            data[i] = function () {
                console.log(i);
            }
        }
        data[0](); // 3
        data[1](); // 3
        data[2](); // 3
        //    
        var data = [];
        for (var i = 0; i < 3; i++) {
            data[i] = (function (num) {
                return function() {
                    console.log(num);
                };
            })(i);
        }
        data[0](); // 0
        data[1](); // 1
        data[2](); // 2
        //data[0]()   :data[0]Context.Scope: [AO,     Context.AO,globalContext.VO]
        //data[0]Context.AO   num ,           Context.AO   ,  num=0
    
    すぐに関数式を呼び出してブロックレベルのスコープを模倣し、プライベート変数とプライベート方法を作成し、クローズドパケットを使って共有変数と方法を作成します.
        (function() {
            //     
            var age = 26;
            var name = 'Leo';
            //     
            function getAge() {
                return 'your age is' + age;
            }
            //     
            function getName() {
                return'your name is' + name;
            }
    //                 ,    ,            
            window.getAge = getAge;
        })();
    
    jQueryのモジュールとクローズド
        //               
        (function(window, undefined) {
            //   jQuery    
             var jQuery = function(name) {
                //         ,    jQuery  
                 return new jQuery.fn.init(name);
             }
            //       
             jQuery.prototype = jQuery.fn = {
                 constructor: jQuery,
                 init:function() { ... },
                 css: function() { ... }
             }
             jQuery.fn.init.prototype = jQuery.fn;
            //  jQuery   $,       window ,    ,    jQuery    ,            jQuery       
             window.jQuery = window.$ = jQuery;
         })(window);
        //     ,           ,   jQuery            ,    jQuery   ,                 new 
        $('#div1');
    
    「JavaScript高級プログラム設計」「JavaScript標準参考教程」トムおじさん-JavaScriptシリーズを深く理解する