Javascript——ContectとScopeの学習まとめ

7323 ワード

1.1.1要約
Javascriptを勉強しているうちに、しばしばスコープや実行文脈などの概念に出会います.ここで、実行文脈は、thisキーとの関係に近い.
オブジェクト指向プログラミングの経験がある方は、thisキーワードについてはもう慣れています.だから、オブジェクト指向のプログラミング方法と簡単にリンクして、構築器を使って新しく作成したオブジェクトを指します.ECMAScriptでは、thisもサポートされていますが、ご承知のように、thisは作成されたオブジェクトだけを表すものではありません.
次のブログでは、Javascriptの役割領域と実行文脈、そしてその違いを紹介します.
目次
  • 作用領域
  • 実行環境
  • コンテキスト問題
  • コンテキストインスタンス問題
  • 作用領域にまたがるコンテキスト
  • コンテキストを用いてスコープ問題を解決する
  • 作用領域を用いてコンテキスト問題を解決する
  • 1.1.2本文
    実行環境(Execution context)は、「環境」とも呼ばれる.Javascriptの中で最も重要な概念である.実行環境は、変数や関数がアクセスできる他のデータを定義し、それぞれの挙動を決定します.各実行環境には、関連する変数オブジェクトがあります.環境で定義されているすべての変数と関数は、このオブジェクトに保存されます.
    実行環境の定義を見てうなずいて気が遠くなり、簡単に言うと「実行環境ごとに変数のオブジェクトがある」ということです.ここで一つの疑問があります.この変数の対象はどのように定義されていますか?
    次に、変数オブジェクトの定義を見てみます.具体的には次のようになります.
    /**
    
     * Execution context skeleton.
    
     */
    
    activeExecutionContext = {
    
        // variable object.
    
        VO: {...},
    
        this: thisValue
    
    };
    上記の擬似コードを通じて、オブジェクトの字面量activeExecution Conttextを知っています.変数オブジェクトVOとthis属性が含まれています.
    これは、thisがコンテキストの実行可能なコードタイプに関係していることを示しており、その値はコンテキストに入る段階で決定され、コードの実行段階では変更できない(thisの使用については、「Javascript thisのいくつかの学習まとめ」を読むことができる).
    スコープは変数とパラメータの可視性とライフサイクルを制御しています.
    簡単に言えば、実行環境はオブジェクトに基づいており、作用域は関数に基づいている.
    スコープ
    一例を通して、スコープの使用を紹介します.まず、関数のFooA()とFooBを定義します.コード例は以下の通りです.
    /**
    
     * Defines a function.
    
     */
    
    var FooA = function(){
    
        var a = 1;
    
        var FooB = function(){
    
            var b = 2;
    
            console.log(a, b); // outputs: 1, 2
    
        }
    
        console.log(a, b); // Error! b is not defined
    
    }
    
    FooA();
    例では、2番目のlog出力変数は、Javascriptで関数内に定義されているパラメータと変数は、関数の外部では見られないが、1つの関数の内部の任意の位置で定義されているパラメータと変数のために定義されていない.
    実行環境
    まず、オブジェクトの字面量oを定義しました.属性xと方法m()を含んでいます.コード例は以下の通りです.
    /**
    
     * Defines a literal object.
    
     * @type {Object}
    
     */
    
    var o = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            console.log(x, this.x); // outputs 1, 23
    
        }
    
    }
    
    o.m();
    例では、2つの変数と属性xがアクセスできますが、それぞれのアクセス方法は異なり、logにおける最初のxへのアクセスは、作用領域によってローカル変数xにアクセスし、this.xはコンテキストを実行することによってオブジェクトoの属性xにアクセスするので、出力値も異なります.
    コンテキストの問題
    次に、前の例を修正して、メソッドm()に関数f()を追加します.コード例は以下の通りです.
    /**
    
     * Defines a literal object.
    
     * @type {Object}
    
     */
    
    var o = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            var f = function(){
    
                console.log(x, this.x); // outputs 1, undefined
    
            }
    
            f();
    
        }
    
    }
    
    o.m();
    上記では、方法m()を呼び出してxの値を出力します.方法m()の具体的な実装は関数f()を呼び出すことによって実現されます.
    オブジェクトoの方法m()を呼び出すと、this.xは未定義であることがわかった.
    これは一体何の原因ですか?前の例を振り返ってみると、方法m()はオブジェクトoの文脈を取得していますので、thisはオブジェクトoを指していますが、関数f()はオブジェクトoの文脈を取得していませんので、どのオブジェクトを指すか分かりません.
    まず、関数と方法と属性と変数の違いを振り返ってみましょう.方法とオブジェクトの関連、例えば:object.myMethod=function(){}ではなく、関数は対象関連ではありません.同じ属性も対象関係です.例えば、object.myProperty=23、変数:var myProperty=23.
    コンテキストというのはオブジェクトベースなので、関数f()はオブジェクトoの実行コンテキストを取得できません.
    関数f()にオブジェクトoの実行コンテキストを取得させてもいいですか?私達は詳しく考えてみますが、関数f()がthisのオブジェクトを指すことがわからない以上、オブジェクトの属性を直接呼び出すことができます.
    /**
    
     * Fixs broken context issue.
    
     * @type {Object}
    
     */
    
    var o = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            var f = function(){
    
                console.log(x, o.x); // outputs 1, 23
    
            }
    
            f();
    
        }
    
    }
    
    o.m();
    関数f()でオブジェクトoの属性xを直接呼び出します.このように関数f()は、コンテキストでオブジェクトを直接呼び出す属性を取得する必要がありません.
    今、私達はまた新しい問題に出会いました.オブジェクトがoではなくpである場合、関数f()のオブジェクトを変更する必要があります.もっと深刻な状況は具体的にどのオブジェクトなのか確認できません.例コードは以下の通りです.
    /**
    
     * Defines a literal object.
    
     * @constructor
    
     */
    
    var C = function(){}
    
    C.prototype = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            var f = function(){
    
                console.log(x, this.x); // outputs 1, undefined
    
            }
    
            f();
    
        }
    
    }
    
    var instance1 = new C();
    
    instance1.m();
    コンテキストインスタンスの問題
    上記では、関数Cとプロトタイプオブジェクトを定義しました.また、new方式でCオブジェクトインスタンスinstance 1を作成できます.前の方法でBroken Contect問題を解決します.具体的には以下のようになります.
    /**
    
     * Defines a literal object.
    
     * @constructor
    
     */
    
    var C = function(){}
    
    C.prototype = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            var f = function(){
    
                console.log(x, instance1.x); // outputs 1, undefined
    
            }
    
            f();
    
        }
    
    }
    
    var instance1 = new C();
    
    instance1.m();
    Cのオブジェクトインスタンスinstance 2を作成すると、関数f()のオブジェクトを指定できなくなります.
    実際には、thisはオブジェクトの実例の抽象的なものであり、例が複数ある場合、百元以上ある場合には、これらのオブジェクトのインスタンスをthisを介して参照する必要があります.
    したがって、オブジェクトを指定する方法は、Broken Contectの問題を効果的に解決できないので、オブジェクトを参照するためにthisを使用する必要があります.前に述べたように、関数f()はオブジェクトoの実行文脈を取得していないので、thisがどのオブジェクトを指すかは分かりません.だから出力this.xは定義されていません.
    スコープにまたがるコンテキスト
    方法はオブジェクトに基づいていると思います.そして、オブジェクトの実行文脈を得ることができます.f()を直接に方法として定義したらいいと思います.
    次に、Cオブジェクトのプロトタイプに方法f()を定義します.コード例は以下の通りです.
    /**
    
     * Defines a literal object.
    
     * @constructor
    
     */
    
    var C = function(){}
    
    
    
    C.prototype = {
    
        x:10,
    
        m: function(){
    
            var x = 1;
    
            this.f();
    
        },
    
        f: function(){
    
            console.log(x, this.x); // Reference ERROR!!
    
        }
    
    }
    
    var instance1 = new C();
    
    instance1.m();
    はい、Cオブジェクトのプロトタイプにメソッドf()を定義すれば、メソッドf()はオブジェクトの実行文脈を取得することができます.
    今、私たちはFirefoxで上記のコードを実行して、結果としてReference ERRORを出力します.これは一体どういう理由ですか?変数xにおいて、方法f()はメソッドm()の作用領域を取得できないので、変数xはメソッドf()にないという問題を考えました.
    コンテキストを使ってスコープの問題を解決します.
    私たちは両方の困難な状況にある方法f()は実行文脈を取得するとともに、方法m()の作用領域を取得します.方法m()の作用領域を得るには、方法f()をm()に定義する必要がある.
    次に、方法f()をm()に定義し、具体的には以下のように実現する.
    /**
    
     * Defines a literal object.
    
     * @constructor
    
     */
    
    var C = function(){}
    
    C.prototype = {
    
        x:23,
    
        m: function(){
    
            var x = 1;
    
            this.f = function(){
    
                console.log(x, this.x); // outputs 1, 23
    
            }
    
            this.f();
    
        }
    
    }
    
    var instance1 = new C();
    
    instance1.m();
    ここでは、オブジェクトinstance 1がコンテキストを実行し、方法m()の作用領域を取得することができるので、方法f()は属性と変数xの値を取得することができる.
    スコープを使ってコンテキスト問題を解決します.
    次に、関数setTimeout()でメソッドonTimeoutを呼び出す例を見てみます.具体的には次のように定義されています.
    /**
    
     * setTimeout function with Broken Context issue
    
     * @type {Object}
    
     */
    
    var o = {
    
        x:23,
    
        onTimeout: function(){
    
            console.log("x:", this.x);
    
        },
    
        m: function(){
    
            setTimeout(function(){
    
                this.onTimeout(); // ERROR: this.onTimeout is not a function
    
            }, 1);
    
        }
    
    }
    
    o.m();
    同様に関数setTimeout()でメソッドonTimeoutを呼び出すことができませんでした.これは方法one Timeout()がオブジェクトを取得してコンテキストを実行できないからです.
    方法m()では、オブジェクトがコンテキストを実行することができることを知っています.したがって、一時変数によって、thisが指すオブジェクトを参照することができます.コードの例は以下の通りです.
    /**
    
     * Fixs setTimeout function with Broken Context issue.
    
     * @type {Object}
    
     */
    
    var o = {
    
        x:23,
    
        onTimeout: function(){
    
            console.log("x:", this.x); // outputs 23
    
        },
    
        m: function(){
    
            
    
            // Keeps instance reference.
    
            var self = this;
    
            setTimeout(function(){
    
               // Gets m scrope. 
    
                self.onTimeout();
    
            }, 1);
    
        }
    
    }
    
    o.m();
    上記では、一時変数selfによってthisの参照を保存しました.setTimeout()関数はm()の作用領域を得ることができますので、私達はself.onTimeout()の方式でonTimeout()を呼び出すことができます.
    1.1.3まとめ
    このブログは文脈と作用域の異同、thisの使用及び変数の対象を紹介することによって、Javascriptの言語特性に対する理解を深めます.
    まず、コンテキストとthisの関係を実行することを紹介し、実行文脈はオブジェクトを有するものである.次に,作用領域が変数を作用領域の範囲内で可視化し,作用領域が関数に基づいていることを紹介した.
    具体的な例を通して、異なる作用領域と実行文脈において、thisと変数に対する影響が作用領域と実行文脈の理解を深め、それによってコードの読み取りと作成を助けてくれます.