JS のクロージャーについては誰もが聞いたことがあると思います。この記事は、サンプル コードを通じて JS のクロージャーを紹介します。興味のある方はぜひ参考にしてください。
1.「クロージャとは、スコープを越えて変数にアクセスすることです。」
【例1】
var name = 'wangxi' function user () { // var name = 'wangxi' function getName () { console.log(name) } getName() } user() // wangxi
getName関数で名前を取得するには、まずgetName関数のスコープ内で名前を検索しますが、見つかりません。 、そしてユーザー関数でスコープ内を検索しますが、これも見つかりませんでした。引き続き上方向にトレースすると、名前がグローバルスコープに存在することがわかり、名前の値を取得して出力します。ここで理解するのは簡単です。つまり、変数が指定されたスコープ内に存在する場合、現在のスコープ内で目的の変数が見つからない場合、同じ名前を持つ最初の変数が見つかるまで、スコープ チェーンを通じて親スコープ内で検索が続行されます。変数が見つかりました (見つからない場合は ReferenceError がスローされます)。これは JS のスコープ チェーンの概念です。つまり、子スコープはスコープ チェーンに従って親スコープの変数にアクセスできます。逆に、親スコープが子スコープの変数にアクセスしたい場合はどうなるでしょうか。 ——これは閉鎖によって達成する必要があります。
【例2】
function user () { var name = 'wangxi' return function getName () { return name } } var userName = user()() console.log(userName) // wangxi
コードを解析すると、nameはユーザー関数のスコープ内に存在するローカル変数であることが分かりますが、通常の状況では外部スコープ(ここではグローバル)ではname変数にアクセスできません。 Closure (変数を含む関数を返します。ここでは getName 関数) を介して、変数へのクロススコープ アクセス (外部から内部へのアクセス) が可能になります。したがって、上記のステートメントは次のように完全に理解される必要があります:
クロージャーとは、スコープを越えて変数にアクセスすることを意味します。内部スコープは外部スコープの変数への参照を維持できるため、外部スコープは (さらに) の内部スコープ変数にアクセスできます。 (それでも理解できない場合は、次の分析を読んでください)
2. 「結論: 父親は祖父の環境で処刑され、孫は父親の環境で返されました。本来、父親は処刑され、父親の環境はそうあるべきでした。」クリアできますが、孫は引用します。このため、お父さんはそれを解放できません。簡単に言うと、クロージャは親の環境を参照し、それを上位の環境に返すオブジェクトです。これをどう理解しますか?まず以下のコードを見てください:
[例 3]
function user () { var name = 'wangxi' return name } var userName = user() console.log(userName) // wangxi
Q: これはクロージャですか?
A: もちろん違います。まず、クロージャとは何かを理解する必要があります。ユーザー関数内のローカル変数名はグローバルスコープでアクセスしているように見えますが、問題はユーザー実行後に名前も破棄されてしまうこと、つまり関数内のローカル変数のライフサイクルがDuring中にしか存在しないことです。関数の宣言サイクルが完了すると、関数は破棄され、関数内の変数は自動的に破棄されます。
しかし、クロージャを使用するとその逆になります。関数が実行された後、ライフサイクルは終了しますが、クロージャによって参照される外側のスコープ内の変数はまだ存在し、クロージャを実行するスコープが破棄されるまで常に存在します。ローカル変数は破棄されます (クロージャがグローバル環境で参照されている場合、クロージャによって参照されるスコープは、プログラムの終了時やブラウザの閉じ時など、グローバル環境が破棄された場合にのみ破棄されます)。したがって、クロージャによるメモリ損失を避けるために、使用後にクロージャを手動で破棄することをお勧めします。上記の例 2 と同じですが、少し変更されています:
[例 4]
function user () { var name = 'wangxi' return function getName () { return name } } var userName = user()() // userName 变量中始终保持着对 name 的引用 console.log(userName) // wangxi userName = null // 销毁闭包,释放内存
[user()() に 2 つの括弧がある理由: user() を実行すると getName 関数が返されます。 name 変数を取得するには、次の操作を行う必要があります。返された getName 関数は 1 回実行されるため、 user()() になります】
ポイント 2 に従って、コードを分析します。userName 変数 (grandpa) がグローバル スコープで作成され、その最終的な戻り結果への参照が作成されます。 user()() を実行すると、ユーザー関数が保存され (つまり、ローカル変数名の値) (お父さん)、返される名前 (孫) は、user()() の実行後、ユーザーの環境 (お父さん) に戻されるはずです。はクリアされますが、返された結果の名前 (孫) は父親の環境を参照しているため (名前は元々ユーザーのスコープに存在するため)、ユーザーの環境は解放できません (メモリ損失が発生します)。
それで [「クロージャは、親環境を参照し、親環境から上位レベルの環境にオブジェクトを返すオブジェクトです。」] どう理解すればよいでしょうか?
別の言い方をしましょう: 関数が親環境のオブジェクトを参照し、この関数内でそのオブジェクトを上位レベルの環境に返す場合、この関数はクロージャです。
上記の例を見てください:
getName 関数は、ユーザー (親) 環境のオブジェクト (変数名) を参照し、関数内でその name 変数をグローバル環境 (上位環境) に返します。 getName クロージャです。
3. 「JavaScript の関数は、実行されるスコープではなく、定義されているスコープで実行されます。」
この文は、クロージャ内の変数への参照を理解するのに非常に役立ちます。次の例を見てみましょう:
var name = 'Schopenhauer' function getName () { console.log(name) } function myName () { var name = 'wangxi' getName() } myName() // Schopenhauer
myName() を実行した出力結果が想像したものと異なる場合は、上の文に戻って
JavaScript の関数が定義されているときに実行されることを確認する必要があります。実行されるスコープではなく、スコープ
执行 myName,函数内部执行了 getName,而 getName 是在全局环境下定义的,因此尽管在 myName 中定义了变量 name,对getName 的执行并无影响,getName 中打印的依然是全局作用域下的 name。
我们稍微改一下代码:
var name = 'Schopenhauer' function getName () { var name = 'Aristotle' var intro = function() { // 这是一个闭包 console.log('I am ' + name) } return intro } function showMyName () { var name = 'wangxi' var myName = getName() myName() } showMyName() // I am Aristotle
结果和你想象的一样吗?结果留作聪明的你自己分析~
以上就是对 js 中闭包的理解,如果有误,欢迎指正。最后引用一段知乎问题下关于闭包概念的一个回答。
什么是闭包?
简单来说,闭包是指可以访问另一个函数作用域变量的函数,一般是定义在外层函数中的内层函数。
为什么需要闭包?
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
特点
占用更多内存
不容易被释放
何时使用?
变量既想反复使用,又想避免全局污染
如何使用?
定义外层函数,封装被保护的局部变量。
定义内层函数,执行对外部函数变量的操作。
外层函数返回内层函数的对象,并且外层函数被调用,结果保存在一个全局的变量中。
相关推荐:
以上がJSにおけるクロージャの簡単な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。