ES6では、変数がletまたはconstで宣言されていませんか?


266

ES6をしばらく使用していて、で宣言された変数がvar期待どおりに巻き上げられていることに気付きました...

console.log(typeof name); // undefined
var name = "John";

...で宣言された変数、letまたはconst巻き上げに問題があると思われる変数:

console.log(typeof name); // ReferenceError
let name = "John";

そして

console.log(typeof name); // ReferenceError
const name = "John";

これは、宣言された変数letまたはconstホイストされていない変数を意味しますか?ここで本当に何が起こっているのですか?間に何らかの差があるletと、constこの問題では?

回答:


346

@thefourtheyeは、これらの変数が宣言されるまでアクセスできないと言っているのは正しいことです。ただし、それよりも少し複雑です。

変数はホイスト付きで宣言されていますか、letまたはconstホイストされていませんか?ここで本当に何が起こっているのですか?

すべての宣言は、varletconstfunctionfunction*class"掲揚"されている JavaScriptで。つまり、名前がスコープで宣言されている場合、そのスコープでは、識別子は常にその特定の変数を参照します。

x = "global";
// function scope:
(function() {
    x; // not "global"

    var/let/… x;
}());
// block scope (not for `var`s):
{
    x; // not "global"

    let/const/… x;
}

これは、関数スコープとブロックスコープの両方に当てはまります1

違いvar/ function/ function*宣言とlet/ const/ class宣言はある初期化
前者はundefined、バインディングがスコープの最上部で作成されるときに、または(ジェネレーター)関数で初期化されます。ただし、字句的に宣言された変数は初期化されないままです。これは、ReferenceErrorアクセスしようとすると例外がスローされることを意味します。場合にのみ初期化されますlet/ const/ class文が評価され、(上記)の前に、すべてを呼ばれている一時的なデッドゾーン

x = y = "global";
(function() {
    x; // undefined
    y; // Reference error: y is not defined

    var x = "local";
    let y = "local";
}());

let y;ステートメントは、undefinedlikeで変数を初期化することに注意してくださいlet y = undefined;

時間的なデッドゾーンは構文場所ではなく、時間変数(スコープ)の作成及び初期化の間。そのコードが実行されない限り(たとえば、関数本体または単にデッドコード)、宣言より上のコードで変数を参照することはエラーではなく、初期化の前に変数にアクセスすると、コードが宣言の下にあります(たとえば、あまりにも早く呼び出されるホイスト関数宣言内)。

間に何らかの差があるletと、constこの問題では?

いいえ、巻き上げに関する限り、それらは同じように機能します。それらの間の唯一の違いは、constantは宣言のイニシャライザ部分でのみ割り当て可能であり、割り当てることができるということです(const one = 1;両方const one;、およびその後の再割り当てone = 2は無効です)。

1:varもちろん、宣言は関数レベルでのみ機能します。


16
一時的なデッドゾーンのために明白ではないので、のようなものlet foo = () => bar; let bar = 'bar'; foo();すべての宣言が巻き上げ効果をよりよく示していることがわかります。
Estus Flask

1
letの前に宣言された関数(つまり、クロージャー)でlet定義を参照することについて尋ねようとしていました。私はこれが質問に答えると思います、それは合法ですが、letステートメントが実行される前に関数が呼び出された場合はrefエラーとなり、関数が後で呼び出された場合は問題ありません。多分これは本当なら答えに追加できますか?
Mike Lippert、2016年

2
@MikeLippertはい、そうです。初期化する前に、変数にアクセスする関数を呼び出さないでください。このシナリオは、たとえば、すべての巻き上げ関数宣言で発生します。
Bergi、2016年

1
constような決定letは設計上の欠陥です。スコープ内constでは、アクセス時に巻き上げられ、ジャストインタイムで初期化される必要があります。本当に、彼らが持っている必要がありconstlet「読み取り専用」のように働く変数を作成し、別のキーワードをlet
パセリエ2017年

1
前者は未定義で初期化されています…」はvar宣言には問題ないかもしれませんが、実行が始まる前に値が割り当てられる関数宣言には適切ではないようです。
RobG 2017年

87

ECMAScript 6(ECMAScript 2015)の仕様letconst宣言のセクションを引用する

変数は、それらを含むLexical Environmentがインスタンス化されるときに作成されますが、変数のLexicalBindingが評価されるまではアクセスできません

したがって、あなたの質問に答えるために、はい、letそしてconstホイストですが、実際の宣言が実行時に評価される前にそれらにアクセスすることはできません。


22

ES6Let付属する変数を紹介しますblock level scopingES5がなくなるまでblock level scoping、ブロック内で宣言された変数は常にhoisted関数レベルのスコープになります。

基本的にScopeは、プログラム内で変数が表示される場所を指し、宣言した変数を使用できる場所を決定します。ではES5、我々持っglobal scope,function scope and try/catch scopeて、ES6我々はまた、レッツを使用してブロックレベルのスコープを取得します。

  • varキーワードで変数を定義すると、定義された瞬間から関数全体がわかります。
  • letステートメントで変数を定義するとき、それはそれが定義されているブロックでのみ知られています。

     function doSomething(arr){
         //i is known here but undefined
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(var i=0; i<arr.length; i++){
             //i is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
    
         for(let j=0; j<arr.length; j++){
             //j is known here
         }
    
         //i is known here
         //j is not known here
    
         console.log(i);
         console.log(j);
     }
    
     doSomething(["Thalaivar", "Vinoth", "Kabali", "Dinesh"]);

コードを実行すると、変数jはでのみ認識され、loop前後では認識されないことがわかります。それでも、私たちの変数ientire function、それが定義された瞬間から知られています。

letを使用すると、新しいレキシカル環境を作成し、古い参照を保持するのではなく、新鮮な値をバインドするという、もう1つの大きな利点があります。

for(var i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

for(let i=1; i<6; i++){
   setTimeout(function(){
      console.log(i);
   },1000)
}

最初のforループは常に最後の値を出力しlet、新しいスコープを作成して新しい値をバインドしてusを出力し1, 2, 3, 4, 5ます。

になるとconstants、基本的にのようletに機能します。唯一の違いは、値を変更できないことです。定数では変更が許可されていますが、再割り当ては許可されていません。

const foo = {};
foo.bar = 42;
console.log(foo.bar); //works

const name = []
name.push("Vinoth");
console.log(name); //works

const age = 100;
age = 20; //Throws Uncaught TypeError: Assignment to constant variable.

console.log(age);

定数がを参照する場合object、それは常にを参照しますobjectが、objectそれ自体を変更できます(変更可能な場合)。イミュータブルをobject使いたい場合は、Object.freeze([])


5

MDN web docsから

ECMAScript 2015ではletconst巻き上げられますが初期化されません。変数宣言の前にブロック内の変数を参照するReferenceErrorと、変数がブロックの開始から宣言が処理されるまでの「一時的なデッドゾーン」にあるため、結果はa になります。

console.log(x); // ReferenceError
let x = 3;

0

es6では、letまたはconstを使用する場合、変数を使用する前に宣言する必要があります。例えば。1-

// this will work
u = 10;
var u;

// this will give an error 
k = 10;
let k;  // ReferenceError: Cannot access 'k' before initialization.

例えば。2-

// this code works as variable j is declared before it is used.
function doSmth() {
j = 9;
}
let j;
doSmth();
console.log(j); // 9
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.