関数内でletを使用して宣言された変数の一部が別の関数で使用できるようになるのに、他の変数は参照エラーになるのはなぜですか?


158

関数内で宣言されたときに変数がなぜ奇妙に動作するのか理解できません。

  1. first機能私は宣言しletた変数bc値で10

    b = c = 10;

    second機能私が示しています。

    b + ", " + c

    そしてこれは示しています:

    10, 10
  2. また、first関数aでは値10で宣言します。

    let a = b = c = 10;

    しかし、 second関数ではエラーが表示されます。

    変数が見つかりません: a

  3. 今、firstdは値20で宣言する関数で:

    var d = 20;

    しかし、second関数では以前と同じエラーが表示されますが、変数はd次のとおり。

    変数が見つかりません: d

例:

function first() {
  let a = b = c = 10;
  var d = 20;
  second();
}

function second() {
  console.log(b + ", " + c); //shows "10, 10"

  try{ console.log(a); }  // Rreference error
  catch(e){ console.error(e.message) }

  try{ console.log(d); } // Reference error
  catch(e){ console.error(e.message) }
}
first()


31
キーワードが前に付いていないためb、グローバルを宣言しています。とにローカルです。cvaradfirst
VLAZ

1
コメントは詳細な議論のためのものではありません。これが良いインタビューの質問になるかどうかに関する接線の会話がチャットにアーカイブされました
コーディグレイ

1
これは、Visual Basicでの同様の状況を思い出させます。Dim Apple, Banana, Pear As Fruit意味しDim Apple / Dim Banana / Dim Pear As Fruit、ではないDim Apple As Fruit / ...
Eric Lippert

回答:


179

それはあなたが実際に言っているからです:

c = 10;
b = c;
let a = b;

そして、あなたが言っていると思うことではありません。

let a = 10;
let b = 10;
let c = 10;

チェーンに追加する変数の数に関係なく、エラーを引き起こすのは最初の(a)に過ぎないことに気付くでしょう。

これは、「let」が変数を宣言するブロック(または、「ローカル」、多かれ少なかれ「括弧内」を意味する)にスコープを設定するためです。

「let」なしで変数を宣言すると、変数のスコープがグローバルになります。

したがって、変数を設定する関数では、すべての値が10になります(ブレークポイントを設定すると、デバッガーでこれを確認できます)。最初の関数にa、b、cのコンソールログを配置すると、問題はありません。

しかし、その関数を終了するとすぐに、最初の関数(a)は、繰り返しになりますが、技術的には割り当て順に、最後の関数です。「消えます」(これも、デバッガが2番目の関数にブレークポイントを設定した場合)、他の2つ(または追加した数)は引き続き使用できます。

これは、チェーン内の最初の変数(つまり、技術的に最後に宣言され、値が割り当てられる最後の変数)にのみ適用できるようにするためです(ローカルスコープのみ)。残りは技術的にそれらの前に「レット」がありません。したがって、これらは技術的にグローバルに(つまり、グローバルオブジェクトで)宣言されているため、2番目の関数に表示されます。

試してみましょう:「let」キーワードを削除します。すべての変数が利用可能になります。

"var"も同様のローカルスコープ効果を持っていますが、変数の "巻き上げ"の方法が異なります。これは、明確に理解する必要がありますが、質問には直接関係しません。

(ところで、この質問はそれを良いものにするのに十分なプロのJS開発者を困惑させるでしょう)。

JSで変数を宣言する方法の違いに時間を費やすことを強くお勧めします。キーワードなし、「let」あり、「var」あり。


4
それは同時にプログラミングに関して最良かつ最悪のことでもあります。コンピュータはあなたが指示したとおりに動作します。必ずしもあなたがそれを行うように意図したものではありません。プログラムは完璧です。問題を作成します。
Niet the Dark Absol

8
@Thevsなぜあなたはこの答えの文脈で推薦varするのletですか?分からない。
Klaycon

4
@Thevs私はあなたに強く同意しません。var不注意に使用すると、バグが発生しやすくなります。このフィドルを
Cid

2
何をするかの場合には@Thevs var持っている任意のよりも有利let?はっきりさせておきましょう:両方がオプションであり、私書くべきコードを求めている現代の状況です。私は前にこれを求めてきましたときに、私は「あなたと変数を再宣言することができますについての回答を受け取ったvar、その場合には、私がいることを人々に思い出させるために持っている」あなたは再宣言した変数であってはならないが。これは、コードのロジックのバグまたはエラーのいずれかです。したがって、再宣言の利点は...欠陥のあるコードを記述できることです。がオプションであるvar場合に賛成する合理的な理由はまだ見ていませんlet
VLAZ

