フロントノート(一)変数、実行環境とスコープ、this

8229 ワード

ECMAScriptの変数値の種類
  • 基本タイプ:Number、String、Boolean、Unidefined、Null
  • 引用タイプ:Object、Aray、Funct、Date、RegExp
  • 1つの値を変数に割り当てるときは、この値がベースタイプの値か参照タイプの値かを判定しなければなりません.
    基本データタイプは、変数に格納されている実際の値を操作できるので、値によってアクセスします.
    引用のタイプは違っています.その値はヒープメモリに保存されているオブジェクトです.JavaScriptはメモリの位置に直接アクセスすることができません.
    したがって、オブジェクトを操作するときは、実際には操作対象の参照であり、参照の種類の値は参照でアクセスされます.
    基本タイプの特徴:
    1.値は変わらない2.属性と方法を追加してはいけません.
    var name = "BarryAllen";
    
    name.substring(5); //"Allen"
    console.log(name) //BarryAllen  
    
    name.identity = "Flash";
    console.log(name.identity) //undefined 
    
    name.skill = function() {
        console.log("Running very fast.")
    }
    name.skill(); //name.skill is not a function
    
    引用タイプの特徴:
    1.値を変更することができます.2.属性と方法を追加することができます.
    var obj = {};
    obj.name = "BarryAllen";
    
    var change = obj;
    change.name = "OliverQueen";
    console.log(obj.name); //OliverQueen
    
    obj.identity = "Flash";
    console.log(obj.identity) //Flash
    
    obj.skill = function() {
        console.log("Running very fast.")
    }
    obj.skill(); //Running very fast.
    
    上のコードからは、コピー変数を行う際の基本型はコピー作成のような動作であることが分かりますが、参照タイプはオブジェクトを指すポインタのコピーですので、コピー操作の終了後に同じオブジェクトを参照します.したがって、変数の一つを変更すると他の変数に影響を与えます.
    パラメータの転送
    ECMAScriptで規定されているすべての関数のパラメータは値によって伝達されます.
    function setAge(obj) {
        obj.age = 18;
        obj = {};
        obj.age = 25;
    }
    var person = {}
    setAge(person);
    console.log(person.age) //18
    
    関数内部でオブジェクトを再宣言し、obj.ageの値を修正したが、パラメータ伝達が参照伝達によるものであればperson.age25を出力すべきであるが、事実はそうではない.この時点でオブジェクトは値で伝達されますので、元の参照は変わりません.実際には、関数が実行された後、この新規作成された局部オブジェクトはすぐに廃棄されます.
    検出タイプ
  • typeofは、基本タイプ
  • を検出するために使用されます.
  • instance ofは、参照タイプを検出するとき、どのタイプのオブジェクトかを判断するために使用される(すべての参照タイプの値がObjectの例であるため).
  • var num = 786;
    var bol = true;
    var name = "Violet";
    console.log(typeof num +"~"+ typeof bol +"~"+ typeof name); //number~boolean~string
    
    var arr = [];
    var func = new Function();
    console.log(arr instanceof Array) //true
    console.log(func instanceof Function) //true
    
    実行環境(Execution Contect)と作用領域
    実行環境は実行コンテキストとも呼ばれ、各実行環境には関連する変数オブジェクトがあり、環境で定義されたすべての変数と関数がこのオブジェクトに保存されます.
    Javascriptには三つのコードの実行環境があります.
  • グローバル実行環境---デフォルトの最周辺の実行環境は、ブラウザ内のその関連変数オブジェクトをwindowオブジェクト
  • と見なしている.
  • 関数実行環境---関数を呼び出すたびに、新しい実行コンテキストが作成されます.
  • Eval---文字列をパラメータとして受け入れ、Javascriptコードとして実行します.eval関数は新しい作用領域
  • を作成しません.
    新たに作成された実行コンテキストは、スコープの上部に追加され、実行または呼び出しスタックと呼ばれる場合がある.ブラウザは、常にアクティブドメインチェーンの先頭にある現在の実行コンテキストを実行します.完了すると、現在の実行コンテキストはスタックのトップから取り除かれ、前の実行コンテキストに制御権を返す.
    以下では、関数実行環境の構築過程を詳しく説明します.
  • 確立段階
  • アーグメンントオブジェクト、パラメータ、関数、変数(作成順序に注意!)
  • を作成します.
  • 作用分域チェーン
  • を確立する.
  • は、thisの値
  • を決定する.
  • コードの実行段階
  • 変数割り当て
  • 関数参照
  • 他のコード
  • を実行します.
    (function (obj) {
      console.log(typeof obj); //number
      console.log(typeof foo); //function
      console.log(typeof boxer); //undefined
      var foo = "Mashics";
       function foo() {
          document.write("This is a function.");
       }
      var boxer = function() {
          document.write("I am a boxer.");
       }
    })(666);
    
    このコードは、関数が環境確立を実行してから実行するプロセス、すなわち、まずパラメータの作成、そして関数の体内で関数を探しに行くという宣言を十分に示しています.最後に変数宣言です.特に、javascriptエンジンが関数宣言を探している間に、まずfooという関数を見つけたので、その後に定義された変数は属性を上書きしません.エンジンは次に具体的なコードセグメント内の変数宣言を検索し、関連変数のイメージ属性に追加し、undefinedとしてその割当てを開始します.したがって、変数のような古典的な問題は、環境作成プロセスを実行する観点から答えられ、解決されます.
    スコープチェーンとクローズド
    コードが一つの環境で実行されると、変数オブジェクトの作用ドメインチェーンが作成されます.その用途は、実行環境にアクセスできるすべての変数と関数に関する規則的なアクセスを保証することです.スコープの先端は常に現在実行されているコードの環境の変数オブジェクトであり、グローバル実行環境の変数オブジェクトは常にスコープ内の最後のオブジェクトです.変数検索を行うときは、フィールドチェーンのレベルを1つ上に検索します.閉ループの一部の特性は,作用ドメイン鎖という重要な特性によって決定される.
    var outer = "Margin";
    function foo() {
      var mider = "Padding";
        function baz() {
           var inner = "Content";
           console.log( "Gotcha! " + outer + " and " + mider + " . " );
        }
      return baz;
    }
    var fn = foo();
    fn(); //Gotcha! Margin and Padding . 
    console.log(inner); //inner is not defined.
    
    このコードは簡単なクローズドですが、スコープ内の最も重要な特性を示しています.
    !すなわち、内部環境は、フィールドチェーンを介してすべての外部環境にアクセスできますが、外部環境は内部環境の変数と関数にアクセスできません.
    PS:また、いくつかの紛らわしい、あるいは分かりにくい概念を説明します.
  • 変数オブジェクトとアクティビティオブジェクト
  • 変数オブジェクトは、実行環境の確立段階に作成され、実行フェーズに入る前に属性がアクセスできなくなり、実行フェーズに入ると変数オブジェクトがアクティブオブジェクトになり、次に実行フェーズ中のステップが実行されます.
  • 作用分域と作用分域チェーン
  • スコープと実行環境は全く異なる概念であり、javascriptコードが実行するプロセスは実は二つの段階でコードコンパイル段階とコード実行段階があり、スコープはコンパイル段階で作成された規則であり、エンジンが現在のスコープおよびネストされているサブスコープの中で識別子名に基づいて変数を検索する方法を管理するために使用される.実行コンテキストの作成は、コードの実行段階で行われます.スコープチェーンは一連の変数オブジェクトから構成されています.この一方向チャネルで変数オブジェクトの識別子を照会することができます.これにより、前の階層のスコープ内の変数にアクセスすることができます.
  • this詳細
    thisのバインディングプロセスを理解する前に、コールスタックと呼び出し位置という2つの概念を理解しなければならない.呼び出し位置は、現在実行中の関数の前の呼び出しにあります.
  • は、現在実行されている位置に到達するために呼び出されたすべての関数を呼び出すスタックを呼び出す.
  • 呼び出し位置:関数がコード内で呼び出された位置(ステートメントの位置ではなく).
  • function head() {
       //      head
       console.log("first");
       body(); //body      --> head
    }
    function body() {
       //      head -> body
       console.log("second");
       footer(); //footer      --> body
    }
    function footer() {
      //       head -> body -> footer
       console.log("third");
    }
    head();  //head      -->      
    
    thisバインディング規則:
  • デフォルトバインディング
  • 暗黙的バインディング
  • はバインディング
  • を表示します.
  • newバインディング
  • デフォルトバインディング
  • 関数が独立して呼び出される場合、すなわち、修飾されていない関数参照を直接使用して呼び出しを行う場合、thisはデフォルトのバインディングを使用し、このときthisはグローバルオブジェクトに向けられます.
    var a = 2;
    function foo() {
       console.log( this.a );
    }
    foo(); // 2
    
  • 暗黙的バインディング
  • 関数がコンテキストオブジェクトを参照すると、このコンテキストオブジェクトに関数呼び出し中のthisをバインドします.
    var obj = {
       a : 2,
       foo : foo
    }
    function foo() {
       console.log( this.a );
    }
    obj.foo(); //2
    
    foo()を呼び出すと、thisはobjに結合されるので、ここのthisはobjに相当する.
    陰でなくします
    暗黙的に結合された関数が明示的または暗黙的に割り当てられた場合、バインディングされたオブジェクトは失われ、したがって、thisをグローバルオブジェクトまたはundefinedにバインディングする.コールバック関数でのthisバインディングが失われるのは、パラメータ伝達が実は暗黙的な割当てであるからです.
    var a = "Global";
    var obj = {
       a : 2,
       foo : foo
    }
    function foo() {
       console.log( this.a );
    }
    var bar = obj.foo;
    bar(); //Global ->    
    
    function doFoo(fn) {
       fn();
    }
    doFoo( obj.foo ); //Global ->    
    
    setTimeout(obj.foo, 1000); //Global ->          ,         
    
    function setTimeout(fn, delay) {
      //  delay  
      fn();
    }
    
  • 明示バインディング
  • Function.prototypeのcall,apply,bindにより直接にthisのバインディングオブジェクトを指定します.
    callとappyは、すぐに実行される関数であり、パラメータの受け入れ形式が異なります.
    bindは新しい包装関数を作成して返します.実行ではありません.
    var obj = {
       a : 2
    }
    function foo() {
       console.log( this.a );
    }
    var bar = function() {
       foo.call(obj);
    }
    bar(); //2  -->   
    
    function calculate(b, c) {
       console.log(this.a, b, c);
       return (this.a * b) + c;
    }
    var excute = function() {
       return calculate.apply(obj, arguments); //apply                  
    }
    excute(5,10); //2 5 10 20
    
    var baz = calculate.bind(obj); //bind   this   obj   
    baz(8,5); //2 8 5 21
    
  • newバインディング
  • JavaScriptにおいて、構造関数はnewオペレータを使用する時に呼び出される普通の関数です.つまり、構造関数の呼び出しが発生した時には、以下の操作が行われます.
  • 新たなオブジェクト
  • を作成します.
  • 新しいオブジェクトが実行されます.
  • 新しいオブジェクトは、関数呼び出しのthis
  • に結び付けられます.
  • 関数がオブジェクトに戻っていない場合、new式の関数呼び出しは自動的にこの新しいオブジェクト
  • に戻ります.
    function Foo(a) {
        this.a = a;
    }
    var bar = new Foo(6);
    console.log( bar.a ); //6
    
  • 優先度
  • newバインディング>明示的バインディング>陰的バインディング>標準バインディング
  • thisと矢印関数
  • ES 6の矢印関数は、外層(関数または大域)の作用領域に基づいてthisを決定する4つの標準原則を使用しない.
    まず次の一般的なthisバインディングの失われた状況を見てください.
    function foo() {
       setTimeout(function() {
         console.log( this.a );
       },1000);
    }
    var obj = {
       a : 2
    }
    foo.call(obj); //undefined 
    
    ここでは、setTimeoutで発生した暗黙的な損失のために、thisはデフォルト規則を適用し、出力undefinedが行われる.thisをどのように私たちが欲しいobjオブジェクトに結び付けますか?
    var obj = {
       a : 2
    }
    function foo() {
       setTimeout( () => {
          console.log( this.a );
       },1000);
    }
    foo.call(obj) //2
    
    矢印関数のthisは語法的にfooを継承しているので、呼び出し時fooのthis、すなわちthisはobjオブジェクトに結合される.
    参考書:「JavaScript高級プログラム設計」「あなたの知らないJavaScript」(上)