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

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

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

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

Q&A

解決済

3回答

1245閲覧

JavaScript クロージャの説明サイトの分からないところ

slimat

総合スコア57

JavaScript

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

1グッド

1クリップ

投稿2019/03/31 08:40

編集2019/08/19 10:29

こんにちは。

JavaScriptの解説サイト: クロージャーがなぜ動くのか のクロージャの説明部分で以下のコードがあるのですが、コメントの「increment関数は外のスコープの変数countを参照している」で、なぜ"関数"が"変数"を参照できるという仕組みがあるのか分からないです。関数が変数を参照するということなど今まで見たことがないのですが、、、変数が値を参照するという仕組みがあることなら知っていますが、、、

JavaScript

1const createCounter = () => { 2 let count = 0; 3 return function increment() { 4 // `increment`関数は外のスコープの変数`count`を参照している 5 // これがクロージャーと呼ばれる 6 count = count + 1; 7 return count; 8 }; 9}; 10// createCounter()の実行結果は、内側で定義されていた`increment`関数 11const myCounter = createCounter(); 12// myCounter関数の実行結果は`count`の評価結果 13console.log(myCounter()); // => 1 14console.log(myCounter()); // => 2

よろしくお願い致します。

bochan2👍を押しています

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

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

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

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

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

m.ts10806

2019/03/31 08:47

なにかおかしいところがありますか?
slimat

2019/03/31 09:05

私は、変数は値を直接持たず、値が格納されているメモリ上のアドレスを持つと思っています。そして、変数を参照するとは、アドレスを用いて値にアクセスすることだと思っています。 そこで、関数自体が、上記の"変数のアドレス"を持つ(関数が変数を持つ)ということが、ありえないことだと思っています。
m.ts10806

2019/03/31 09:15

関数内で変数が宣言されているので当然かと思いますが(メモリ云々よりスコープの問題)
m.ts10806

2019/03/31 09:16

というか、これを突き詰めても何もならないと思いますが。別の言語と混同してませんか?
slimat

2019/03/31 09:25

言い方が良くありませんでした。increment関数がcountを参照しているのではなくて、increment関数内の"count"が外のスコープの変数countを参照しているのではないか、という疑問です。
slimat

2019/03/31 09:45

ご指摘ありがとうございます。修正致します。
ikedas

2019/03/31 10:19 編集

「参照している」という言葉に引っかかったようですが、これは正式な用語ではなく、その資料の筆者が勝手に使っているだけです。「その関数を含むスコープの中にある」とか、あるいはmaisumakunさんの説明の枠組みで言うなら「その関数が持っている環境に含まれている」とか解釈しておけばいいと思います。回答に仕上げる気力がないのでコメントにした。
think49

2019/03/31 10:34 編集

「参照している」はガベージコレクションによるメモリ管理で使われる事があります。関数が変数を参照していると、その変数はガベージコレクションの開放対象とならず、メモリを占有し続けます。
slimat

2019/03/31 10:48

今気付いたのですが、関数が変数を参照するとは、関数が変数名を参照していることであって、変数に代入されている値、を格納しているメモリの住所を参照している訳でないということなのでしょうか。
ikedas

2019/03/31 11:20

変数名。まあ近いですが、変数名が同じでもスコープが違えば「参照」できませんので、名前でもメモリの番地でもなく抽象的な「変数そのもの」ということになると思います。 >think49さん そうですね。ただここは文脈が違うと思います。
slimat

2019/03/31 11:53

了解しました。返信有難うございました。
slimat

2019/03/31 12:07

そもそも、関数はレキシカル?的にその中にある変数を、自分の関数の中に変数と認識しているんですね。 変数の方が、自分は関数の中で定義されていると認識していて、関数の方は含んでいる変数を認識していないものだと思っていました。
guest

回答3

0

JavaScriptの解説サイト: リンク内容のクロージャの説明部分で以下のコードがあるのですが、コメントの「increment関数は外のスコープの変数countを参照している」で、なぜ"関数"が"変数"を参照するのかが分からないです。

その節の少し先にあるスコープチェーンを読めば、関数から変数が参照される仕組みが分かります。

Re: slimat さん

投稿2019/03/31 09:46

think49

総合スコア18162

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

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

slimat

2019/03/31 09:49

回答ありがとうございます。読んでみたいと思います。
guest

0

そこで、関数自体が、上記の"変数のアドレス"を持つ(関数が変数を持つ)ということが、ありえないことだと思っています。

それが起きるのがJavaScriptです。

より言語に即して言うなら、関数は実行ごとにEnvironment Recordという、外部からは見えないオブジェクトを持っていて、関数のローカル変数はその*Environment Recordのプロパティとして扱われます。

投稿2019/03/31 09:24

maisumakun

総合スコア145183

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

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

maisumakun

2019/03/31 09:28

クロージャと関係なく、関数をネストしても外部の変数を参照できますが、これもEnvironment Recordが外部のEnvironment Recordを参照するという、プロトタイプ継承と同様な仕組みで実現されています。
slimat

2019/04/01 02:36

返信が遅くなって申し訳ございません。 関数が、その関数の中にレキシカル?的に存在している変数を「自分の関数の中に存在している変数だ」という認識をしているということでしょうか。私は逆に、関数内にある変数は「自分は関数の中にいる変数だ」という認識をしていると思っていたのですが(スコープチェーン)。
guest

0

ベストアンサー

increment関数は外のスコープの変数countを参照している

これは厳密には間違っています。
「increment関数内の変数countは、(関数ローカルでの宣言が無いため、)外のスコープの変数countを指している」ということでしょう。

「外のスコープの変数count」は、createCounterに代入された無名関数の関数ローカル変数ですので、その無名関数が呼び出される度に新しく作られます。普通は、関数ローカルの変数は、その関数からリターンしたときに廃棄されますが、内側の関数から参照されているので、その内側の関数が生きている限り(=呼び出し可能である限り)廃棄されません。

投稿2019/03/31 12:42

otn

総合スコア84499

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

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

slimat

2019/03/31 18:03

やはり、当該サイトのコード中のコメント、「`increment`関数は外のスコープの変数`count`を参照している」という文が示す、"関数自体が変数を参照"するというようなJavaScriptのシステムはなく。変数の持つ値、が格納されているメモリのアドレスを、結果的にグローバルスコープで利用できるようにする構文なので、このクロージャが成り立つという理解でよろしいでしょうか。
otn

2019/04/01 05:13 編集

> "関数自体が変数を参照"する まあ、言葉の綾としてはありでしょう。 「関数の中のコードが、外のスコープ関数countを参照する」を 「関数が、外のスコープ関数countを参照する」と略記したと考えればよい。 「自動車の中の人が、信号無視をする」⇒「自動車が、信号無視をする」 と同じ構造ですね。(自動運転自動車なら信号無視の主体にもなり得るが) > 変数の持つ値、が格納されているメモリのアドレスを、結果的にグローバルスコープで利用できるようにする構文なので、 これもニュアンスが違う。countを指すメモリのアドレス自体が、グローバルスコープで利用可能なわけではない。メモリのアドレスにアクセスする関数が、グローバルスコープで利用可能なだけです。もちろん、この例のように createCounter() が返す関数をグローバルスコープの変数に代入した場合に限っての話ですが。 つまり、「グローバル変数に代入した関数は、グローバルスコープで利用可能」というあまり意味のない言明です。
slimat

2019/04/01 07:47

> メモリのアドレスにアクセスする関数が、グローバルスコープで利用可能なだけです やはり、関数主体なのですね。 > countを指すメモリのアドレス自体が、グローバルスコープで利用可能なわけではない。 私が、countをグローバル変数であると考えてしまう、大きな間違いをしておりました。 > 「関数の中のコードが、外のスコープ関数countを参照する」を「関数が、外のスコープ関数countを参照する」と略記したと考えればよい。 ずばり、そういうことでしたか。あと、"関数count"ではなく"変数count"でしょうか。 分かりやすい解説でした。ご丁寧に有難うございました。
otn

2019/04/01 09:18

あ、関数countは、ご察しの通り誤記です。 countはグローバル変数じゃなくて、関数のローカル変数なので、外側の関数を複数回呼び出せば、同名の別の変数が作られるんですよね。 const myCounter1 = createCounter(); const myCounter2 = createCounter(); とすれば、2つのカウンター関数を別々に使えます。
slimat

2019/04/01 09:31

了解しました。最後まで有難うございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問