実際、js が関数クロージャーをサポートする主な理由は、js がデータを保存できる関数を必要とするためです。ここでの保存データとは、関数終了後に関数内の変数の値のみが保存されるものです。なぜ js が関数内にデータを保存できる必要があるのかというと、js は関数型言語だからです。関数内にデータを保存することは、関数型言語の特徴です。
前に紹介した関数を定義する 3 つの方法を確認してください
functiosu(numnumreturnunum//関数宣言構文定義
vasufunction(numnum)returnunum}//関数式定義
vasuneFunction("num""num""returnunum")//Functio コンストラクター
クロージャを分析する前に、まず関数の定義と呼び出しにおけるよくある間違いを見てみましょう。
例 1:
sayHi(); //错误:函数还不存在 var sayHi = function () { alert("test"); };
例 2:
if (true) { function sayHi() { alert("1"); } } else { function sayHi() { alert("2"); } } sayHi();//打印结果并不是我们想要的
例 3:
var fun1 = function fun2() { alert("test"); } fun2();//错误:函数还不存在
例 1 では、関数宣言構文を使用して関数を定義する前に関数を呼び出すことはできません。解決策:
1. 関数式を使用して関数を定義する場合は、式を定義した後に関数を呼び出す必要があります。
var sayHi = function () { alert("test"); }; sayHi()
2. 関数宣言を使用します。 (ここでは、ブラウザ エンジンが関数宣言をプロモートし、すべてのコードが実行される前に関数宣言を読み取ります)
sayHi(); function sayHi () { alert("test"); };
例 2 では、期待される結果は 1 を出力するはずですが、実際の結果は 2 を出力します。
if (true) { function sayHi() { alert("1"); } } else { function sayHi() { alert("2"); } } sayHi();//打印结果并不是我们想要的
なぜこのようなことが起こっているのでしょうか?関数宣言のプロモーションにより、ブラウザーは事前解析中に if 条件を判断せず、2 番目の関数定義を解析するときに最初の if 条件を直接上書きします。
解決策:
var sayHi; if (true) { sayHi = function () { alert("1"); } } else { sayHi = function () { alert("2"); } } sayHi();
例 3 では、fun1() のみを使用して呼び出すことができ、fun2() を呼び出すことはできないことがわかりました。
私自身の理解では、本当の理由はわかりません。情報は見つかりませんでした。
次の図に示すように、1: function fun3() { }; は var fun3 = function fun3() { }; と同等であるためです。
つまり、fun1() のみを使用して呼び出すことができ、fun2() を呼び出すことはできません。
実は、まだ質問があるのですが?知っている人がいたら教えてください。
fun2 は外部から呼び出せないのに、なぜ関数内から呼び出せるのでしょうか?ただし、デバッガではまだ fun1 を取得できません。
それでは、上記の 3 つの質問について準備をしていきましょう。今日は「閉店」の話を続けましょう。
1. クロージャとは何ですか?定義: 別の関数のスコープ内の変数にアクセスできる関数です
関数の例から始めましょう:
例 1:
function fun() { var a = "张三"; } fun();//在我们执行完后,变量a就被标记为销毁了
function fun() { var a = "张三"; return function () { alert("test"); } } var f = fun();//同样,在我们执行完后,变量a就被标记为销毁了
function fun() { var a = "张三"; return function () { alert(a); } } var f = fun();//【现在情况发生变化了,如果a被销毁,显然f被调用的话就不能访问到变量a的值了】 f();//【然后变量a的值正常的被访问到了】 //这就是闭包,当函数A 返回的函数B 里面使用到了函数A的变量,那么函数B就使用了闭包。 示例: function fun() { var a = "张三"; return function () { alert(a); } } var f = fun();//【现在情况发生变化了,如果a被销毁,显然f被调用的话就不能访问到变量a的值了】 f();//【然后变量a的值正常的被访问到了】
図: (スコープ チェーンを理解していない学生は、まず前回の記事「スコープとスコープ チェーン」を読んでください)
2. 匿名関数とは何ですか? (概念の説明のみ)
例: (つまり、名前のない関数)
オブジェクト内の関数の戻り値が匿名関数の場合の奇妙な現象について
説明する前に、まず頭を整理して、読みながらさらに混乱しないようにしてください。混乱している場合は、次の内容は無視してください。
var name1 = "张三"; var obj = { name1: "李四", fun2: function () { alert(this.name1); }, fun3: function () { return function () { alert(this.name1); } } }
//本当に混乱しています。これは全体的な状況を示していますか?
前に「どのオブジェクトがメソッドをクリックしても、これがオブジェクトである」と述べましたが、obj.fun3()() は「Zhang San」を出力します。これは、グローバル スコープを実行することを意味します。
以下の例を見るとその理由がわかるかもしれません。
obj.fun3()() を分解してみましょう。まず、obj.fun3() はウィンドウ スコープに匿名関数を返し、これを呼び出してウィンドウを指します。 (ちょっと強引な説明になっている気がしますし、正しいかどうかわかりませんが、今のところはこんな感じです)
クロージャ形成の原因: メモリ解放の問題
一般に、関数が実行されると、ローカルのアクティブ オブジェクトは破棄され、グローバル スコープのみがメモリに保存されますが、クロージャの場合は状況が異なります。
クロージャのアクティブオブジェクトは引き続きメモリに格納されるため、上記の例では、関数呼び出しが戻った後、変数 i はアクティブオブジェクトに属します。これは、そのスタック領域が解放されていないことを意味しますが、 c() を呼び出します。 i 変数によって保存されたスコープ チェーンがグローバルに b()->a()-> になると、スコープ var i 宣言が検索され、次に var i=1; が見つかります。クロージャ ++i; 結果、最終出力値は 2 です。
上記は、編集者が共有した JavaScript の基本パート 6 の関数式クロージャです。気に入っていただければ幸いです。