Jsのクローズドとthis

9004 ワード

この問題を理解するにはまず一つの概念を明確にしなければならない.
コンテキストを実行
実行の文脈は何ですか?
実行コンテキストはjsコードが実行する環境であり、jsが実行可能なコードを実行すると、対応する実行コンテキストが作成されることが分かります.彼の構成は以下の通りです
executionContextObj = {
    this:           this,
    VO:    ,
    scopeChain:     ,     
}
JSはシングルスレッドなので、一回に一つのことしかできません.他のことは指定されたコンテキストスタックに並べられます.
jsインタプリタは、初期化コード実行時に、グローバル実行コンテキストをスタックに作成し、その後、各関数の呼び出しに従って、新しい実行コンテキストスタックを作成して押し込む.関数が実行されると、実行コンテキストがポップアップされます.
5つのポイント:
  • 単スレッド
  • 同期実行
  • グローバルコンテキスト
  • 無制限関数コンテキスト
  • は、関数呼び出しごとに新しいコンテキストを作成し、呼び出し自体
  • を含む.
    コンテキスト確立のステップを実行
    シシシシ菗の創建段階
    スコープチェーンを初期化して変数オブジェクトを作成し、argmentsスキャン関数を作成します.スキャン変数宣言はthisを求めます.
    実行フェーズ
    初期化変数と関数の参照実行コードthisは、関数実行時に、thisは常に関数を呼び出すオブジェクトを指します.thisの方向を判断するには、thisの位置を判断する関数は誰ですか?
    コール対象:
    function foo() {
        console.log( this.a );
    }
    var obj = {
        a: 2,
        foo: foo
    };
    obj.foo(); // 2
    //       
    
    function foo() {
        console.log( this.a );
    }
    var a = 2;
    foo(); // 2
    //   
    
    //  
    var bar = foo
    a = 3
    bar() // 3  2
    
    //この例によって、thisは関数呼び出し時に決定されることが分かります.
    //もうちょっと回ってください
    function foo() {
        console.log( this.a );
    }
    function doFoo(fn) {
        this.a = 4
        fn();
    }
    var obj = {
        a: 2,
        foo: foo
    };
    var a =3
    doFoo( obj.foo ); // 4
    //  
    
    function foo() {
        this.a = 1
        console.log( this.a );
    }
    function doFoo(fn) {
        this.a = 4
        fn();
    }
    var obj = {
        a: 2,
        foo: foo
    };
    var a =3
    doFoo( obj.foo ); // 1
    
    これはなぜですか?fooに設定されているa、類似作用域の原理を優先的に読み取るためですか?
    fooとdoFooのthisを印刷することで、彼らのthisはwindowを指しています.彼らの操作はwindowの中のaの値を修正します.fooに設定されているaを優先的に読み取るわけではない.
    だからコードを変えたら
    function foo() {
        setTimeout(() => this.a = 1,0)
        console.log( this.a );
    }
    function doFoo(fn) {
        this.a = 4
        fn();
    }
    var obj = {
        a: 2,
        foo: foo
    };
    var a =3
    doFoo( obj.foo ); // 4
    setTimeout(obj.foo,0) // 1
    
    上のコードの結果は推測を確認することができます.
    new構造で新しいオブジェクトを指します.
    a = 4
    function A() {
        this.a = 3
        this.callA = function() {
            console.log(this.a)
        }
    }
    A() //   undefined, A().callA   。callA    window 
    var a = new A()
    a.callA() // 3,callA new A      
    
    apply/call/bind
    皆さんはよく知っているはずです.thisは伝達の最初のパラメータを指します.もし最初のパラメータがnullであれば、undefinedであれば、グローバル変数を指します.
    a = 3
    function foo() {
        console.log( this.a );
    }
    var obj = {
        a: 2
    };
    foo.call( obj ); // 2
    foo.call( null ); // 3
    foo.call( undefined ); // 3
    foo.call(  ); // 3
    var obj2 = {
        a: 5,
        foo
    }
    obj2.foo.call() // 3,  5!
    //bind        
    function foo(something) {
        console.log( this.a, something );
        return this.a + something;
    }
    var obj =
        a: 2
    };
    var bar = foo.bind( obj );
    var b = bar( 3 ); // 2 3
    console.log( b ); // 5
    
    矢印関数
    矢印関数は特殊で、自分のthisがないので、閉じたコンテキスト(関数またはglobal)を実行するthis値を使います.
    var x=11;
    var obj={
     x:22,
     say:()=>{
       console.log(this.x); //this  window
     }
    }
    obj.say();// 11
    obj.say.call({x:13}) // 11
    x = 14
    obj.say() // 14
    //    
    var obj2={
     x:22,
     say() {
       console.log(this.x); //this  obj2
     }
    }
    obj2.say();// 22
    obj2.say.call({x:13}) // 13
    イベント傍受関数
    結合されたdom要素を指します.
    document.body.addEventListener('click',function(){
        console.log(this)
    }
    )
    //     
    // ...
    HTMLHTMLタグの属性ではJSと書くことができます.この場合thisはこのHTML要素を指します.
    document.getElementById("foo").click(); //logs <div id="foo"...

    变量对象

    变量对象是与执行上下文相关的数据作用域,存储了上下文中定义的变量和函数声明。

    变量对象式一个抽象的概念,在不同的上下文中,表示不同的对象

    全局执行上下文的变量对象
    全局执行上下文中,变量对象就是全局对象。
    在顶层js代码中,this指向全局对象,全局变量会作为该对象的属性来被查询。在浏览器中,window就是全局对象。

    var a = 1
    console.log(window.a) // 1
    console.log(this.a) // 1
    関数がコンテキストを実行する変数オブジェクト
  • 関数コンテキストにおいて、変数オブジェクトVOはアクティブオブジェクトAOである.
  • 初期化時は、アーグメンント属性が付いています.関数コードを二つの段階に分けて実行します.
    コンテキストの実行時に変数オブジェクトが含まれます.
  • モダリティ
  • 関数宣言は、既存の変数オブジェクト
  • を置き換えます.
  • 変数宣言は、変形および関数
  • を置換しません.
  • 関数実行
  • コードに基づいて変数オブジェクトの値を変更します.
    例をあげる
    function test (a,c) {
      console.log(a, b, c, d) // 5 undefined [Function: c] undefined
      var b = 3;
      a = 4
      function c () {
      }
      var d = function () {
      }
      console.log(a, b, c, d) // 4 3 [Function: c] [Function: d]
      var c = 5
      console.log(a, b, c, d) // 4 3 5 [Function: d]
    }
    test(5,6)
    過程を分析します.
    1.実行コンテキストを作成する場合
    VO={argments:{0:5}、a:5、b:undefined、c:[Function]、/関数Cはパラメータcをカバーしていますが、変数宣言cは関数cの声明d:undefinedを上書きできません./関数式は向上していません.対応する文を実行する前にundefinedです.
    コード実行時に最後のconsoneで関数宣言が上書きされていることが分かります.
    作用ドメインチェーンを先に調べてください.
    スコープ
    変数と関数のアクセス可能範囲は、変数と関数の視認性とライフサイクルを制御しています.大域作用域と局所作用域に分けられる.
    グローバルスコープ:
    コードのどこにでもアクセスできるオブジェクトは、グローバルスコープを持っています.以下の種類があります.
    最外層で定義された変数.
    グローバルオブジェクトの属性
    任意の場所で暗黙的に定義された変数(直接的に割り当てられた変数が定義されていません)は、任意の場所で暗黙的に定義された変数を大域的な作用領域に定義します.すなわち、varを通じて直接的に割り当てられた変数を宣言しません.
    ローカルスコープ:
    JavaScriptのスコープは関数によって定義されています.一つの関数で定義されている変数はこの関数の内部だけに見えます.
    スコープチェーン
    スコープチェーンは、コンテキストコードに表示される識別子を検索するためのオブジェクトリストである.識別子は変数名、パラメータ、関数宣言として理解できる.
    関数は、定義時に親レベルの変数オブジェクトAO/VOのセットを内部属性[[scope]]に保存します.このセットを作用ドメインチェーンと呼びます.自由変数とは、関数内で宣言されていない変数のことです.関数が自由変数にアクセスする必要がある場合、スコープチェーンに沿ってデータを検索します.サブオブジェクトは親オブジェクトの変数を1段階ずつ上に検索しますが、親オブジェクトの変数はサブオブジェクトに対して表示されます.逆は成立しません.スコープチェーンとは、すべての内部環境で変数を検索するためのチェーンです.
    直接的に言えば、JSは語法作用域(静的作用域)を採用しており、JSの関数は彼らが定義された作用域で実行されるのではなく、彼らが実行される作用域で実行される.例を挙げて説明してもいいです.
    var s = 3
    function a () {
      console.log(s)
    }
    function b () {
      var s = 6
      a()
    }
    b() // 3,  6
    jsがダイナミックスコープを採用すれば、3ではなく6でプリントされるべきであり、この例は、jsが静的スコープであることを示している.
    関数のスコープの疑似コード:
    function foo() {
       function bar() {
           ...
       }
    }
    foo.[[scope]] = [
     globalContext.VO
    ];
    bar.[[scope]] = [
       fooContext.AO,
       globalContext.VO
    ];
    
    関数は動作がアクティブになると、まず[scope]属性をコピーしてスコープチェーンを作成し、変数オブジェクトVOを作成して、スコープチェーンに追加します.
    executionContextObj: {
        VO:{},
        scopeChain: [VO, [[scope]]]
    }
    包みを閉じる
    クローズドは何ですか
    クローズドはmdnの定義によると 自由変数は前に述べましたが、関数内で宣言されていない変数のことです.
    クローズド形式
    function a() {
        var num = 1
        function b() {
            console.log(num++)
        }
        return b
    }
    var c1 = a()
    c1() // '1'
    c1() // '2'
    var c2 = a()
    c2() // '1'
    c2() // '2'
    クローズドのプロセス
    あまり厳密に書かれていません.いくつかのプロセスが省略されているかもしれません.
    実行関数aが関数aのVOを作成し、変数numと関数b定義関数bを含むとき、aの変数オブジェクトVOとグローバル変数オブジェクトを保存します.「[scope]」に戻ります.c 1を実行してc 1を作成します.この作用領域チェーンはaの変数オブジェクトVO創建c 1のVO実行c 1を保存します.これは変数numにアクセスする必要があります.現在VOは存在しません.そこで、作用ドメインチェーンを通じてアクセスし、aのVOに保存されているnumを見つけて操作しました.numの値は2回に設定されてc 1を実行し、2回目の操作を繰り返すと、numの値は3つの問題に設定されています.上記の運転結果により、c 2の問い合わせnum変数とc 1の訪問num変数は同じ変数ではないことが分かりました.コードを修正して、自分の予想を確認できます.
    function a() {
        var x = {y : 4}
        function b() {
            return x
        }
        return b
    }
    var c1 = a()
    var c2 = a()
    c1 === c2()  // false
    したがって、クローズドが訪問する変数は、親関数を実行するたびに再作成され、互いに独立していると判断できます.なお、同じ関数で作成された自由変数は、異なるクローズドで共有できます.
    function a() {
        var x = 0
        function b() {
            console.log(x++)
        }
        function c() {
            console.log(x++)
        }
        
        return {
            b,
            c
        }
    }
    var r =  a()
    r.b() // 0
    r.c() // 1
    
    機能領域チェーンとクローズドのテクニックを追加します.
    chromeコンソールを開く
    console.dir(r.b)
    f b() {
        [[Scopes]]: [
            {x:0}, 
            {type: 'global', name: '', object: Window}
        ]    
    }
    最後に、文脈を実行する過程をまとめて印象を深めましょう.
    var scope = "global scope";
    function checkscope(a){
        var scope2 = 'local scope';
    }
    checkscope(5);
  • は、グローバルコンテキスト実行スタック
  • を作成する.
  • グローバル変数global Contect.VO.
  • を作成します.
  • checkscope関数
  • を作成します.
  • は、大域変数VOを作用領域チェーンとして保存し、関数の内部属性[scope]
  • に設定する.
    checkscope.[[scope]] = [
        globalContext.VO
    ];
    checkscope関数を実行してコンテキストを実行します.checkscope関数をコンテキストスタックに押し入れて実行します.
    ECStock=[
    checkscopeContext,
    globalContext
    」「
    関数実行コンテキスト作成フェーズ
    第一歩は[scope]をコピーして、スコープチェーンを作成します.
    checkscopeContext = {
        Scope: checkscope.[[scope]],
    }
    第二ステップは、アクティブなオブジェクトAOの作成です.
    checkscopeContext = {
        AO: {
            arguments: {
                0: 5
                length: 1
            },
            a: 5
            scope2: undefined
        },
        Scope: checkscope.[[scope]],
    }
    第三ステップは、アクティブなオブジェクトAOを作用ドメインチェーンの先端に入れることです.
    checkscopeContext = {
        AO: {
            arguments: {
                0: 5
                length: 1
            },
            a: 5
            scope2: undefined
        },
        Scope:  [AO, checkscope.[[scope]]],
    }
    第四ステップ、thisを求めて、文脈の作成段階は終了します.
    ここのthisはwindowに等しいです.
    関数実行段階に入ると、関数とともにAOの値を変更します.
      AO: {
          arguments: {
        0: 5
              length: 1
          },
    a: 5
          scope2: 'local scope'
      },
    
    関数実行済み関数コンテキストは、実行コンテキストスタックからポップアップされます.
    ECStack = [
        globalContext
    ];
    文章は長く書いています.範囲も広いので、間違いが多いかもしれません.指摘してください.