ホームページ > 記事 > ウェブフロントエンド > JavaScript 変数のスコープとクロージャーに関する簡単な説明
クロージャに関連する概念: 変数のスコープと変数の有効期間。次の記事では、JavaScript における変数のスコープとクロージャについて紹介します。一定の参考値があります。困っている友人は参照してください。皆様のお役に立てれば幸いです。
1. 変数のスコープ
1. 変数のスコープは、変数の有効範囲。変数定義の場所と密接に関連しています。スコープは空間的な観点から変数を記述し、変数の可視性としても理解できます。変数は特定のスコープ内で可視、つまり使用可能です。 [関連コースの推奨事項: JavaScript ビデオ チュートリアル ]
2. さまざまなスコープに応じて、変数はグローバル変数とローカル変数に分類できます。
##● グローバル変数: グローバル環境で宣言された変数##● ローカル変数: 関数で宣言された変数
##● 関数が実行されると、クロージャが作成されます。関数のコンテキストでは、関数内で宣言された変数は関数内でのみ使用でき、外部からはアクセスできませんが、グローバル変数はどこでも使用できます3. 関数内で var キーワードを使用すると、宣言された変数を表示する ローカル変数; var キーワードを使用する代わりに、直接代入によって宣言された変数はグローバル変数var m=8; function f1(){ var a1=10; console.log(m); //8 } function f2(){ var a2=20; console.log(m); //8 } f1(); f2();4. カプセル化機能を実装するには変数スコープに依存します (1) を使用しますES6 提供されている let
(2) は、関数
var myObject=(function(){ var _name="yian"; //私有变量 return { getName:function(){ //公有方法 return _name; } } })(); console.log(myObject._name); //undefined console.log(myObject.getName()); //yian
のライフ サイクル1. グローバル変数の場合、グローバル変数がアクティブに破棄されない限り、そのライフサイクルは永続的です;
2. 関数内で var キーワードを使用して宣言されたローカル変数の場合、関数が終了すると、これらのローカル変数は、それらは値を失い、関数呼び出しの終了時に破棄されます3. ブロックレベルのスコープの模倣(1) ブロックレベルのスコープで匿名関数として使用されます: A関数宣言は 1 組のかっこで囲まれており、それが実際には関数式であることを示し、それに続く別のかっこ 1 組が関数をすぐに呼び出します。(function(){ //这里是块级作用域 })();匿名関数で定義された変数は実行終了時に破棄されます(2) まず関数を定義してから呼び出します。関数を定義するには、匿名関数を作成してその匿名関数を変数に割り当てます。また、関数を呼び出すには、関数名の後に 1 組のかっこを追加します。
var someFunction=function(){ //这里是块级作用域 }; someFunction();
古典的な質問:
var nodes=document.getElementsByTagName("div"); for(var i= 0,len=nodes.length;i<len;i++){ nodes[i].onclick=function(){ console.log(i); //无论点击哪个div,最后弹出的结果都是5 } }説明: div ノードの onclick イベントは非同期でトリガーされます。イベントがトリガーされると、forループ かなり前に終了しました。この時点で私はすでに 5
解決策:
方法 1: ES6 で let を使用します
方法2: クロージャ内 1) カプセル化された変数の助けを借りて: クロージャは、グローバルに公開する必要のないいくつかの変数を「プライベート変数」としてカプセル化できます。プライベート変数には、関数パラメータ、ローカル変数、および関数内で定義されたその他の関数が含まれます。 例: mult 関数は数値型パラメータを受け取り、これらのパラメータの積を返します。 初期コード:var nodes=document.getElementsByTagName("div"); for(var i= 0,len=nodes.length;i<len;i++){ (function(x){ nodes[i].onclick=function(){ console.log(x); } })(i); }クロージャ使用後:
var cache={ }; var mult=function(){ var args=Array.prototype.join.call(arguments,","); if(cache[args]){ return cache[args]; } var a=1; for(var i=0,len=arguments.length;i<len;i++){ a=a*arguments[i]; } return cache[args]=a; }; console.log(mult(1,2,3)); //6 console.log(mult(3,4,5)); //60補足: in は、属性がオブジェクトに属していると判断します
var mult=(function(){ var cache={ }; //加入缓存机制,避免相同参数的计算 var calculate=function(){ var a=1; for(var i= 0,len=arguments.length;i<len;i++){ a=a*arguments[i]; } return a; }; return function(){ var args=Array.prototype.join.call(arguments,","); if(args in cache){ return cache[args]; } return cache[args]=calculate.apply(null,arguments); } })();(2) ローカル変数の寿命を延ばす例: レポート データを使用してレポートを作成すると、約 30% のデータが失われますその理由は、レポート内で img レポート関数の呼び出しが終了すると、img ローカル変数がすぐに破棄されるためです初期コード:
var mycar = {make: "Honda", model: "Accord", year: 1998}; if ( "make" in mycar ){ //属性名必须是字符串形式,因为make不是一个变量 document.write('true'); // 显示true } else{ document.write('false'); }クロージャを使用した後 (img 変数を次のようにカプセル化します)クロージャ):
var report=function(src){ var image=new Image(); image.src=src; };5. クロージャとオブジェクト指向の設計 クロージャの書き方:
var report=(function(){ var imgs=[ ]; return function(){ var image=new Image(); imgs.push(image); image.src=src; } })();オブジェクト指向の書き方 1:
var extent=function(){ var value=0; return { call:function(){ value++; console.log(value); } } } var extent=extent(); extent.call(); //1 extent.call(); //2オブジェクト指向の記述方法 2:
var extend={ value:0, call:function(){ this.value++; console.log(this.value); } }; extend.call(); //1 extend.call(); //26. クロージャとメモリ管理# 関数の終了時にローカル変数を逆参照する必要がありますが、ローカル変数が、クロージャを閉じると、ローカル変数は存続します。つまり、メモリ内に残ります。 # クロージャを使用すると循環参照の形成が容易になりますが、一部の DOM ノードがクロージャのスコープ チェーンに格納されている場合、メモリ リークが発生する可能性があります。 # 循環参照によって引き起こされるメモリ リークの問題を解決します。循環参照内の変数を null に設定します。 (変数を null に設定すると、変数とその変数が以前に参照していた値との間の接続が切断されます。次回ガベージ コレクターが実行されるときに、これらの値は削除され、それらが占有しているメモリは再利用されます) 7、機能: # 関数の入れ子関数; # 外部パラメーターと変数は関数内で参照できます;
##● パラメーターと変数は、関数によってリサイクルされません。ガベージコレクションメカニズム。
8. 利点: グローバル変数の汚染を回避します
9、缺点:会常驻内存,增加内存的使用量,使用不当会造成内存泄漏;闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存
10、创建闭包
写法一:
function a() { var b=123; function c(){ console.log(b+=1); } return c; } var d=a(); d();
方式二:
function f1(){ var num=10; //函数执行完毕,变量仍然存在 var f2=function(){ num++; console.log(num); //11 }; return f2; } var res=f1(); res();
● 解释:执行f1()后,f1()闭包内部的变量会存在,而闭包内部函数的内部变量不会存在,使得JavaScript的垃圾回收机制不会收回f1()占用的资源,因为f1()中内部函数的执行需要依赖f1()中的变量。
方式三:
function foo(x) { var tmp = 3; return function f2(y) { alert(x + y + (++tmp)); //17 }; } var bar = foo(3); //bar现在是一个闭包 bar(10);
练习题:
function f1(){ var a=1; t=function(){ a++; } return function(){ console.log(a); } } var b=f1(); //返回值为一个匿名函数 b(); //1 t(); b(); //2
声明变量,若变量名称相同,就近原则:
var name="g"; function out(){ var name="loc"; function foo(){ console.log(name); } foo(); } out(); //name=loc
补充知识点:
1、JS中有哪些垃圾回收机制?
(1)引用计数:跟踪记录每个值被使用的次数。
● 当声明一个变量并将一个引用类型赋值给该变量时,该值的引用次数加1;
● 若该变量的值变为另一个,则该值引用次数减1;
● 若该值引用次数为0时,说明变量没有在使用,此值无法访问;
● 因此,可以将它占用的空间回收,垃圾回收机制会在运行时清理引用次数为0 的值所占用的空间。
● 在低版的IE中会发生内存泄漏,很多时候就是因为它采用引用计数得到方式进行垃圾回收(如果两个对象之间形成了循环引用,那么这两个对象都无法被回收)。
(2)标记清除:最常见的垃圾回收方式
● 当变量进入执行环境时,垃圾回收器将其标为“进入环境”,离开时标记为“离开环境”;
● 垃圾回收机制在运行时给存储在内存中的所有变量加上标记;
● 去掉环境中的变量及被环境中变量所引用的变量(闭包)的标记;
● 完成这些后仍存在的标记就是要删除的变量。
2、哪些操作会造成内存泄漏?
● 内存泄漏:指不再拥有或需要任何对象(数据)之后,它们仍然存在于内存中。
● 垃圾回收器定期扫描对象,并计算引用了每个对象的其他对象的数量。如果一个对象的引用数量为0(没有其他对象引用过该对象),或对该对象的唯一引用是循环的,那么该对象占用的内存立即被回收。
● 如果setTimeout的第一个参数使用字符串而非函数,会造成内存泄漏。
● 闭包、控制台日志、循环(在两个对象彼此引用且彼此保留时,就会产生一个循环)等会造成内存泄漏。
本文来自 js教程 栏目,欢迎学习!
以上がJavaScript 変数のスコープとクロージャーに関する簡単な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。