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 変数は外部スコープではアクセスできません。グローバル) ですが、クロージャ (変数を含む関数を返す、ここでは getName 関数) を通じて、スコープを越えて変数にアクセスできます (外部から内部へのアクセス)。したがって、上記のステートメントは次のように完全に理解される必要があります:
クロージャーとは、スコープを越えて変数にアクセスすることを意味します。内部スコープは外部スコープの変数への参照を維持できるため、外部スコープは (さらに) の内部スコープ変数にアクセスできます。 (それでも理解できない場合は、次の分析を読んでください)
2. 「結論: お父さんはおじいちゃんの環境で処刑され、孫はお父さんの環境に戻されます。もともと、お父さんは処刑されており、お父さんの環境はをクリアする必要がありますが、孫が父親の環境を参照したため、父親はそれを解放できませんでした。簡単に言うと、クロージャは親の環境を参照して上位の環境に返すオブジェクトです。これわかりますか?まず以下のコードを見てください:
[例 3]
function user () { var name = 'wangxi' return name } var userName = user() console.log(userName) // wangxi
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 // 销毁闭包,释放内存
ポイント 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
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 サイトの他の関連記事を参照してください。