質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

Q&A

解決済

1回答

327閲覧

javascriptのfor文のブロックスコープについて

Artz

総合スコア158

スコープ

スコープとは、プログラム内で変数名など、参照可能な有効範囲のことを指します。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

ループ

ループとは、プログラミングにおいて、条件に合致している間、複数回繰り返し実行される箇所や、その制御構造を指します

クロージャ

クロージャは、プログラミング言語における関数オブジェクトの一種です。 引数以外の変数を実行時の環境ではなく、 自身が定義された環境において解決することを特徴とします。

1グッド

3クリップ

投稿2018/02/10 14:32

javascript初心者です。
クロージャやらスコープ周りを勉強していると混乱してきました。。。
どなたかお助けください。。。

質問1

まずクロージャの理解が合ってるか知りたいです。
例えば次の様にクロージャを生成する関数があったとします。

function testScope () { let funcs = {} let n //(外側のn) for (n = 0; n < 3; n++) { funcs[n] = () => n //(nを閉じ込め) } return funcs } console.log(testScope())

funcs[1]funcs[3]にクローズされた変数nは、それぞれ外側のnを参照するので、結果同じ値を取るという認識でおります。
イメージ説明

n++されて3となったnを見てる、と捉えているんですが合ってますでしょうか。。

質問2

for文でletを使うと何が起こっているのか知りたいです。
上記例でfuncs[1]1を、funcs[2]2を保持しようとすると次の様にできると思います。

function testScope () { let funcs = {} for (let n = 0; n < 3; n++) { //外側でなく、for初期化でnをletで宣言 funcs[n] = () => n } return funcs } console.log(testScope())

するとコンソールログ上で狙いの挙動になります。
※ChromeDevToolで見るとトップレベルが{0:f,1:f,2:f}からObjectになってますが、スコープのタイプがClosureからBlockに変わったことと関係あるんでしょうか???)
イメージ説明

ここで、なぜ各ループでnが使われているのに、結果的に違うnを参照できてるんでしょうか。
これはつまり「次のループに移るとスコープが変わってる」ということでしょうか??????
だとすれば(実際に行われてる内部処理は分かりませんが)for文は次のようなブロックスコープを生成するイメージで合ってますか?

function testScope () { let funcs = {} { let n = 0 funcs[n] = () => n } { let n = 1 //前回ループとは別のn funcs[n] = () => n } { let n = 2 funcs[n] = () => n } return funcs }

諸々理解が追いついておらず質問が的を射てないかもしれませんが、何卒ご回答の程よろしくお願いします。。。。。

jun68ykt👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

1番目の場合、letはループと別枠で存在する変数ですので、ループ構文で書き換えても特殊な動きをすることはなく、抜けきった後の3となります。

forの中にletを書いた場合、かなり特殊な動作となります。ES2017の仕様書(かなり重いです)を確認してみたところ、

  1. ループのfor文自体と、ループのブロック1回ごとがそれぞれスコープを生成する(変数宣言がletの場合のみ、constならfor文全体で1スコープ)
  2. ループ変数の加算は、前のループでの値を次のループのスコープにコピーしてから行う

というような動作でした。

投稿2018/02/10 15:04

maisumakun

総合スコア145123

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2018/02/10 15:04

うーん、やっぱり仕様書は厳密なだけあって難解だ…
Artz

2018/02/10 15:09

まさに求めていたご回答をありがとうございます! 相当特殊なんですね。。。 そして私は仕様書を読むべきだったんですね、リンクを教えていただきありがとうございます。骨が折れそうですがじっくり見てみたいと思います。。。。
jun68ykt

2018/02/10 16:07 編集

横から失礼いたします。 クロージャーとブロック、スコープについて深掘りした、有用なご質問とご回答、ありがとうございます。 maisumakunさんのご回答の > ループのfor文自体と、ループのブロック1回ごとがそれぞれスコープを生成する(変数宣言がletの場合のみ、constならfor文全体で1スコープ) ということは、Artzさんのご質問にある、 > for文は次のようなブロックスコープを生成するイメージで合ってますか? の答えは「合ってます」ということになりそうですね。
Artz

2018/02/10 16:17

ありがとうございます! そうみたいでよかったです。 なかなかこの点に踏み込んでるソースがなくて参っていました。 ※この暗号の様な仕様書はさらなる混乱を招きそうですが...
jun68ykt

2018/02/12 04:29 編集

再度、横から失礼致します。 追加情報になりますが、JSの基礎についての書籍 「徹底マスター JavaScriptの教科書」初版 http://amzn.asia/50W1UCa の 195〜196ページに、このご質問と同じ論点の説明とコード例がありました。 以下の3行は同著 P196 からの引用です。 > ECMAScript 6 では、 for文の初期化式で宣言された変数は、 > 「各ループごとに宣言されて値が代入される」 > というように変更されました。 上記の動きを説明するために挙げられているコードも、Artzさんの > for文は次のようなブロックスコープを生成するイメージ と同様のものになっていますので、機会があれば ご覧になるとよいかもしれません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問