ES 6新特性のletとconstコマンド


ES 6新特性のletとconstコマンド
1、letコマンド
ES 6は、変数を宣言するためにletコマンドを追加しました.その使い方はvarに似ていますが、宣言された変数はletコマンドがあるコードブロック内でのみ有効です.
{
     
  let a = 10;
  var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
  • 変数アップ
  • が存在しません.
    letはvarのように「変数アップ」現象が発生しません.したがって、変数は宣言後に必ず使用されます.そうでないとエラーが発生します.
    consolone.log(foo)//.undefined consoline.logs(bar)を出力します./エラーReferenceError var foo=2;let bar=2
    上記のコードの中で、変数fooはvarコマンドで宣言しています.すなわち、スクリプトが起動すると変数fooは既に存在しますが、値がないので、undefinedを出力します.変数barはletコマンドで宣言します.変数のアップグレードは発生しません.これは声明の前に変数barが存在しないという意味です.これを使うとエラーが発生します.
  • 一時性死区
  • ブロックレベルのスコープ内にletコマンドが存在する限り、その宣言した変数は「バインディング」(binding)という領域になり、外部の影響を受けなくなる.
    var tmp = 123;
    if (true) {
         
      tmp = 'abc'; // ReferenceError
      let tmp;
    }
    
    上のコードには、グローバル変数tmpがありますが、ブロックレベルのスコープ内のletはまた、局所変数tmpを宣言しています.このブロックレベルのスコープを結合させるため、letが変数を宣言する前に、tmp割当値に対してエラーが発生します.
    ES 6は、ブロック内にletとconstコマンドが存在する場合、このブロックがこれらのコマンドに対して宣言する変数は、最初から閉鎖作用領域を形成することを明確に規定している.宣言する前にこれらの変数を使うと、エラーが発生します.つまり、コードブロック内では、letコマンドを使用して変数を宣言する前に、この変数は使用できません.これは文法的には「一時的なデッドゾーン」と呼ばれています.
    if (true) {
         
      // TDZ  
      tmp = 'abc'; // ReferenceError
      console.log(tmp); // ReferenceError
      
      let tmp; // TDZ  
      console.log(tmp); // undefined
    
      tmp = 123;
      console.log(tmp); // 123
    }
    
    上のコードは、letコマンドが変数tmpを宣言する前に、変数tmpの「デッドゾーン」に属します.
    いくつかの「デッドゾーン」は比較的に隠れています.見つけにくいです.
    function bar(x = y, y = 2) {
         
      return [x, y];
    }
    bar(); //   
    
    上のコードの中で、bar関数を呼び出してエラーが発生したのは、パラメータxのデフォルト値が別のパラメータyに等しいためであり、yはまだ宣言されていません.デッドゾーンに属しています.yのデフォルト値がxであれば、エラーは発生しません.この時点でxはすでに宣言されています.
    ES 6は、一時的なデッドエリアとlet、constステートメントに変数のアップグレードがないことを規定しています.主に運行時のエラーを減らすために、変数宣言前にこの変数を使用することを防止して、予想外の行動を招いています.このようなエラーはES 5でよく見られます.このような規定があるので、このような間違いを避けるのは容易です.
    つまり、一時的なデッドゾーンの本質は、現在のスコープに入ると、使用する変数が既に存在しますが、取得できないのは、宣言変数の行のコードが現れるまで、その変数を取得して使用することができます.
  • 重複した声明は許されない.
    letは同じスコープ内で同じ変数を繰り返し宣言することができません.
    //   
    function () {
         
      let a = 10;
      var a = 1;
    }
    //   
    function () {
         
      let a = 10;
      let a = 1;
    }
    
    したがって、パラメータは関数内で再宣言できません.
    function func(arg) {
         
      let arg; //   
    }
    function func(arg) {
         
      {
         
        let arg; //    
      }
    }
    
  • ブロック級作用領域
  • なぜブロックレベルのスコープが必要ですか?ES 5はグローバルスコープと関数スコープだけで、ブロックレベルのスコープがないため、多くの不合理なシーンをもたらします.
    第一のシーンでは、内層変数が外層変数をカバーする場合があります.
    var tmp = new Date();
    function f() {
         
      console.log(tmp);
      if (false) {
         
        var tmp = "hello world";
      }
    }
    f(); // undefined
    
    上のコードでは、関数fが実行された後、出力結果はundefinedであり、その理由は変数が向上し、内部層のtmp変数が外層のtmp変数をカバーしているためです.
    第二のシーンでは、計数するための循環変数が大域変数として漏洩されます.
    var s = 'hello';
    for (var i = 0; i < s.length; i++) {
         
      console.log(s[i]);
    }
    console.log(i); // 5
    
    上記のコードでは変数iはループ制御のみに使用されますが、ループ終了後は消えず、グローバル変数に漏れました.
    ES 6のブロックレベルのスコープは実際にJavaScriptのためにブロックレベルのスコープを追加しました.
    function f1() {
         
      let n = 5;
      if (true) {
         
        let n = 10;
      }
      console.log(n); // 5
    }
    
    内層作用領域は外層作用領域の同名変数を定義することができる.
    {
         {
         {
         {
         
      let insane = 'Hello World';
      {
         let insane = 'Hello World'}
    }}}};
    
    ブロックレベルのスコープの出現は、実際には、幅広いアプリケーションを得るための即実行関数式(IIIIFE)を必要としなくする.
    // IIFE   
    (function () {
         
      var tmp = ...;
      ...
    }());
    
    //        
    {
         
      let tmp = ...;
      ...
    }
    
    ブロックレベルのスコープと関数宣言関数は、ブロックレベルのスコープ内で宣言できますか?かなり混淆された問題です.ES 5は、関数はトップレベルのスコープと関数のスコープの中でしか宣言できないと規定しています.ブロックレベルのスコープ宣言はできません.
    //    
    if (true) {
         
      function f() {
         }
    }
    
    //    
    try {
         
      function f() {
         }
    } catch(e) {
         
    }
    
    上のコードの2つの関数宣言は、ES 5の規定により違法です.しかし、ブラウザはこの規定を遵守していません.従来のコードに対応するために、ブロックレベルのスコープ内の宣言関数をサポートしています.したがって、上の2つの場合は実際に実行できます.エラーは発生しません.ただし、厳密モードではエラーが発生します.
    // ES5    
    'use strict';
    if (true) {
         
      function f() {
         }
    }
    //   
    // ES6         ,                。
    
    // ES6    
    'use strict';
    if (true) {
         
      function f() {
         }
    }
    //    
    //ES6   ,       ,            let,            。
    
    環境による挙動の違いを考慮して、ブロックレベルのスコープ内で関数を宣言することを避けるべきです.必要であれば、関数ステートメントの代わりに関数式を作成するべきです.
    //       
    {
         
      let a = 'secret';
      function f() {
         
        return a;
      }
    }
    
    //      
    {
         
      let a = 'secret';
      let f = function () {
         
        return a;
      };
    }
    
    また、もう一つの注意点があります.ES 6のブロックレベルのスコープは、ステートメント関数のルールを許可しています.大きな括弧だけを使用して成立します.大きな括弧を使用していないと、エラーが発生します.
    //    
    'use strict';
    if (true) {
         
      function f() {
         }
    }
    
    //   
    'use strict';
    if (true)
      function f() {
         }
    
  • do表現
  • 本質的には、ブロックレベルのスコープは、ステートメントであり、複数の動作を一緒にカプセル化し、リターン値がない.
    {
         
      let t = f();
      t = t * t + 1;
    }
    
    上のコードでは、ブロックレベルのスコープは2つのステートメントをカプセル化しています.しかし、ブロックレベルのスコープ以外では、tの値は得られません.なぜなら、ブロックレベルのスコープは戻りません.tがグローバル変数でない限り、値を返しません.
    ブロックレベルのスコープを式に変えるという提案があります.つまり、値を返すことができます.ブロックレベルのスコープの前にdoを加えて、do表現にします.
    let x = do {
         
      let t = f();
      t * t + 1;
    };
    //     ,  x              。
    
    2、constコマンド
    constは読み取り専用の定数を宣言します.一度宣言したら定数の値は変更できません.
    const PI = 3.1415;
    PI // 3.1415
    
    PI = 3;
    // TypeError: Assignment to constant variable.
    
    上のコードは定数を変更する値がエラーとなることを示しています.
    const宣言の変数は値を変更してはいけません.これは、constが変数を宣言すると、すぐに初期化しなければならないことを意味しています.今後の割り当てには残してはいけません.
    const foo;
    // SyntaxError: Missing initializer in const declaration
    
    上のコードによると、constにとっては、割り当てがないと宣言するだけで、エラーが発生します.
    constのスコープはletコマンドと同じです.声明があるブロックレベルのスコープ内でのみ有効です.
    if (true) {
         
      const MAX = 5;
    }
    
    MAX // Uncaught ReferenceError: MAX is not defined
    
    const命令声明の定数も上げられず、同様に一時的なデッドゾーンがあり、声明の位置の後にのみ使用されます.
    if (true) {
         
      console.log(MAX); // ReferenceError
      const MAX = 5;
    }
    
    上のコードは定数MAX宣言の前に呼び出します.結果はエラーです.
    const声明の定数は、letと同様に重複して声明してはならない.
    var message = "Hello!";
    let age = 25;
    
    //         
    const message = "Goodbye!";
    const age = 30;
    
    複合型の変数に対しては、変数名はデータを指すのではなく、データの所在地を指すものです.constコマンドは変数名が指すアドレスが変わらないだけで、そのアドレスのデータが変わらないことを保証するものではないので、オブジェクトを定数として宣言するには十分注意が必要です.
    const foo = {
         };
    foo.prop = 123;
    
    foo.prop
    // 123
    
    foo = {
         }; // TypeError: "foo" is read-only
    
    上記のコードの中で、常量fooは一つのアドレスを記憶しています.この住所は一つのオブジェクトを指しています.可変ではないのはこの住所だけです.fooを他の住所に向けることはできませんが、オブジェクト自体は可変ですので、まだ新しい属性を追加することができます.
    const a = [];
    a.push('Hello'); //    
    a.length = 0;    //    
    a = ['Dave'];    //   
    
    上のコードでは、定数aは配列であり、この配列自体は書き込み可能ですが、他の配列をaに割り当てるとエラーが発生します.
    本当に対象を凍結したいなら、Object.freeze方法を使うべきです.
    
    const foo = Object.freeze({
         });
    
    //      ,        ;
    //      ,     
    foo.prop = 123;
    
    上のコードでは、定数fooは凍結されたオブジェクトを指していますので、新しい属性を追加するのは機能しません.厳しいモードではエラーが発生します.
    オブジェクト自体を凍結するだけでなく、オブジェクトの属性も凍結されます.以下はオブジェクトを完全に凍結する関数です.
    var constantize = (obj) => {
         
      Object.freeze(obj);
      Object.keys(obj).forEach( (key, value) => {
         
        if ( typeof obj[key] === 'object' ) {
         
          constantize( obj[key] );
        }
      });
    };
    
    ES 5は2つの変数を宣言する方法しかありません.varコマンドとfunctionコマンドです.ES 6はletとconstコマンドを追加する以外に、後の章でも言及されています.他の2つの変数を宣言する方法:importコマンドとclassコマンドです.したがって、ES 6は6つの変数を宣言する方法があります.