javascript関数とスコープ(クローズド、スコープ)(7)
13605 ワード
一、閉め切ってください
クローズドとは、別の関数のスコープにアクセスする変数の関数です.
特典:フレキシブルで、パッケージ可能
短所:空間の浪費、メモリの漏洩、性能の消耗
1、簡単な例
一般関数が実行された後、局所変数が解放されます.閉込められていると、局所変数は関数で解放されません.
outerを呼び出して匿名関数に戻りますが、この匿名関数は依然として外部outerのローカル変数localValにアクセスできますので、outer実行が完了したらlocalValはリリースされません.
outer()の呼び出しが終了し、func()を再起動した時も外層のouter()という外関数の局所変数にアクセスできます.このような場合は通常のようにクローズドと言います.
2、フロントエンドのクローズド
イベントの中をクリックして外層の局部変数を使って、データの転送に閉じ込めることができます.
期待の結果:aaaをクリックして1を弾いて、bbをクリックして2を弾いて、cccをクリックして3を弾いてください.
addEventListenerの中にはバック関数があります.クリックすると、このコールバック関数はiの値を動的に取得します.全体の初期化が完了したら、iの値はもう4です.
正しいやり方:
ループのたびに直ちに実行される匿名関数で包装し、ループのたびにiの値を匿名関数に送り、匿名関数の中からiを参照する.このようにすると、alertの関数iをクリックするたびに、クローズド環境ごとのiを取ります.このiはサイクル毎の賦値iに由来しています.このようにすると、クリックして1,2,3をイジェクトする順番が実現できます.
(function(){}()には、外部から直接取得できないようにしたい変数(u)が定義されています.userId,_typeIdは最後にwindow.export=exportを通じて最後に出力したい対象を出力します.
関数の外ではmyExport.uを通過できません.userIdが直接変数にアクセスしても、変数を書き換えることはできません.
二、スコープ
1、グローバル\関数\evalスコープ
簡単です.時には誤解を招くこともあります.どのような作用領域がありますか?大域、関数、およびeval作用領域.
2、作用ドメインチェーン
クローズドアウトouter 1ではフリー変数local 2にもアクセスできます.global 3にもアクセスできます.
モジュール化されたツールがない場合、多くの種類のライブラリやコードの最外層をよく見ます.匿名関数は以下の通りです.
使えまたは+の目的は、関数宣言ではなく関数を関数式にすることです.省略すれば完全な文をFunctionで始まると、関数宣言として解釈され、前処理されてしまい、最後に括弧または関数宣言のペアを残して名前を省略すると文法エラーが発生します.
三、ES 3実行コンテキスト(オプション)
毎回関数が呼び出される時、実行環境があります.
抽象概念:コンテキスト、変数オブジェクトを実行します.
1、実行コンテキスト
同じスタックの概念です.
関数を1万回呼び出すと、1万個のExecution contextがコンテキストを実行します.
2、変数オブジェクト
JavaScript解釈器はどうやって私達が定義した関数と変数を見つけますか?
抽象名詞を導入する必要があります.変数オブジェクト.
変数オブジェクト(Varable Object、VOと略す)は、実行コンテキストにおいて1、変数2、関数宣言3、関数パラメータを記憶する抽象的概念の「オブジェクト」である.
すなわち
例:たとえば、JavaScriptコードの一部があります.
3、グローバル実行コンテキスト(ブラウザ)
JavaScriptの最初の行でMath、String、isNaNなどの方法を呼び出すことができます.ブラウザでもwindowを得ることができます.なぜですか?
グローバルスコープでは、背後に変数オブジェクトVO(global Context)があるからです.
最初の行のコードが実行される前に、ブラウザjsエンジンはいくつかの大域的なものをVOに初期化します.例えば、[global]の中にはMath方法、Stringオブジェクト、isNaN関数などがあります.windowはこの全体的なオブジェクト自体を指します.
VOオブジェクトは標準的な抽象的な概念であり、javascript言語自体に対応しています.見えないものです.直接に訪問することはできません.
例えば関数オブジェクトのVOは手に入れることができません.しかし、ブラウザの中にはグローバルなwindowがあります.自分を指すので、コンソールの中でwindow.window.window.window.windowを使って、ずっと嵌め込んでいくことができます.これは無限ループであることを証明できます.
String(10)の背後には対応するVOオブジェクト、つまり[global]オブジェクトにアクセスし、[global]オブジェクトの属性Stringを得る.
4、関数内のアクティブなオブジェクト
関数はちょっと特殊なところがありますが、関数にはもう一つの概念をアクティブオブジェクトといいます.
関数は実行時にAOアクティブオブジェクトにアーグメンントを置きます.
augumentsを初期化したら、このAOオブジェクトはVOオブジェクトと呼ばれます.
グローバルVOと同様に、初期化関数のイメージ、初期化変数の宣言、または関数の宣言など、他のいくつかの初期化が行われます.
4.1、変数初期化フェーズ
目的は主に理解点です.なぜ関数と変数の宣言が前置きされますか?なぜ匿名関数の表式の名前が外部で呼び出されないのですか?
関数オブジェクトのVOについては2段階に分けられ、最初の段階は変数初期化段階である.
上に述べたように、グローバルスコープでVO変数初期化はMath、Stringなどの大域的なものを入れます.第二段階でコードをよりよく実行することができます.
関数の変数初期化段階では、argmentsを初期化し、変数宣言と関数宣言を入れます.
具体的な操作:
上のように、var e=function_e(){}雄になるeはAOには入れません.これもなぜ外を通過できないのですか?e関数オブジェクトを取得します.
関数変数初期化の段階では、関数宣言dをAOに置いています.これは、関数宣言が事前に行われる理由を説明しています.
関数宣言の衝突は上書きされます.変数の衝突は無視されます.
4.2コードの実行段階
このコード:
第一段階:変数初期化フェーズAOは以下の通りです.
第二段階:コード実行段階
手に入れる
5、テストしてみます
この文章の著者starofは、知識自体が変化しているため、作者も成長を続けています.文章の内容も定期的に更新されていません.読者を誤解させないように、根源を追求しやすく、出所を転載してください.http://www.cnblogs.com/starof/p/4904929.html問題があれば、私と討論して、共に進歩します.
クローズドとは、別の関数のスコープにアクセスする変数の関数です.
特典:フレキシブルで、パッケージ可能
短所:空間の浪費、メモリの漏洩、性能の消耗
1、簡単な例
一般関数が実行された後、局所変数が解放されます.閉込められていると、局所変数は関数で解放されません.
outerを呼び出して匿名関数に戻りますが、この匿名関数は依然として外部outerのローカル変数localValにアクセスできますので、outer実行が完了したらlocalValはリリースされません.
outer()の呼び出しが終了し、func()を再起動した時も外層のouter()という外関数の局所変数にアクセスできます.このような場合は通常のようにクローズドと言います.
2、フロントエンドのクローズド
イベントの中をクリックして外層の局部変数を使って、データの転送に閉じ込めることができます.
!function(){
var localData="localData here";
document.addEventListener('click',
function(){
console.log(localData);
});
}();
非同期要求は、$ajax()の方法で、successフィードバックにおいて、外層のこれらの変数を使用する.フロントエンドプログラミングでは、しばしば直接または間接的に、意図的または意図的には、クローズドを使用します.!function(){
var localData="localData here";
var url="http://www.baidu.com";
$.ajax({
url:url,
success:function(){
//do sth
console.log(localData);
}
});
}();
3、よくあるエラー—サイクルクローズ期待の結果:aaaをクリックして1を弾いて、bbをクリックして2を弾いて、cccをクリックして3を弾いてください.
<span style="color:#000000;">
document.body.innerHTML</span>="<div id=div1>aaa</div>"+"<div id=div2>bbb</div><div id=div3>ccc</div>"<span style="color:#000000;">;
</span><span style="color:#0000ff;">for</span>(<span style="color:#0000ff;">var</span> i=1;i<4;i++<span style="color:#000000;">){
document.getElementById(</span>'div'+i).addEventListener('click',<span style="color:#0000ff;">function</span><span style="color:#000000;">(){
alert(i);</span><span style="color:#008000;">//</span><span style="color:#008000;">all are 4!!!</span>
<span style="color:#000000;"> });
}
</span>
このコードが実行されたら、どれをクリックしても、ポップアップは常に4です.addEventListenerの中にはバック関数があります.クリックすると、このコールバック関数はiの値を動的に取得します.全体の初期化が完了したら、iの値はもう4です.
正しいやり方:
ループのたびに直ちに実行される匿名関数で包装し、ループのたびにiの値を匿名関数に送り、匿名関数の中からiを参照する.このようにすると、alertの関数iをクリックするたびに、クローズド環境ごとのiを取ります.このiはサイクル毎の賦値iに由来しています.このようにすると、クリックして1,2,3をイジェクトする順番が実現できます.
document.body.innerHTML="aaa"+"bbbccc";
for(var i=1;i<4;i++){
!function(i){
document.getElementById('div'+i).addEventListener('click',function(){
alert(i);//all are 4!!!
});
}(i);
}
4、包装のメリット—パッケージ(function(){}()には、外部から直接取得できないようにしたい変数(u)が定義されています.userId,_typeIdは最後にwindow.export=exportを通じて最後に出力したい対象を出力します.
<span style="color:#000000;">
(</span><span style="color:#0000ff;">function</span><span style="color:#000000;">(){
</span><span style="color:#0000ff;">var</span> _userId=23492<span style="color:#000000;">;
</span><span style="color:#0000ff;">var</span> _typeId='item'<span style="color:#000000;">;
</span><span style="color:#0000ff;">var</span> myExport=<span style="color:#000000;">{};
</span><span style="color:#0000ff;">function</span><span style="color:#000000;"> converter(userId){
</span><span style="color:#0000ff;">return</span> +<span style="color:#000000;">userId;
}
myExport.getUserId</span>=<span style="color:#0000ff;">function</span><span style="color:#000000;">(){
</span><span style="color:#0000ff;">return</span><span style="color:#000000;"> converter(_userId);
}
myExport.getTypeId</span>=<span style="color:#0000ff;">function</span><span style="color:#000000;">(){
</span><span style="color:#0000ff;">return</span><span style="color:#000000;"> _typeId;
}
window.myExport</span>=<span style="color:#000000;">myExport;
})();
console.log(myExport.getUserId()); </span><span style="color:#008000;">//</span><span style="color:#008000;">23492</span>
console.log(myExport.getTypeId()); <span style="color:#008000;">//</span><span style="color:#008000;">item</span>
console.log(myExport._userId);<span style="color:#008000;">//</span><span style="color:#008000;">undefined</span>
console.log(myExport._typeId);<span style="color:#008000;">//</span><span style="color:#008000;">undefined</span>
console.log(myExport.converter);<span style="color:#008000;">//</span><span style="color:#008000;">undefined</span>
exportオブジェクト上のgetUserId()メソッドを外部に使用する人には、exportで提供される方法だけで、特定の関数内の変数に間接的にアクセスすることができます.クローズドの特性を利用して、getUserIdは関数の実行が完了しても内部の自由変化量にアクセスできます.関数の外ではmyExport.uを通過できません.userIdが直接変数にアクセスしても、変数を書き換えることはできません.
二、スコープ
1、グローバル\関数\evalスコープ
簡単です.時には誤解を招くこともあります.どのような作用領域がありますか?大域、関数、およびeval作用領域.
2、作用ドメインチェーン
クローズドアウトouter 1ではフリー変数local 2にもアクセスできます.global 3にもアクセスできます.
function outer2(){
var local2=1;
function outer1(){
var local1=1;
// local1,local2 or global3
console.log(local1+','+local2+','+global3);
}
outer1();
}
var global3=1;
outer2();//1,1,1
3、関数を利用したドメインパッケージモジュール化されたツールがない場合、多くの種類のライブラリやコードの最外層をよく見ます.匿名関数は以下の通りです.
(function(){
//do sth here
var a,b;
})();
または!function(){
//do sth here
var a,b;
}();
または+function(){
//do sth here
var a,b;
}();
利点:関数内部の変数を、大域変数ではなく関数の局所変数に変え、大域変数と他のコードやクラスの衝突を防ぐ.使えまたは+の目的は、関数宣言ではなく関数を関数式にすることです.省略すれば完全な文をFunctionで始まると、関数宣言として解釈され、前処理されてしまい、最後に括弧または関数宣言のペアを残して名前を省略すると文法エラーが発生します.
三、ES 3実行コンテキスト(オプション)
毎回関数が呼び出される時、実行環境があります.
抽象概念:コンテキスト、変数オブジェクトを実行します.
1、実行コンテキスト
同じスタックの概念です.
関数を1万回呼び出すと、1万個のExecution contextがコンテキストを実行します.
console.log('EC0');
function funcEC1(){
console.log('EC1');
var funcEC2=function(){
console.log('EC2');
var funcEC3=function(){
console.log('EC3');
}
funcEC3();
}
funcEC2();
}
funcEC1();
//EC0 EC1 EC2 EC3
制御権はEC 0からEC 1まで、EC 2からEC 3まで、EC 3が実行された後、制御権はEC 2に戻り、EC 2が実行された後、制御権はEC 1に戻り、EC 1が実行されたらEC 0に戻ります.2、変数オブジェクト
JavaScript解釈器はどうやって私達が定義した関数と変数を見つけますか?
抽象名詞を導入する必要があります.変数オブジェクト.
変数オブジェクト(Varable Object、VOと略す)は、実行コンテキストにおいて1、変数2、関数宣言3、関数パラメータを記憶する抽象的概念の「オブジェクト」である.
すなわち
例:たとえば、JavaScriptコードの一部があります.
var a=10;
function test(x){
var b=20;
}
test(30);
大域作用域のVOはwindowに等しく、thisに等しい.3、グローバル実行コンテキスト(ブラウザ)
JavaScriptの最初の行でMath、String、isNaNなどの方法を呼び出すことができます.ブラウザでもwindowを得ることができます.なぜですか?
グローバルスコープでは、背後に変数オブジェクトVO(global Context)があるからです.
最初の行のコードが実行される前に、ブラウザjsエンジンはいくつかの大域的なものをVOに初期化します.例えば、[global]の中にはMath方法、Stringオブジェクト、isNaN関数などがあります.windowはこの全体的なオブジェクト自体を指します.
VOオブジェクトは標準的な抽象的な概念であり、javascript言語自体に対応しています.見えないものです.直接に訪問することはできません.
例えば関数オブジェクトのVOは手に入れることができません.しかし、ブラウザの中にはグローバルなwindowがあります.自分を指すので、コンソールの中でwindow.window.window.window.windowを使って、ずっと嵌め込んでいくことができます.これは無限ループであることを証明できます.
String(10)の背後には対応するVOオブジェクト、つまり[global]オブジェクトにアクセスし、[global]オブジェクトの属性Stringを得る.
4、関数内のアクティブなオブジェクト
関数はちょっと特殊なところがありますが、関数にはもう一つの概念をアクティブオブジェクトといいます.
関数は実行時にAOアクティブオブジェクトにアーグメンントを置きます.
augumentsを初期化したら、このAOオブジェクトはVOオブジェクトと呼ばれます.
グローバルVOと同様に、初期化関数のイメージ、初期化変数の宣言、または関数の宣言など、他のいくつかの初期化が行われます.
4.1、変数初期化フェーズ
目的は主に理解点です.なぜ関数と変数の宣言が前置きされますか?なぜ匿名関数の表式の名前が外部で呼び出されないのですか?
関数オブジェクトのVOについては2段階に分けられ、最初の段階は変数初期化段階である.
上に述べたように、グローバルスコープでVO変数初期化はMath、Stringなどの大域的なものを入れます.第二段階でコードをよりよく実行することができます.
関数の変数初期化段階では、argmentsを初期化し、変数宣言と関数宣言を入れます.
具体的な操作:
VO :
1、 ( , undefined)
2、 ( , )
3、 ( undefined, , )
注意点:関数式はVOに影響しません. 上のように、var e=function_e(){}雄になるeはAOには入れません.これもなぜ外を通過できないのですか?e関数オブジェクトを取得します.
関数変数初期化の段階では、関数宣言dをAOに置いています.これは、関数宣言が事前に行われる理由を説明しています.
関数宣言の衝突は上書きされます.変数の衝突は無視されます.
4.2コードの実行段階
このコード:
第一段階:変数初期化フェーズAOは以下の通りです.
第二段階:コード実行段階
手に入れる
5、テストしてみます
<span style="color:#000000;">
console.log(x); </span><span style="color:#008000;">//</span><span style="color:#008000;">function x(){}</span>
<span style="color:#0000ff;">var</span> x=10<span style="color:#000000;">;
console.log(x);</span><span style="color:#008000;">//</span><span style="color:#008000;">10</span>
x=20<span style="color:#000000;">;
</span><span style="color:#0000ff;">function</span><span style="color:#000000;"> x(){}
console.log(x); </span><span style="color:#008000;">//</span><span style="color:#008000;">20</span>
<span style="color:#0000ff;">if</span>(<span style="color:#0000ff;">true</span><span style="color:#000000;">){
</span><span style="color:#0000ff;">var</span> a=1<span style="color:#000000;">;
}</span><span style="color:#0000ff;">else</span><span style="color:#000000;">{
</span><span style="color:#0000ff;">var</span> b=<span style="color:#0000ff;">true</span><span style="color:#000000;">;
}
console.log(a); </span><span style="color:#008000;">//</span><span style="color:#008000;">1</span>
console.log(b); <span style="color:#008000;">//</span><span style="color:#008000;">undefined</span>
この文章の著者starofは、知識自体が変化しているため、作者も成長を続けています.文章の内容も定期的に更新されていません.読者を誤解させないように、根源を追求しやすく、出所を転載してください.http://www.cnblogs.com/starof/p/4904929.html問題があれば、私と討論して、共に進歩します.