2
JavaScriptに対する別のマークをチョークします。ローカルで宣言されない限り、すべての変数はグローバルです。:(
JRE

68

関数ではfirst()、変数bcは、varまたはを使用せずに、その場で作成されletます。

let a = b = c = 10; // b and c are created on the fly

とは違う

let a = 10, b = 10, c = 10; // b and c are created using let (note the ,)

それらは暗黙のグローバルになります。それが彼らが利用できる理由ですsecond()

ドキュメントから

宣言されていない変数に値を割り当てると、割り当ての実行時に暗黙的にグローバル変数として作成されます(グローバルオブジェクトのプロパティになります)。

これを回避"use strict"するには、宣言されていない変数を使用するとエラーになるエラーを使用できます

"use strict"; // <-------------- check this

function first() {
   /*
    * With "use strict" c is not defined.
    * (Neither is b, but since the line will be executed from right to left,
    * the variable c will cause the error and the script will stop)
    * Without, b and c become globals, and then are accessible in other functions
    */
   let a = b = c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //reference error
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


15
さらに:let a = 10, b = 10, c = 10;またはlet a, b, c; a = b = c = 10;、そうでなければ変数を宣言する正しい方法になります。
リッカードエリマ

厳密モードでは、変数bはどうでしょうか?
Tick20

2
@ Tick20変数bは評価/到達されず、エラーは行let a = b = c = 10;で発生し、右から左に読み取られます。cを引き起こす最初の変数ReferenceErrorであるため、行の残りの部分は実行されません(スクリプトは停止しました)
Cid

2
のようなものlet a = 10, b = a, c = b;も有効です
Kaddath

8
主に「厳格な使用」のために賛成。インタビューの質問の文脈では、それもこのコードに関する私のコメントの始まりです。
Pac0

23

奇妙なことを呼ぶ前に、まずいくつかの基本を知っておきましょう:

varletはどちらもJavaScriptの変数宣言に使用されます。例えば、

var one = 1;
let two = 2;

変数は、varまたはを使用せずに宣言することもできletます。例えば、

three = 3;

上記のアプローチの違いは次のとおりです。

var 関数スコープです

そして

let ブロックスコープです。

var/ letキーワードなしで宣言された変数のスコープはグローバルになりますそれが宣言されている場所に関係なくます。

グローバル変数は、Webページのどこからでもアクセスできます(グローバルは誤って変更される可能性があるためお勧めしません)。

これらの概念に従って、問題のコードを見てみましょう。

 function first() {
   let a = b = c = 10;
   /* The above line means:
    let a=10; // Block scope
    b=10; // Global scope
    c=10; // Global scope
    */

   var d = 20; // Function scope
   second();
}

function second() {
   alert(b + ", " + c); // Shows "10, 10" //accessible because of global scope
   alert(a); // Error not accessible because block scope has ended
   alert(d); // Error not accessible because function scope has ended
}

1
JonoJamesの回答の一部を盗用しました。どうして?
Peter Mortensen、

2
申し訳ありませんが、そのような意図はありませんでした。同じソースから情報を収集した可能性があるため、同様のものが存在する可能性があります。
fatimasajjad

2
どちらの回答にも、明確な引用なしに同じ元のソースからコピーされたコンテンツが含まれている可能性があります-おそらくtutorialsteacher.com/javascript/javascript-variable。文法エラーが元から再生されるので、盗作の存在は、明白である- 「変数のスコープはせずに宣言var、宣言されている場所のグローバル関係なくなっキーワード」のいずれかでなければなりません「スコープ...となり」または「スコープ...になります」。他人の正確な言葉を使用するには、ここからであろうとどこからであろうと、引用が必要です。meta.stackexchange.com/q/160071/211183
マイケル-sqlbot

みんなありがとう、ソースの参照リンクを追加しました。
fatimasajjad

6

を使用する変数 letキーワードは、ブロックのスコープ内でのみ使用でき、外部関数では使用できません...

その方法で宣言している各変数は、letまたはvar。変数宣言にコンマがありません。

キーワードなしで変数を宣言することは推奨さませんvar。既存のグローバル変数を誤って上書きする可能性があります。varキーワードなしで宣言された変数のスコープは、宣言された場所に関係なくグローバルになります。グローバル変数には、Webページのどこからでもアクセスできます。

function first() {
   let a = 10;
   let b = 10;
   let c = 10;
   var d = 20;
   second();
}

function second() {
   console.log(b + ", " + c); //shows "10, 10"
   console.log(a); //reference error
   console.log(d); //reference error
}

first();


3

これは、使用しない場合、letまたはvar変数がオンザフライで宣言されている場合に、以下のように宣言する方がよいためです。

let a = 10;
let b = 10;
let c = 10;

2

奇妙な問題は、JavaScriptのスコープルールによって引き起こされます

function first() {
   let a = b = c = 10; // a is in local scope, b and c are in global scope
   var d = 20; // d is in local scope
   second(); // will have access to b and c from the global scope
}

同じ値(100)に初期化された3つのローカル変数を宣言するとします。first()は以下のようになります。この場合、second()はどの変数にもアクセスできません。、first()に対してローカルである

function first() {
   let a = 100; // a is in local scope init to 100
   let b = a; // b is in local scope init to a
   let c = b // c is in local scope init to b

   var d = 20; // d is in local scope
   second(); // will not have access a, b, c, or d
}

ただし、グローバル変数が必要な場合は、first()は次のようになります。彼らはしているので、この場合、第2は、すべての変数にアクセスする必要がありますグローバルスコープにある

function first() {
   a = 100; // a is in global scope
   b = a; // b is in global scope
   c = b // c is in global scope

   d = 20; // d is in global scope
   second(); // will have access to a, b, c, and d from the global scope
}

ローカル変数(宣言されているコードブロックでアクセス可能)。
コードブロックは、コードの行が間にある任意の{}です。

  • function(){ここでは、var、let、constは関数全体にアクセス可能}、
  • for(){ここのvarは外部スコープにアクセス可能、let、constはここでのみアクセス可能}、

グローバル変数(グローバルスコープでアクセス可能)。
これらの変数は、グローバルオブジェクトにアタッチされます。グローバルオブジェクトは環境に依存します。これはブラウザのウィンドウオブジェクトです。

特記事項: var、let、constキーワードを使用せずに、JavaScriptで変数を宣言できます。この方法で宣言された変数はグローバルオブジェクトにアタッチされるため、グローバルスコープでアクセスできます。
a = 100 // is valid and is in global scope

さらに読書のためのいくつかの記事: https://www.sitepoint.com/demystifying-javascript-variable-scope-hoisting/ https://scotch.io/tutorials/understanding-scope-in​​-javascript ます。https://www.digitalocean .com / community / tutorials / understanding-variables-scope-hoisting-in-javascript


0

主な違いはスコーピングルールです。varキーワードで宣言された変数のスコープは、直接の関数本体(つまり、関数スコープ)ですが、変数のスコープは、{}で示される直接の囲みブロック(つまり、ブロックスコープ)です。そしてあなたが言うとき

c = 10;
b = c;
let a = b;

cとbにはファンと同じように寿命がありますが、aにはブロックスパンしかありません。参照してaにアクセスしようとすると、常にエラーが表示されますが、cとbはグローバルなのでエラーは発生しません。チェーンに追加する変数は、エラーを引き起こすのは最初の(a)のみです。これは、「let」が変数をブロックにスコープするためです(または「ローカル」、多かれ少なかれ「括弧内」を意味します)。 "let"なしで変数を宣言すると、変数のスコープがグローバルになります。そのため、変数を設定する関数では、すべての値が10になります(これをデバッガーで確認すると、ブレークポイント)。最初の関数にa、b、cのコンソールログを配置すると、問題はありませんが、その関数を終了するとすぐに、最初の関数(a)を繰り返します。


0

JavaScriptでの変数宣言の3つの興味深い側面は次​​のとおりです。

  1. var は、変数のスコープを、それが定義されているブロックに制限します。( 'var'ローカルスコープ用です。)

  2. letができます一時的なオーバーライドブロック内の外部変数の値のを。

  3. varまたはletなしで変数を宣言すると、宣言されている場所に関係なく、変数がグローバルになります。

これが最新の言語追加であるletのデモです。

// File name:  let_demo.js

function first() {
   a = b = 10
   console.log("First function:    a = " + a)
   console.log("First function:    a + b = " + (a + b))
}

function second() {
    let a = 5
    console.log("Second function:    a = " + a)
    console.log("Second function:    a + b = " + (a + b))
}

first()   

second()

console.log("Global:    a = " + a)
console.log("Global:    a + b = " + (a + b))

出力:

$ node let_demo.js 

First function:    a = 10
First function:    a + b = 20

Second function:    a = 5
Second function:    a + b = 15

Global:    a = 10
Global:    a + b = 20

説明:

変数aおよびbは、varまたはletキーワードなしで、「first()」内で宣言されました。

したがって、abはグローバルであるため、プログラム全体でアクセスできます。

名前の関数では「第2」、声明「= 5を聞かせては」一時的に「の値に設定」から「5のみの機能の範囲内で、」を。

' second() ' のスコープ外では、IE、グローバルスコープでは、 ' a 'の値は先に定義したとおりになります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.