JavaScript関数式——「関数の再帰とクローズド」の注意点

9150 ワード

関数式の基本概念name属性と関数の向上
まず、name属性は、この属性を通じて関数に指定された名前にアクセスすることができる.(標準以外の属性)
function People(){};
console.log(People.name); //People
第二に、関数宣言は、関数宣言を呼び出し文の後に置くことができることを意味します.例えば:
sayHi(); //    
function sayHi(){ //    
    console.log("Hi");
} //    
関数式を使うといけません.
sayHi();
var sayHi = function(){
    console.log("Hi");
} //  
関数を作成する2つの方法、1つは関数宣言(例えば、最初の方法)です.一つは関数式(第二の方式)です.第二の関数作成方式で作成された関数は、「匿名関数」または「ラムダ関数」と呼ばれる.
関数アップのよくあるエラー
注意したいのは、対比として、次の二つのコードの中で、最初はエラーです.二つ目が正しいと思います.コードは以下の通りです
var condition = true;
if (condition){
    function sayHI(){
        console.log("hi")
    }
    sayHI(); //"hello"
}else{
    function sayHI(){
        console.log("hello")
    }
    sayHI();
}
エラーを申し込む
var condition = false;
var sayHi;
if(condition){
    sayHi = function(){
        console.log("hi")
    };
    sayHi();
}else{
    sayHi = function(){
        console.log("hello")
    };
    sayHi(); //hello
}
エラーがありません
var condition = true;
if(condition){
    var sayHi = function(){
        console.log("hi")
    };
    sayHi(); //hi
}else{
    var sayHi = function(){
        console.log("hello")
    };
    sayHi(); //hello
}
ここにも問題はありません.上の問題の根源は関数の昇格であり、関数の宣言と関数の表現の違いによるものです.
関数の再帰
再帰関数は、一つの関数が名前で自身を呼び出した場合に構成されます.例えば:
function factorial(num){
    if(num <= 1){
        return 1;
    }else{
        return num * factorial(num - 1);
    }
}    
console.log(factorial(4)); //24 4*3*2*1
しかし、関数には関数自体が含まれていますので、arguments.calleeを使用してこの問題を解決するべきです.例えば:
function factorial(num){
    if(num <= 1){
        return 1;
    }else{
        return num * arguments.callee(num - 1);
    }
}    
console.log(factorial(4)); //24 4*3*2*1
しかし、このような方法を使うと、厳しいモードでは通用しません.名前付き関数式を使用しても同じ結果が得られます.例えば:
var factorial = (
    function f(num){
        if(num <= 1){
            return 1;
        }else{
            return num * f(num - 1);
        }
    }
);    
console.log(factorial(4)); //24 4*3*2*1
つまり、それを一つの変数に含めると、他の必要性があるときにarguments.calleeを使用してもいいです.
関数のクローズド
クローズドとは、別の関数のスコープにアクセスするための変数の関数です.一般的なクローズドの作成方法は、関数の内部に別の関数を作成することです.その前に、まず役割ドメインチェーンの概念を身につけるべきです.
スコープチェーン
下記のコードを例にとって

function compare(value1,value2){
    if (value1 > value2){
        return 1;
    }else if (value1 < value2){
        return -1;
    }else {
        return 0;
    }
}
var result = compare(5,10);
関数compareを呼び出した場合、関数は環境内の作用ドメインチェーンを実行します.
作用するドメインチェーンは、本質的に変数オブジェクトを指すポインタのリストです.
別の例:
function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        
        if (value1 < value2){
            return -1;
        }else if(value1 > value2){
            return 1;
        }else{
            return 0;
        }
    };
}
var compare = createComparisonFunction("name");
var result = compare({name: "Nicholas"},{name: "Greg"});
これは次のとおりです.
var compare = function(object1,object2){
    var value1 = object1["name"];
    var value2 = object2["name"];
    
    if (value1 < value2){
        return -1;
    }else if(value1 > value2){
        return 1;
    }else{
        return 0;
    }
};
var result = compare({name: "Nicholas"},{name: "Greg"});
相当于:
var result = function(){
    var value1 = {name: "Nicholas"}["name"];
    var value2 = {name: "Greg"}["name"];
    
    if (value1 < value2){
        return -1;
    }else if(value1 > value2){
        return 1;
    }else{
        return 0;
    }
};
console.log(result()); //1
したがって、完全なコードは以下の通りです.
var compareNames = createComparisonFunction("name");

function createComparisonFunction(propertyName){
    return function(object1,object2){
        var value1 = object1[propertyName];
        var value2 = object2[propertyName];
        
        if (value1 < value2){
            return -1;
        }else if(value1 > value2){
            return 1;
        }else{
            return 0;
        }
    };
}

