ホームページ > ウェブフロントエンド > jsチュートリアル > JavaScript クロージャーの概念についての理解を深めます。

JavaScript クロージャーの概念についての理解を深めます。

高洛峰
リリース: 2016-11-28 15:16:13
オリジナル
1174 人が閲覧しました

インターネット上で JavaScript クロージャ関連の情報をたくさんチェックしましたが、そのほとんどは非常に学術的で専門的なものでした。初心者にとっては、クロージャを理解することはおろか、テキストの説明すら理解することが困難です。この記事を書く目的は、最も一般的な言葉を使って JavaScript クロージャの本当の姿を明らかにすることです。

1. 閉鎖とは何ですか?

「公式」の説明は次のとおりです: クロージャは、多くの変数とこれらの変数にバインドされた環境を持つ式 (通常は関数) であり、したがって、これらの変数も式の一部です。 あまりにも学術的な記述のため、この文章を直接理解できる人は少ないと思います。実際、この文は平たく言えば、「JavaScript のすべての関数はクロージャである」という意味です。しかし、一般的に言えば、入れ子関数によって生成されたクロージャはより強力であり、ほとんどの場合、これが「クロージャ」と呼ばれるものです。次のコードを見てください:

function a() {
    var i = 0;
    function b() {
        alert(++i);
    }
    return b;
}
var c = a();
c();
ログイン後にコピー

このコードには 2 つの特徴があります:

関数 b は関数 a 内にネストされています;

関数 a は関数 b を返します。

このように、var c=a() を実行すると、変数 c は実際には関数 b を指します。変数 i は b で使用されます。c() を実行すると、i の値を表示するウィンドウが表示されます。時間は1)です。このコードは実際にクロージャを作成します。なぜでしょうか。関数 a の外側の変数 c は関数 a 内の関数 b を参照しているため、つまり、関数 a の内部関数 b が関数 a の外側の変数によって参照される場合、いわゆる「クロージャ」が作成されます。

もっと徹底しましょう。いわゆる「クロージャ」とは、コンストラクタ本体内に別の関数を対象オブジェクトのメソッド関数として定義し、そのオブジェクトのメソッド関数が外側の関数本体内の一時変数を参照することです。これにより、ターゲット オブジェクトが存続期間中常にそのメソッドを維持できる限り、元のコンストラクター本体で使用される一時変数の値を間接的に維持できます。最初のコンストラクター呼び出しが終了し、一時変数の名前が消えていますが、変数の値はターゲット オブジェクトのメソッド内で常に参照でき、このメソッドを介してのみ値にアクセスできます。同じコンストラクターが再度呼び出された場合でも、新しいオブジェクトとメソッドのみが生成され、新しい一時変数は最後の呼び出しから独立した新しい値にのみ対応します。

クロージャをより深く理解するために、クロージャの機能と効果を引き続き調べてみましょう。

2. クロージャの機能と効果は何ですか?

つまり、クロージャの機能は、a が実行されて返された後、a の内部関数 b の実行は変数に依存する必要があるため、JavaScript のガベージ コレクション メカニズム GC が a によって占有されているリソースを再利用するのをクロージャが阻止することです。これはクロージャの役割を非常に簡単に説明したもので、専門的でも厳密でもありませんが、必ず理解できます。クロージャを理解するには、段階的なプロセスが必要です。

上記の例では、クロージャの存在により、関数aが戻った後もaのiが常に存在します。このように、c()が実行されるたびに、iは1を加えた後に通知されるiの値になります。 。

次に、a が関数 b 以外の値を返す場合、状況はまったく異なります。 a が実行された後、b は a の外に戻されるのではなく、a によってのみ参照されるため、関数 a と関数 b は相互に参照しますが、干渉されません。外部の世界によって (外部の世界によって参照される)、関数 a と b は GC によってリサイクルされます。

3. クロージャのミクロの世界

クロージャと関数 a と入れ子関数 b の関係をより深く理解したい場合は、関数実行コンテキスト (実行コンテキスト)、アクティブ オブジェクトなどの他の概念を導入する必要があります。 (呼び出し) オブジェクト)、スコープ、スコープ チェーン。これらの概念を説明するために、関数 a の定義から実行までのプロセスを例として取り上げます。

