ES 6——ブロックレベルのスコープ

14848 ワード

前の話
過去、javascriptはブロックレベルの機能領域が不足していました.var宣言時の声明の昇格、属性変数などの行為は困惑しました.ES 6の新しい文法は、作用領域をよりよく制御する助けになります.この論文では、ES 6が新たに導入したブロックレベル作用ドメインバインディング機構、let及びconst声明メカニズム及び最適な実践を詳細に紹介する.
 
var声明
【変数アップ】  var宣言が発生します.変数の引き上げ現象、すなわち変数は声明の前に使用できます.値はundefinedです. 
function getValue(condition){
    if(condition){
        var value = 'blue';
        return value;
    }else{
     // value, undefined
return null; }
    // value, undefined }
javascriptの開発経験がないと、conditionがtrueである場合にのみ変数valueが作成されると考えられます.
しかし、実際には、プリコンパイル段階で、javascriptエンジンは上の関数を次のように修正します.
function getValue(condition){
    var value;
    if(condition){
        value = 'blue';
        return value;
    }else{
        return null;
    }
}
変数valueの宣言は、関数の上部に引き上げられ、初期化動作は元の場所に残っています.注意しないと、エラーを引き起こす可能性があります.いくつかのために、ES 6はブロックレベルの作用領域を導入し、変数のライフサイクルの制御を強化します.
【ブロックレベル宣言】
ブロックレベル宣言は、指定されたブロックの作用領域以外ではアクセスできない変数を宣言します.
1、関数の内部
2、{}間のブロックエリア内
 
let声明
let宣言の使用法はvar宣言と同じです.varの代わりにletで変数を宣言すると、変数のスコープを現在のコードブロックに制限できます.
function getValue(condition){
    if(condition){
        let value = 'blue';
        return value;
    }else{
         //  value      
        return null;
    }
    //  value      
}
変数valueはキーワードletによって宣言された後、関数の上部には昇格されません.ifブロックを実行すると、valueはすぐに破壊されます.conditionの値がfalseであれば、永遠にvalueを宣言して初期化しません.
【重言禁止】
既にロールエリアに識別子が存在していると仮定して、letキーを使って宣言するとエラーが発生します.
var count = 30;
//      
//Uncaught SyntaxError: Identifier 'count' has already been declared
let count = 40;
 
const声明
const声明を使用するのは定数で、その値はいったん設定されたら変更できません.したがって、各const声明の定数は初期化されなければなりません.
const num = 30;
//      
//Uncaught SyntaxError: Missing initializer in const declaration
const name;
constとletは、教師のブロックレベルの識別子を宣言します.したがって、定数も現在のコードブロックだけで有効です.ブロック外に実行されるとすぐに破壊されます.定数も同様に、スコープの上部にアップグレードされません.
if(condition){
    const num = 30;    
}
//      num
【重言禁止】
letと同様に、同一のスコープ内で既に存在する識別子をconstで宣言することは、構文エラーを引き起こすことがあり、識別子がvarを使用するか、またはlet声明を使用するかにかかわらず、構文エラーを引き起こすことがある.
var message = 'hello';
let num = 10;

//           
const message = "goobye";
const num = 30;
【再割り当てができません】
constとlet声明の最大の違いは、const声明の定数が再割り当てできないことである.
let num1 = 10;
num1= 20;

const num2 = 10;
//Uncaught TypeError: Assignment to constant variable.
num2 = 20;
【オブジェクト属性の変更が可能】
const宣言は、バインディングの変更を許可しませんが、値の変更を許可します.これは、オブジェクトをconstで宣言した後、オブジェクトの属性値を変更することができます.
const person = {
    name: 'huochai'
};
//          
person.name = 'match';
//Object {name: "match"}
console.log(person);

//      
//Uncaught TypeError: Assignment to constant variable.
person = {
    name: 'match'
}
 
臨時死区
varと違って、letおよびconst宣言の変数は、スコープのトップにまで引き上げられません.宣言の前にこれらの変数にアクセスすると、エラーが発生します.スコープのトップから変数ステートメントの前のこのエリアは、一時的なデッドゾーン(temporal dead zone)と呼ばれ、TDZと略称されます.
if(true){
    //undefined
    console.log(typeof value);
    var value = "blue";
}

if(true){
    //Uncaught ReferenceError: value is not defined
    console.log(typeof value);
    let value = "blue";
}
ただし、letまたはconst宣言のスコープ以外でこの変数を使うとエラーが発生しません.
//undefined
console.log(typeof value);
if(true){
    let value = "blue";
}
 
循環結合
【var声明】
長い間、var宣言は循環中に関数を作成することが非常に困難になりました.変数は循環外にもアクセスできます.
var funcs = [];
for(var i = 0; i < 10; i++){
    funcs.push(function(){
        //  10 10
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
上記のコードでは、予想される結果は、0から9の数字が出力されますが、10回連続して出力されています.これはサイクル内の反復のたびに変数iを共有しています.循環内部で作成された関数はすべて同じ変数に対する参照を保持しています.循環終了時に変数iの値は10です.
【IIIF】
この問題を解決するために、カウンタ変数のコピーを強制的に生成するために、即座に関数式(IIIIFE)を呼び出すことができます.
var funcs = [];
for(var i = 0; i < 10; i++){
    funcs.push((function(value){
        return function(){
            //0
            //1
            //...
            //9
            console.log(value);
        }
    })(i));
}
funcs.forEach(function(func){
    func();
})
循環内部では、IIIIIIIIFE表現は、受け入れられた各変数iのコピーを作成し、変数valueとして記憶しています.この変数の値は、反復によって作成された関数によって使用される値です.したがって、各関数を呼び出して0-9サイクルのように所望の値が得られます.
【let】
let宣言は、上記の例のIIIIIIFEのすべてを模倣してループプロセスを簡略化します.反復のたびに新しい変数を作成し、同じ名前の変数の値を前の反復で初期化します.
var funcs = [];
for(let i = 0; i < 10; i++){
    funcs.push(function(){
        //0
        //1
        //...
        //9
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
以上のサイクルはより簡潔であり、ループ毎にlet宣言は新しい変数iを作成し、iの現在値に初期化するので、ループ内で作成された関数ごとに属性のiのコピーを得ることができます.
for-i-nサイクルとfor-ofサイクルについても同じです.
var funcs = [];
obj = {
    a:true,
    b:true,
    c:true
}
for(let key in obj){
    funcs.push(function(){
        //a
        //b
        //c
        console.log(key);
    })
}
funcs.forEach(function(func){
    func();
})
【const】
const宣言では変数の値を変えることができませんので、通常のforループは使えません.
var funcs = [];
for(const i = 0; i < 10; i++){
    funcs.push(function(){
            //Uncaught TypeError: Assignment to constant variable.
        console.log(i);
    });
}
funcs.forEach(function(func){
    func();
})
for-i-nサイクルでは、反復ごとに既存のバインディングを修正することなく、新たなバインディングを作成するので、for-inサイクルでconstを使用することができる.
var funcs = [];
obj = {
    a:true,
    b:true,
    c:true
}
for(const key in obj){
    funcs.push(function(){
        //a
        //b
        //c
        console.log(key);
    })
}
funcs.forEach(function(func){
    func();
})
 
属性変数
var宣言の変数にとって、グローバルスコープであれば、自動的にwindowオブジェクトの属性になります.これは、すでに存在しているグローバル変数をvarで無意識に上書きする可能性が高いことを意味します.
//function RegExp() { [native code] }
console.log(RegExp);
var RegExp = "hello";
console.log(RegExp);//'hello'
console.log(window.RegExp);//'hello'
letまたはconst宣言の変数を使うと、windowオブジェクトの属性にはなりません.
let RegExp = "hello";
console.log(RegExp);//'hello'
console.log(window.RegExp);//function RegExp() { [native code] }
したがって、Windowsオブジェクトで変数を定義したい場合は、var宣言を使用します.望まない場合は、letまたはconstを使用します.
 
ベストな実践
デフォルトではconstを使いますが、確実に変数の値を変える必要がある場合のみletを使います.
ほとんどの変数の値は初期化後に変更すべきではないので、予想外の変数値の変化は多くのバグの元です.