Javascript関数式


WilsonLiu'sブログの最初のアドレス
関数を定義する方法
第一に、関数宣言第二に、関数式
関数宣言の昇格
sayHi();
function sayHi(){
    alert("Hello world!")
}
7.1再帰
再帰関数は、1つの関数が名前で自身を呼び出す場合に構成されます.
function fac(num) {
    if (num <=1) {
        return 1;
    } else {
        return num * fac(num - 1);
     }
}
var anotherFac = fac;
fac = null;
alert(anotherFac(4))
上記のコードはエラーが発生します.fac=nullは関数に対するfacの参照を解除しました.another Facは関数を指すだけです.
function fac(num) {
    if (num <=1) {
        return 1;
    } else {
        return num * arguments.callee(num - 1);
     }
}
var anotherFac = fac;
fac = null;
alert(anotherFac([4,4]))
arguments.calleeは、正則に対して実行される関数を指すポインタである.しかし、use strictの場合、arguments.calleeはエラーを報告します.
名前付き関数式を使うことができます.
var factory = (function fac(num) {
    if (num <=1) {
        return 1;
    } else {
        return num * fac(num - 1);
     }
})
7.2クローズド
クローズドとは、別の関数作用ドメイン変数にアクセスする権限を持つ関数です.閉じたパケットを作成する一般的な方法は、関数の内部に別の関数を作成することです.内部匿名関数の作用ドメインチェーンは、2を指します.大域変数オブジェクト、1:関数変数オブジェクト、0:自分の変数オブジェクトを含みます.したがって、関数を含む実行が終了すると、関数を含む作用分域チェーンは破壊されてしまうが、関数を含む変数オブジェクトに対しては任然関数の作用分域チェーンが指し示すため、回収機構はトリガされない.
回収メカニズム:メモリ資源を適時に回収するために、メモリに記憶されているオブジェクトにポインタがない場合、対象は回収され、廃棄されます.
例をあげて説明する
function test(arg){
    var ss = arg;
    return function(opt){
        console.log(ss);  //  ,                    ss,    ,                      ,  ss =  arg,  
        console.log(opt); //    opt
    }
}
var testCache = test("name"); //    ,  ss = arg ="name";testCache      
var result = testCache("test");  //      ,  opt = "test",               log
testCacheは内部の匿名関数(回収メカニズムを満たしていない)を指すので、内部の匿名関数は破壊されず、testの作用ドメインチェーンは破壊されたが、彼の活動対象は匿名関数が破壊されるまでメモリに残っている.testCache = null // ( )7.2.1クローズドと変数
作用するドメインチェーンのこの構成機構は,関数内の任意の変数を含む最後の値しか得られないという注意すべき副作用を引き出す.パッケージを閉じて保存するのは、変数オブジェクト全体で、特定の変数ではありません.
function test(){
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function(){
            return i;
        }
    }
    return result;
}
var testCache = test();  //testCache  result
for (var i = 0; i < 10; i++) {
    var anonymous = testCache[i]; //  testCache[i]  result       i        ;
    console.log(anonymous());  //      ,           
}
以上のコードの出力は全部10です.予想の1~10ではありません.anonymousが指す匿名関数はtest関数の活動対象として保存されているので、彼らが引用するのは同じiです.test関数の呼び出しが完了し、resultに戻った後、変数iの値は10となりますので、anonymousは変数iを保存する同じ変数オブジェクトを参照します.
function test(){
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = (function(num){
                return function () {
                    console.log(i);
                    return num;
                }
        })(i)
    }
    return result;
}
var testCache = test();  //testCache  result
for (var i = 0; i < 10; i++) {
    var anonymous = testCache[i]; //  testCache[i]  result       i        ;
    console.log(anonymous());  //      ,           
    // anonymous()
}
第二のコードでは、私たちは直接にクローズドパケットを配列にコピーせず、匿名関数を定義し、直ちに匿名関数を実行する結果を配列に割り当てた.ここの匿名関数にはパラメータnumがあります.つまり、最後の関数が戻る値はnumはiから値によって伝達されます.だから毎回iの現在値をパラメータnumにあげます.この匿名関数の内部では、numにアクセスするクローズドが作成されます.このように、result配列の各関数は、自分のnum変数のコピーを持っていますので、それぞれの異なる値を返します.
7.2.2 thisオブジェクトについて
匿名関数の実行環境は大域的であるので、そのthisオブジェクトは一般にwindowを指す(call()またはappy()で指向を変えることができる).
各関数は、呼び出し時に自動的に2つの特殊変数を取得します.thisとargmentsは、内部関数がこの2つの変数を検索すると、そのアクティブなオブジェクトの位置だけが検索されます.したがって、外部関数の2つの変数に直接アクセスすることは永遠に不可能です.
var name = "this window";
var object = {
    name: "my object",
    getNameFun: function(){
        return function(){
            return this.name;
        }
    }
}
console.log(object.getNameFun()()); //log "this window"     this    window
上記のコードの内部に戻る匿名関数がほしいなら、object内の変数値に戻ることができます.
var name = "this window";
var object = {
    name: "my object",
    getNameFun: function(){
        var that = this;
        return function(){
            return that.name;
        }
    }
}
console.log(object.getNameFun()()); //log "my object" that     object,              that
7.2.3メモリリーク
function test(){
    var el = document.getElementById('someOne');
    el.onclick = function(){
        alert(el.id);
    }
}
上のようなtest関数にはdom要素の参照がありますが、クローズドにはdom要素が参照されています.これにより、dom元素が占有しているメモリはいつまでも回収されません.
function test(){
    var el = document.getElementById('someOne');
    var id = el.id;
    el.onclick = function(){
        alert(id);
    }
    el = null;
}
しかし、小さな変更ができます.domを回収することができます.
7.3ブロックレベルのスコープを模倣する
JavaScriptには、ブロックレベルのスコープの概念がありません.これはブロックレベルのステートメントで定義された変数を意味します.実際には、ステートメントではなく関数を含めて作成されます.次の例を見てください.
function test(count){
    for (var i = 0; i < count; i++) {
        console.log(i);//  1~count-1
    }
    console.log(i);//  count
}
test(5)
C++、Javaなどの言語では、変数iはforループのステートメントブロックに定義があり、ループが終了すると変数iは破棄されます.しかし、JavaScriptの変数iはtest()の活動対象に定義されている.
したがって、匿名関数はブロックレベルのスコープを模倣するために使用されてもよい.
function(){
    //        
}()  //  
上述のコードが文法的なエラーを引き起こすのは、JavaScriptが関数宣言の開始として機能宣言を行い、関数宣言の後に括弧を付けてはいけないからです.しかし、関数式の後には、丸括弧があります.関数宣言を関数式に変換するには、下記のように丸括弧を付けるだけでいいです.
(function(){
    //        
})()
7.4プライベート変数
厳密には、jsにはプライベートメンバーの概念がないので、オブジェクト属性はすべて公有です.しかし、プライベート変数の概念があります.関数内で定義された変数は、関数の外部ではこれらの変数にアクセスできないので、プライベート変数として認識できます.プライベート変数は、関数のパラメータ、局所変数、および関数内部で定義される他の関数を含みます.
私たちはプライベート変数とプライベート関数にアクセスする権利がある公開方法を特権方法と呼びます.相手に特権を作る方法は二つあります.
7.4.1構築関数を利用して特権方法を定義する
第一に、構造関数で特権を定義する方法であり、基本パターンは以下の通りである.
function Person(name){
    var year = 2015;
    this.getName = function(){
        return name;
    }
    this.setName = function(Value){
        name = Value;
    }
    console.log(this);
}
var person = new Person("example"); //       ,log(this)     Person  ,person         
Person("test");を使用して直接に関数を呼び出すと、thisはグローバルオブジェクトwindowを指し、windowに2つの方法を追加します.したがって、オブジェクトをnewで具体化しなければなりません.毎回インスタンス化されたオブジェクトの中で、name値は異なりますが、毎回この2つの関数を再作成します.これも構造関数を使って特権的な方法を作成する欠点です.
2つの方法は、作用ドメインチェーンを介してyearというプライベート変数にアクセスすることができるが、yearが特権的な方法ではない場合、方法のclosure(クローズド中にyearという属性はない).
7.4.2静的私有変数
プライベートスコープにプライベート変数や関数を定義することによって、特権的な方法を作成することもできます.だから
(function(){
    var name = "";
    Person = function(value){
        name = value;
    };
    Person.prototype.getName = function () {
        return name;
    };
    Person.prototype.setName = function(value){
        name = value;
    };
})();
var person1 = new Person("test");
console.log(person1.getName());
注意点:このモードは、コンストラクタを定義する際に関数宣言を使用せずに、関数式を使用します.関数宣言は局所関数のみ作成できます.私達もPersonを宣言する時にvarを使っていません.だからPersonはグローバル変数です.前者との違いは、プライベート変数と関数がインスタンスによって共有されることです.
7.4.3モジュールモード
前のパターンは、カスタムタイプのプライベート変数と特権方法を作成するためのものです.モジュールモードは、一例でプライベート変数と特権方法を作成します.単一の例とは、一例のオブジェクトだけを指す.以下の通りです
var singleton = function(){
    //        
    var privateVariable = 10;
    function privateFunction(){
        return false;
    }
    //   /       
    return {
        publicProperty: true,
        publicMethod: function(){
            privateVariable++;
            return privateFunction();
        }
    }
}()
webアプリケーションでは、アプリケーションレベルの情報を管理するために、1つの例を使用することが多い.
7.4.4強化モジュールモード
var application = function(){
    //        
    var privateVariable = 10;
    function privateFunction(){
        return false;
    };
    //   BaseComponent,app 
    var app = new BaseComponent();
    //   /       
    app.publicProperty = true;
    app.publicMethod = function(){
        privateVariable++;
        return privateFunction();
    };
    return app;
}()
このような強化されたモジュールパターンは、これらの単一の例があるタイプの例である必要があり、いくつかの属性と方法を追加して強化しなければならない場合にも適用される.
付録
thisの作業原理
JavaScriptには他の言語とは全く異なる対応メカニズムがあります.5つの異なる場合、thisの指す方向はそれぞれ異なる.グローバル範囲thisは、全範囲でthisを使用すると、グローバルオブジェクトに向けられます.関数コールfoo();は、ここでthisもグローバルオブジェクトを指す.方法コールtest.foo();この例では、thisはtestオブジェクトを指す.構造関数new foo();を呼び出し、関数がnewキーワードと一緒に使用される傾向がある場合、この関数を構造関数と呼ぶ.関数の内部で、新たに作成されたオブジェクトを指します.表示の設定this
function foo(a, b, c) {}
var bar = {};
foo.apply(bar, [1, 2, 3]); //        ,    
foo.call(bar, 1, 2, 3); //    foo    :a = 1, b = 2, c = 3
関数内のthisは、Funtion.prototype上のcallまたはapply方法を使用すると、関数呼び出しの最初のパラメータとして明示的に設定されます.
よくある誤解
誤解1
よくある誤解はtestの中のthisがFooの対象を指していますが、実際にはそうではありません.
Foo.method = function() {
    function test() {
        // this           (   :          window   )
    }
    test();
}
testでFooオブジェクトへの参照を得るためには、Fooオブジェクトに向けた局所変数を作成する必要があります.
Foo.method = function() {
    var that = this;
    function test() {
        //    that     Foo   
    }
    test();
}
thatは私たちが勝手につけた名前ですが、この名前は外部のthisオブジェクトを指すために広く使われています.閉込めセクションでは、thatがパラメータとして伝達されることが見られます.
誤解2
もう一つの不思議なところは関数の別名、つまり一つの方法を変数に割り当てます.
var test = someObject.methodTest;
test();
上記の例では、testは普通の関数のように呼び出されます.したがって、関数内のthisはsomeObjectオブジェクトには向けられなくなります.