JavaScript学習ノート(11)は閉じます.

6581 ワード

閉包とは何ですか?まず、「JavaScript権威の手引き」の定義を見てみます.
関数オブジェクトは、スコープチェーンを介して関連付けられます.関数の内部の変数は、関数のスコープ内に保存できます.このような特性は、クローズドと呼ばれます.
ははははははは読み終わって1面がぼんやりしているのではありませんか?大丈夫です.次は一番簡単な作用域、作用域チェーンから始まります.
1、スコープ
(1)関数スコープ
スコープは何ですか?一つの変数のスコープはソースコードにこの変数を定義する領域であり、JavaScriptには関数のスコープを採用しています.
つまり、変数は、それらの関数体およびこの関数体の入れ子を宣言する任意の関数の中に定義されています.
function scope() {
    if (true) {
        var i = 0
        for (var j = 1; j <= 5; j++) {
            i = i + j
        }
        console.log(j)
    }
    console.log(i)
    ~function() { //       
        console.log(i)
        console.log(j)
    }()
}

scope()

/*
 *     :
 * 6
 * 15
 * 15
 * 6
**/
(2)事前宣言
JavaScriptは関数のスコープを採用しています.これは関数内で宣言されたすべての変数がこの関数の中で見られます.
これは変数が声明の前にすでに利用可能になっていることをもたらして、JavaScriptの中でこのような現象は声明の前倒しと称しています.
function hoisting() {
    console.log(i) //     ,        
    var i = 'hello'
    console.log(i)
}

hoisting()

/*
 *     :
 * undefined
 * hello
**/
2、作用ドメインチェーン
(1)コンテキストオブジェクトを宣言する
JavaScriptではグローバルオブジェクトの属性が大域変数であることは知っていますが、多くの人は局所変数があるオブジェクトの属性であることを知らないです.
このオブジェクトは宣言コンテキストオブジェクトと呼ばれ、関数呼び出しに関連するオブジェクトです.内部実装としては、このオブジェクトを直接参照することはできません.
(2)スコープ
各セグメントJavaScriptコード(グローバルコードまたは関数)には、関連するスコープチェーンがあります.
ロールフィールドチェーンとは、実際にはオブジェクトリストです.このグループのオブジェクトは、このコードのスコープ内の変数を定義し、変数オブジェクトと呼びます.
一般に、変数オブジェクトは、グローバル変数を定義するグローバルオブジェクトと、ローカル変数を定義する宣言コンテキストオブジェクトを含みます.
グローバルコードには(関数定義に含まれていないコードです.)スコープ上にオブジェクトが一つしかありません.グローバルオブジェクトです.
ネストされた関数が含まれていない場合、スコープ上に2つのオブジェクトがあり、最初は関数パラメータと局所変数を定義するオブジェクトで、2番目はグローバルオブジェクトです.
ネストを含む関数については、そのスコープ上に少なくとも3つのオブジェクトが存在します.
(3)変数解析
JavaScriptが変数xの値を検索する必要があるときは、スコープ上の最初のオブジェクトから検索を開始します.
このオブジェクトにxという属性がある場合、その属性の値を変数xの値として直接使用します.
このオブジェクトにxという属性がないと、スコープ上の次のオブジェクトが検索されます.スコープの最後まで到達します.
フィールドチェーン上にxという属性が存在しない場合、参照異常をスローします.
(4)関数のスコープ
関数を定義すると、スコープチェーンが保存されます.この関数を呼び出すと、ローカル変数を保存するための新しいオブジェクトが作成されます.
保存されたロールフィールドチェーンにこのオブジェクトを追加し、関数呼び出しを表す新しいスコープを作成します.
関数が戻ると、保存されたスコープからこのオブジェクトを削除しますが、これは対象がすぐにゴミとして回収されるという意味ではありません.
JavaScriptのゴミ回収メカニズムによって、対象が引用されていない時だけ、JavaScriptに回収されるからです.
3、クローズド
(1)クローズドの理解
関数の内部にネスト関数が含まれている場合(ただし、ネスト関数が返されていません)、外部関数が戻ったらネスト関数も回収されます.
クローズドの中心は、関数にネスト関数が含まれていて、このネスト関数を返します.このとき、外部の参照がネストの関数を指します.
このネスト関数はゴミとして回収されません.これに対応するスコープも保存されます.
var global_scope = 'global'
function outer_function(outer_params) {
    var outer_scope = 'outer'
    console.log(outer_scope)
    // console.log(inner_scope) -> inner_scope is not defined
    var inner_function = function(inner_params) {
        var inner_scope = 'inner'
        console.log(outer_scope)
        console.log(inner_scope)
    }
    return inner_function
}
outer_function('outer')('inner')