var result = compareNames({name: "Nicholas"},{name: "Greg"});

compareNames = null;
compareNames()関数を呼び出している間に発生する作用ドメインチェーン間の関係図は以下の通りである.
一般的なクローズドのパターンは、一般的にこうです.
var X = function A(a){
    return function(b1,b2){...a...} //    
};    
var Y = X(b1,b2);
例を挙げます
var obj1 = {
    name: "co",
    color: ["white","black"]
};
var obj2 = {
    name: "lor",
    color: ["yellow","red"]
};

function displayProperty(propertyName){
    return function(obj1,obj2){
        var value1 = obj1[propertyName];
        var value2 = obj2[propertyName];
        
        if(typeof value1 === "string"){
            return value1 + " and " + value2 + "
"; }else if(value1 instanceof Array){ return value1.toString() + "
" + value2.toString(); }else{ return false; } }; } var displayP = displayProperty("name"); var displayStr = displayP(obj1,obj2); document.write(displayStr); displayP = null; var displayP = displayProperty("color"); var displayStr = displayP(obj1,obj2); document.write(displayStr); /* co and lor white,black yellow,red */
クローズドは彼が含む関数の作用領域を携帯しますので、メモリ資源をより多く占用します.絶対必要な時だけクローズドを使うことを提案します.V 8最適化後のjsエンジンはクローズドされたメモリの回収を試みる.
クローズドと変数
クローズドの副作用は、クローズドは関数内の任意の変数を含む最後の値しか得られません.thisオブジェクト
グローバル関数でthis=window;手紙があるオブジェクトのメソッドとして呼び出された場合、this=そのオブジェクトです.しかし匿名関数の実行環境はまたグローバルであり、thisは通常windowを指す.例外があります
var name = "the window";

var obj = {
    name: "the obj",
    
    getNameFunc: function(){
        return function(){
            return this.name;
        };
    }
};

console.log(obj.getNameFunc()()); //"the window"           
各関数は呼び出し時に自動的に二つの特殊な変数を取得するからです.内部関数は、この2つの変数を検索すると、アクティブなオブジェクトだけが検索されます.
var obj = {
    name: "Oliver",
    age: 18,
    friends: ["alice","troy"],
    sayName: function(){
        return this.name;
    },
    sayFriends: function(){
        return function(){
            return this.friends;
        };
    }
    
};

console.log(obj.sayFriends()()); //undefined
上のコードはクローズドの問題でエラーが発生しました.また、
var friends = "window's friends";

var obj = {
    name: "Oliver",
    age: 18,
    friends: ["alice","troy"],
    sayName: function(){
        return this.name;
    },
    sayFriends: function(){
        return function(){
            return this.friends;
        };
    }
    
};

console.log(obj.sayFriends()()); //window's friends             
この問題を解決する方法は:
var friends = "window's friends";

var obj = {
    name: "Oliver",
    age: 18,
    friends: ["alice","troy"],
    sayName: function(){
        return this.name;
    },
    sayFriends: function(){
        var outer = this;
        return function(){
            return outer.friends;
        };
    }
    
};

console.log(obj.sayFriends()()); //["alice","troy"]            
また、
var friends = "window's friends";

var obj = {
    name: "Oliver",
    age: 18,
    friends: ["alice","troy"],
    sayWindowFriends: function(){
        return function(){
            return this.friends;
        };
    },
    sayFriends: function(){
        var outer = this;
        return function(){
            return function(){
                return function(){
                    return outer.friends
                };
            };
        };
    }
};

console.log(obj.sayWindowFriends()()); //"window's friends"
console.log(obj.sayFriends()()()()); //["alice", "troy"]
もう一つ例を挙げます
var obj = {
    name: "Oliver",
    age: 18,
    friends: ["alice","troy"],
    sayFriends: function(i){
        var outer = this;
        return function(j){
            return outer.friends[i] + outer.friends[j];
        };
    }
};

console.log(obj.sayFriends(0)(1)); //alicetroy
また、いくつかの特殊な場合には、thisの値が変更される場合があります.例えば:
var name = "the window";

var obj = {
    name: "my object",
    
    getName: function(){
        return this.name;
    }
};

console.log(obj.getName()); //my object
console.log((obj.getName)()); //my object         ,       
console.log((obj.getName = obj.getName)()); //the window
次の2つの方法で関数を起動しない限り大丈夫です.
メモリリーク
上記の繰り返しを通じて、最も明らかなのはwindow.nameがずっとメモリを使っています.手で掃除してwindow.name = nullで操作しなければなりません.