ホームページ >ウェブフロントエンド >jsチュートリアル >JavaScript 概要共有の閉鎖
この記事では、JavaScript に関する関連知識を提供し、主にクロージャとは何か、クロージャがこのように設計されている理由、使用方法など、クロージャに関連する問題を紹介します。以下の関連コンテンツをご覧ください。皆様のお役に立てれば幸いです。
[関連する推奨事項: JavaScript ビデオ チュートリアル 、Web フロントエンド ]
知識ポイントについて、どこから始めても、この知識ポイントを真に理解するには、3 つの質問を徹底的に理解し、その後、実際に実践して理解できるようにする必要があると私は常々信じています。それを主張すること、マスターすること。これら 3 つの質問は次のとおりです:
まず、クロージャとは何かという質問に答えてください。ほとんどの人は多くの関連記事を読んでいるはずですし、多くの人が独自の説明も行っているので、最初に私が理解している説明をします。 前提条件となる概念が 2 つあります。
クロージャは字句解析中に決定されるため、字句の範囲に関連します。
クロージャが存在するための前提条件は、プログラミング言語が第一級市民としての関数をサポートする必要があるため、関数に関連することになります。
最終的な結論は次のとおりです:
クロージャは最初に構造体であり、この構造体のコンポーネントは関数です。
クロージャは関数によって生成された構造
であり、関数は独自の字句スコープを宣言することを覚えています。 関数が生成する関数実行コンテキスト内のスコープ チェーンは親の字句スコープを保存するため、親変数オブジェクトはその存在によって参照されません。破棄され、使用するためにメモリ内に常駐します。この状況を閉鎖と呼びます。
JavaScript が非同期シングルスレッド 言語であるためです。非同期プログラミングの最大の問題は、関数を作成するときに、実際に呼び出される時刻がいつになるかわからないことです。
メモリ管理にとって大きな問題です。通常、同期的に実行されるコードの場合、関数が宣言されて呼び出されたときに必要なデータはメモリ内に残ります。アクセス可能です。非同期コードは、関数のコンテキストが破棄された可能性があることを宣言することがよくありますが、関数が呼び出されるまでに、必要な外部データがメモリ内で消去されている場合、これは大きな問題になります。
関数が以前に取得できたデータの範囲を記憶できるようにし、それらはすべてメモリに保存されるようにすることです。関数が再利用されない限り、メモリ、メモリ自体、およびメモリが記憶できる内容 どのスコープも破壊されません。
とデータ取得における不十分なメモリ管理
によって引き起こされる問題を 解決するために作成されます。
for (var i = 0; i < 3; i++) { setTimeout(function cb() { console.log(i); }, 1000); }基本的に、基本的なスキルを持つすべての人は、出力が 3 つ 3 つであることが一目でわかります。 その後、順番に出力するように変更します。通常は、var を次のように変更するだけです:
for (let i = 0; i < 3; i++) { setTimeout(function cb() { console.log(i); }, 1000); }このようにすると、出力は 0、1、2 になります。毎回ではなく同時に 1 秒に 1 回出力します。 では、問題はなぜでしょうか? 私が書いたことと同じかどうかを確認するために以下を読まなくても、最初に自分の説明を書いても構いません。 1. まず、変数 i が var である状況について説明します。 コードの実行が開始されると、実行コンテキストのスタックとメモリの状況は次のようになります。 グローバル オブジェクトの
変数 i とグローバル実行コンテキストの変数環境の
変数 i は同じ変数です。
ループ本体に入った後、i = 0 の場合:
次に、i = 1 の場合の状況に入ります:
最後に i = 2 の状況に入ります。これは基本的に i = 1 と同様です。
最後に i は i value 3 になり、サイクルは終了します。タイマー作業の開始:
当执行第一个定时器的回调函数时,创建了函数执行上下文,此时执行输出语句i时,会先从自己的词法环境里寻找变量i的值,也就是在 record环境记录里搜索,但是不存在。因而通过自己外部环境引用outer找到原先创建的块级作用域里 i = 0的情况, 输出了i值为0的结果。
对于之后的定时器也都是一样的情况,原先的块级作用域由于被回调函数所引用到了,因而就产生了闭包的情况,不会在内存中被销毁,而是一直留着。
等到它们都执行完毕后,最终内存回收会将之全部都销毁。
其实以上画的图并不是很严谨,与实际在内存中的表现肯定是有差异的,但是对于理解闭包在内存里的情况还是不影响的。
首先需要先明确一点,那就是在JavaScript中,只要创建了函数,其实就产生了闭包
。这是广义上的闭包,因为在全局作用域下声明的函数,也会记着全局作用域。而不是只有在函数内部声明的函数才叫做闭包。
通常意义上所讨论的闭包,是使用了闭包的特性
。
let a = 1function outer() { let a = 2 function inside() { a += 1 console.log(a) } return inside }const foo = outer()foo()
此处outer函数调用完时,返回了一个inside函数,在执行上下文栈中表示的既是outer函数执行上下文被销毁,但有一个返回值是一个函数。 该函数在内存中创建了一个空间,其[[scope]]指向着outer函数的作用域。因而outer函数的环境不会被销毁。
当foo函数开始调用时,调用的就是inside函数,所以它在执行时,先询问自身作用域是否存在变量a, 不存在则向上询问自己的父作用域outer,存在变量a且值为2,最终输出3。
var name = 'xavier'function foo() { var name = 'parker' function bar() { console.log(name) } console.log(name) return bar }function baz(fn) { var name = 'coin' fn() }baz(foo())baz(foo)
对于第一个baz函数调用,输出的结果为两个'parker'。 对于第二个baz函数的调用,输出为一个'parker'。
具体的理解其实跟上面一致,只要函数被其他函数调用,都会存在闭包。
闭包可以实现对于一些属性的隐藏,外部只能获取到属性,但是无法对属性进行操作。
function foo(name) { let _name = name return { get: function() { return _name } } }let obj = foo('xavier') obj.get()
对于一些需要存在状态的函数,都是使用到了闭包的特性。
// 节流function throttle(fn, timeout) { let timer = null return function (...arg) { if(timer) return timer = setTimeout(() => { fn.apply(this, arg) timer = null }, timeout) } }// 防抖function debounce(fn, timeout){ let timer = null return function(...arg){ clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, arg) }, timeout) } }
在没有模块之前,对于不同地方声明的变量,可能会产生冲突。而闭包能够创造出一个封闭的私有空间,为模块化提供了可能性。 可以使用IIFE+闭包实现模块。
var moduleA = (function (global, doc) { var methodA = function() {}; var dataA = {}; return { methodA: methodA, dataA: dataA }; })(this, document);
【相关推荐:JavaScript视频教程、web前端】
以上がJavaScript 概要共有の閉鎖の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。