関数 a を定義するとき、js インタープリターは関数 a のスコープ チェーンを、 a がグローバル関数の場合、スコープ チェーン内に存在する「環境」に設定します。

関数 a を実行すると、 a は対応する実行コンテキストに入ります。

実行環境を作成するプロセスでは、最初に a にscope属性が追加されます。これはaのスコープであり、その値はステップ1のスコープチェーンです。つまり、a.scope=a のスコープ チェーンです。

その後、実行環境は呼び出しオブジェクトを作成します。アクティブ オブジェクトもプロパティを持つオブジェクトですが、プロトタイプを持たないため、JavaScript コードから直接アクセスできません。アクティブ オブジェクトを作成した後、そのアクティブ オブジェクトを のスコープ チェーンの先頭に追加します。この時点で、a のスコープ チェーンには、a のアクティブ オブジェクトとウィンドウ オブジェクトの 2 つのオブジェクトが含まれています。

次のステップは、関数 a を呼び出すときに渡されるパラメーターを保持する、アクティブなオブジェクトに argument 属性を追加することです。

最后把所有函数a的形参和内部的函数b的引用也添加到a的活动对象上。在这一步中,完成了函数b的的定义,因此如同第3步,函数b的作用域链被设置为b所被定义的环境,即a的作用域。

到此,整个函数a从定义到执行的步骤就完成了。此时a返回函数b的引用给c,又函数b的作用域链包含了对函数a的活动对象的引用,也就是说b可以访问到a中定义的所有变量和函数。函数b被c引用,函数b又依赖函数a,因此函数a在返回后不会被GC回收。

当函数b执行的时候亦会像以上步骤一样。因此,执行时b的作用域链包含了3个对象:b的活动对象、a的活动对象和window对象

当在函数b中访问一个变量的时候,搜索顺序是:

先搜索自身的活动对象,如果存在则返回,如果不存在将继续搜索函数a的活动对象,依次查找,直到找到为止。

如果函数b存在prototype原型对象,则在查找完自身的活动对象后先查找自身的原型对象,再继续查找。这就是Javascript中的变量查找机制。

如果整个作用域链上都无法找到,则返回undefined。

小结,本段中提到了两个重要的词语:函数的定义与执行。文中提到函数的作用域是在定义函数时候就已经确定,而不是在执行的时候确定(参看步骤1和3)。用一段代码来说明这个问题:

function f(x) {
    var g = function () { return x; }
    return g;
}
var h = f(1);
alert(h());
ログイン後にコピー

这段代码中变量h指向了f中的那个匿名函数(由g返回)。

假设函数h的作用域是在执行alert(h())确定的,那么此时h的作用域链是:h的活动对象->alert的活动对象->window对象。

假设函数h的作用域是在定义时确定的,就是说h指向的那个匿名函数在定义的时候就已经确定了作用域。那么在执行的时候,h的作用域链为:h的活动对象->f的活动对象->window对象。

如果第一种假设成立,那输出值就是undefined;如果第二种假设成立,输出值则为1。运行结果证明了第2个假设是正确的,说明函数的作用域确实是在定义这个函数的时候就已经确定了。

4. 闭包的应用场景

保护函数内的变量安全。以最开始的例子为例,函数a中i只有函数b才能访问,而无法通过其他途径访问到,因此保护了i的安全性。

在内存中维持一个变量。依然如前例,由于闭包,函数a中i的一直存在于内存中,因此每次执行c(),都会给i自加1。

function Constructor(...) {
    var that = this;
    var membername = value;
    function membername(...) {...}
}
ログイン後にコピー

以上3点是闭包最基本的应用场景,很多经典案例都源于此。

5. JavaScript的垃圾回收机制

在Javascript中,如果一个对象不再被引用,那么这个对象就会被GC回收。如果两个对象互相引用,而不再被第3者所引用,那么这两个互相引用的对象也会被回收。因为函数a被b引用,b又被a外的c引用,这就是为什么函数a执行后不会被回收的原因。

6. 结语

理解JavaScript的闭包是迈向高级JS程序员的必经之路,理解了其解释和运行机制才能写出更为安全和优雅的代码。


関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート