ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript クロージャーの徹底解説 (Closure)
クロージャ - どこでも
フロントエンド プログラミングでは、意図的または非意図的に、直接的または間接的にクロージャを使用することがよくあります。クロージャにより、データ転送をより柔軟にすることができます (一部のクリック イベントの処理など)
!function() { var localData = "localData here"; document.addEventListener('click', //处理点击事件时用到了外部局部变量,比如这里的localData function(){ console.log(localData); }); }();
別の例は次のとおりです: (とてもフレンドリーですね~~)
!function() { var localData = "localData here"; var url = "http://www.baidu.com/"; $.ajax({ url : url, success : function() { // do sth... console.log(localData); } }); }();
別の例を見てみましょう~~この状況は私たちが通常行うものですcall Closure
function outer() { var localVal = 30; return function(){ return localVal; } } var func = outer(); func(); // 30
この例では、outer() が呼び出され、無名関数 function() が返されます。この無名関数では、outer() の呼び出しが完了した後に、outer() のローカル変数 localVal にアクセスできます。 func() が再度呼び出されても、outer() のローカル変数 localVal にアクセスできます
クロージャの概念
通常の関数とは異なり、クロージャにより、関数は直接の字句の外で呼び出された場合でも非ローカル変数にアクセスできます範囲。 --Wikipedia
クロージャは、他の関数の内部変数を読み取ることができる関数です。 --Ruan Yifeng
JavaScript 言語では、関数内のサブ関数のみがローカル変数を読み取ることができるため、クロージャーは単に「関数内で定義された関数」として理解できます。
つまり、本質的に、クロージャは関数の内部と関数の外部をつなぐ橋です
クロージャの使い方
この部分はこのブログ投稿からの転載です
クロージャはさまざまな場所で使用できます。その最大の用途は 2 つあり、1 つは前述したように関数内の変数を読み取ること、もう 1 つはこれらの変数の値をメモリに保持することです。
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
このコードでは、result は実際にはクロージャ f2 関数です。これは 2 回実行され、1 回目の値は 999、2 回目の値は 1000 でした。これは、関数 f1 のローカル変数 n が常にメモリに格納され、f1 が呼び出された後に自動的にクリアされないことを証明します。
なぜこれが起こっているのですか?その理由は、f1 が f2 の親関数であり、f2 がグローバル変数に割り当てられているため、f2 は常にメモリ内に存在し、f2 の存在は f1 に依存するため、f1 は常にメモリ内にあり、削除されないためです。呼び出しが完了すると、ガベージ コレクション メカニズム (ガベージ コレクション) によってリサイクルされます。
このコードでもう 1 つ注目すべき点は、「nAdd=function(){n+=1}」という行です。まず、var キーワードが nAdd の前に使用されていないため、nAdd はローカル変数ではなくグローバル変数です。 。次に、nAdd の値は匿名関数であり、匿名関数自体もクロージャであるため、nAdd は関数の外部で関数内のローカル変数を操作できるセッターと同等です。
クロージャーのカプセル化
(function() { var _userId = 23492; var _typeId = 'item'; var export = {}; function converter(userId) { return +userId; } export.getUserId = function() { return converter(_userId); } export.getTypeId = function() { return _typeId; } window.export = export; //通过此方式输出 }()); export.getUserId(); // 23492 export.getTypeId(); // item export._userId; // undefined export._typeId; // undefined export.converter; // undefined
クロージャーの特性を使用すると、いくつかの複雑な関数ロジックをカプセル化できます。この例では、export のメソッド (getUserId、getTypeId) を呼び出すと関数内のプライベート変数に間接的にアクセスしますが、export は直接呼び出します。 _userId は _userId を取得できません。これは Node でよく使用される機能でもあります~
よくある間違い: ループ クロージャ
次のケースでは、値 aaa、bbb、ccc を持つ 3 つの div を追加します。私たちが実現したいのは、aaa をクリックして、出力 1、bbb をクリック 出力 2、ccc をクリックして出力 3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div><div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { document.getElementById('div' + i). addEventListener('click', function() { alert(i); // all are 4! }); }
その結果、aaa、bbb、または ccc をクリックするとすべてalert(4)~~
問題は、i の値がすでに初期化が完了したら 4 を実行します
目的を達成するには、aaa をクリックして 1 を出力し、bbb をクリックして出力 2 を、ccc をクリックして 3 を出力します。各ループで、クロージャー手法を使用して、匿名でラップする必要があります。このようにすると、alert(i) の値は毎回クロージャ環境の i から取得され、各ループでの i の代入から取得され、1、2 が出力されます。 、 3
document.body.innerHTML = "<div id=div1>aaa</div>" + "<div id=div2>bbb</div>" + "<div id=div3>ccc</div>"; for (var i = 1; i < 4; i++) { !function(i){ //②再用这个参数i,到getElementById()中引用 document.getElementById('div' + i). addEventListener('click', function() { alert(i); // 1,2,3 }); }(i); //①把遍历的1,2,3的值传到匿名函数里面 }
考える質問
如果你能理解下面两段代码的运行结果,应该就算理解闭包的运行机制了。(来自阮老师)这题目总结得真秒~~
代码片段一。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; alert(object.getNameFunc()());
代码片段二。
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var that = this; return function(){ return that.name; }; } }; alert(object.getNameFunc()());