1. まず変数スコープチェーンを知る必要があります
変数のスコープには、グローバル変数とローカル変数の 2 種類があります。関数内で定義されていない変数はグローバル変数であり、関数内で定義されている変数はローカル変数です。関数内で変数を定義する場合は、var キーワードを使用しない変数がグローバル変数であることに注意してください。
JavaScript のすべてのコードには、関連付けられたスコープ チェーンがあります。このスコープ チェーンは、このコードの「スコープ」内の変数を定義するオブジェクトのリストまたはリンクされたリストです。トップレベルのコードのスコープはグローバル変数で構成されます。ネストされた関数を含まないスコープ チェーンには 2 つのオブジェクトがあります。1 つは定義された関数パラメーターとローカル変数のオブジェクトで、もう 1 つはグローバル変数オブジェクトです。ネストされた関数のスコープ チェーンには 3 つのオブジェクトがあります: 関数パラメーターとローカル変数 - 外部関数のパラメーターとローカル変数 - グローバル変数。関数はスコープ チェーン上のオブジェクトにアクセスできるため、関数はグローバル変数にアクセスできますが、その逆はできません。つまり、関数内のローカル変数には関数の外部からアクセスできません。
var a=1; function wai(){ alert(a); var m=10; n=20; } wai(); //=> 1; 函数内部可以访问全局变量 alert(m); //=> error; 外部访问函数局部变量报错 alert(n); //=> 20; 函数内部定义的变量未使用var关键字,所以是全局变量,外部可以访问
2. ローカル変数を外部から読み取る方法
場合によっては、関数内のローカル変数に外部からアクセスする必要があります。この場合は、回避策を使用する必要があります。 JavaScript の変数スコープの特性を利用して関数内にサブ関数を定義し、サブ関数は親関数
内の変数にアクセスできます。function wai(){ var m=10; function nei(){ alert(m); } return nei; } var f = wai(); nei(); //=> error; nei()函数是一个局部变量,在外部不能访问 f(); //=> 10;
3. 終了
上記のコードの nei() 関数はクロージャです。 上記からもわかるように、クロージャは関数内で定義された関数です。 、機能の内部と外部を接続するブリッジと考えることができます。
クロージャーには 2 つの機能があります:
まず、前述したように、関数内の変数を読み取ることができます
第 2 に、これらのローカル変数をメモリに保存して変数データの共有を実現できます
function wai(){ var m=99; function nei(){ alert(m); m++; } return nei; } var f= wai(); f(); //=> 99; f(); //=> 100; f(); //=> 101;
上記の例では、wai() 関数の実行中、変数 m の値は f() を実行することで読み取ることができますが、直接のalert(m) では読み取ることができません!
次の例に示すように、匿名関数が定義され、関数は渡されたパラメーターを匿名関数のローカル変数 i に追加し、Make することもできます。私はインクリメントします。
var wai=(function(){ var i=0; return function(num){ num+=i; alert(num); i++; } })(); wai(1);//1 wai(2);//3 wai(3);//5
クロージャをより深く理解するために、次の例を見てみましょう:
次に、配列を返す関数を定義したいと思います。配列の各要素は関数であり、各関数は対応するインデックス値をポップアップします
このように書くかもしれません
function box(){ var arr=[]; for(i=0;i<5;i++){ arr[i]=function(){return i;} } return arr; } var a=box(); alert(a); //=>包含五个函数体的数组 alert(a[0]()); //=> 5; alert(a[1]()); //=> 5;
上記のコードでは、a[0 を実行すると、すべてのポップアップが 0、1、2、3、4 ではなく 5 であることがわかります。これは、i もメモリ内に存在するローカル変数であるためです。 ]() この時点で、i の値はすでに 5 であり、box() 関数の実行プロセス全体を通じて i の値は増加し続けます。
解決策: クロージャの実装
function box(){ var arr=[]; for(var i=0;i<5;i++){ arr[i]=(function(num){ return function(){return num;} })(i); } return arr; } var arr=box(); for(var i=0;i<5;i++){ alert(arr[i]());//0,1,2,3,4 }
4. クロージャを使用する際の注意点
1) クロージャによって関数内の変数がメモリに格納され、大量のメモリが消費されるため、クロージャを悪用することはできません。悪用しないと、Web ページでパフォーマンスの問題が発生し、Web ページでメモリ リークが発生する可能性があります。いえ。解決策は、関数を終了する前に、未使用のローカル変数をすべて削除することです。
2) クロージャは、親関数内の変数の値を親関数の外で変更します。したがって、親関数をオブジェクトとして使用し、クロージャをそのパブリックメソッドとして使用し、内部変数をそのプライベート値として使用する場合は、親関数の変数値を自由に変更しないように注意する必要があります。
5. クロージャに関するいくつかの質問があります
次のコードの結果を理解できれば、クロージャの動作メカニズムを理解できるはずです。
JSコード
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; //=>嵌套函数的this为全局变量或undefined,不会继承父函数的this }; } }; alert(object.getNameFunc()()); //The Window
上記の出力が「The window」である理由は、ネストされた関数内の this が親関数 this を継承せず、その値がグローバル変数または未定義 (ECMAScript5 の下で) であるため、返されるのは名前であるためです。グローバルオブジェクトの変数。オブジェクトの name 属性を返したい場合、コードは次のとおりです:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ var cur=this; return function(){ return cur.name; }; } }; alert(object.getNameFunc()()); //=》My Object
上記のコードは、親関数オブジェクトの this を cur 変数に割り当て、そのネストされた関数は cur 変数を通じてそのプロパティにアクセスできます
----------------------------------------------- --- --------------------------------------------------- --- -----
JavaScript クロージャの例
function outerFun() { var a=0; function innerFun() { a++; alert(a); } } innerFun(); //=>error
上記のコードは間違っています。innerFun() のスコープは、outerFun() の内側にあり、outerFun() の外側で呼び出すのは間違っています。
は次のように変更され、クロージャーになります:
JSコード
function outerFun() { var a=0; function innerFun() { a++; alert(a); } return innerFun; //注意这里 } var obj=outerFun(); obj(); //结果为1 obj(); //结果为2 var obj2=outerFun(); obj2(); //结果为1 obj2(); //结果为2
什么是闭包:
当内部函数 在定义它的作用域 的外部 被引用时,就创建了该内部函数的闭包 ,如果内部函数引用了位于外部函数的变量,当外部函数调用完毕后,这些变量在内存不会被 释放,因为闭包需要它们.
--------------------------------------------------------------------------------------------------------
再来看一个例子
Js代码
function outerFun() { var a =0; alert(a); } var a=4; outerFun(); //=> 0 alert(a); //=> 4
结果是 0,4 . 因为在函数内部使用了var关键字 维护a的作用域在outFun()内部.
再看下面的代码:
Js代码
function outerFun() { //没有var a =0; alert(a); } var a=4; outerFun(); //=> 0 alert(a); //=> 0
结果为 0,0 真是奇怪,为什么呢?
作用域链是描述一种路径的术语,沿着该路径可以确定变量的值 .当执行a=0时,因为没有使用var关键字,因此赋值操作会沿着作用域链到var a=4; 并改变其值.