/*
 *     :
 * outer
 * outer
 * inner
**/
outer_functionを呼び出したとき、作用ドメインチェーンは以下の通りである.
  • outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
  • window { global_scope: 'global' }
  • したがって、outer_scopeを印刷する際に、まずouter_を検索する.functionの対象は、中にouter_があります.scope属性は、直接に戻ります.inner_scopeを印刷するなら、outer_を検索します.functionの対象とwindowの対象はすべてouter_を探し当てていません.scope属性、投げ異常inner_functionを呼び出したとき、作用ドメインチェーンは以下の通りである.
  • inner_function { inner_params: 'inner', inner_scope: 'inner'}
  • outer_function { outer_params: 'outer', outer_scope: 'outer', inner_function: function }
  • window { global_scope: 'global', outer_function: function }
  • outer_scopeを印刷するとき、まずinner_を検索する.functionオブジェクトが見つかりませんでした.その後、outer_を探します.functionオブジェクトは、見つけることができます.inner_scopeを印刷するとき、まずinner_を検索する.functionオブジェクトは、見つけることができます.
    (2)クローズドの適用
  • プライベート属性を定義する
  • var counter = (function() { //       ,      
        var value = 0 //     ,      
        var changeBy = function(val) { value += val } //     ,      
        //                 
        return {
            getValue: function() { return value },
            increase: function() { changeBy(+1) },
            decrease: function() { changeBy(-1) }
        }
    })()
    
    console.log(counter.getValue())
    counter.increase()
    console.log(counter.getValue())
    counter.decrease()
    console.log(counter.getValue())
    
    /*
     *     :
     * 0
     * 1
     * 0
    **/
  • キャッシュ処理結果
  • function memory(f) {
        //       
        var cache = {}
        return function() {
            //          
            var key = arguments.length + Array.prototype.join.call(arguments, ',')
            if (key in cache) { //       ,      
                return cache[key]
            } else { //       ,        
                return cache[key] = f.apply(this, arguments)
            }
        }
    }
    
    var factorial = function(n) { return (n <= 1) ? 1 : n * factorial(n - 1) }
    
    var factorialWithMemory = memory(factorial)
    (3)クローズドの使用に関する注意事項
  • 外部変数の値は
  • を変更しますか?
    function test() {
        var array = []
        for(var count = 0; count < 5; count++) {
            array[count] = function() { console.log(count) }
        }
        return array
    }
    
    var result = test()
    result[0]()
    result[1]()
    result[2]()
    result[3]()
    result[4]()
    
    /*
     *     :
     * 5
     * 5
     * 5
     * 5
     * 5
    **/
    
    /*
     *     :
     *           ,         count   ,                 
     *         test         ,         count        5
    **/
    解決策
    function test() {
        var array = []
        for(var count = 0; count < 5; count++) {
            array[count] = function(value) { //       
                return function() { console.log(value) }
            }(count)
        }
        return array
    }
    
    var result = test()
    result[0]()
    result[1]()
    result[2]()
    result[3]()
    result[4]()
    
    /*
     *     :
     * 0
     * 1
     * 2
     * 3
     * 4
    **/
    
    /*
     *     :
     *     ,         value   ,                      
     *                       value,   value           count   
    **/
  • thisの指向が期待に合致しているかどうか
  • var test = {
        value: 0,
        getValue: function() {
            return function() { console.log(this.value) }
        }
    }
    
    var result = test.getValue()
    result()
    
    /*
     *     :
     * undefined
    **/
    
    /*
     *     :
     *         ,              ,   this       
    **/
    解決方法
    var test = {
        value: 0,
        getValue: function() {
            return function() { console.log(this.value) }.bind(this)
        }
    }
    
    var result = test.getValue()
    result()
    
    /*
     *     :
     * 0
    **/
    
    /*
     *     :
     *    bind     this       test   
    **/
    【JavaScriptシリーズの記事をもっと読んで、JavaScript学習ノートを見てください】