[解決済み] JavaScriptの変数のスコープとは何ですか?
質問
javascriptの変数のスコープとは何ですか?関数の内部と外部で同じスコープを持つのでしょうか?あるいは、それは重要なことなのでしょうか?また、グローバルに定義されている場合、変数はどこに格納されるのでしょうか?
解決方法は?
TLDR
JavaScript にはレキシカル (静的) スコープとクロージャがあります。これは、ソースコードを見れば、識別子のスコープがわかるということです。
4つのスコープがあります。
- グローバル - すべてから見える
- 関数 - 関数(およびそのサブ関数とブロック)内で表示されます。
- ブロック - ブロック(およびそのサブブロック)内に表示されます。
- Module - モジュール内に表示されます。
グローバルスコープとモジュールスコープという特殊なケースを除いて、変数の宣言には
var
(関数スコープ)。
let
(ブロックスコープ)、および
const
(ブロックスコープ)。その他のほとんどの識別子宣言の形式は、ストリクトモードではブロックスコープを持ちます。
概要
スコープとは、ある識別子が有効なコードベースの領域を指します。
字句環境とは、識別子の名前とそれに関連する値の間のマッピングである。
スコープは、語彙的環境のリンクされた入れ子で形成され、入れ子の各レベルは、祖先の実行コンテキストの語彙的環境に対応する。
これらのリンクされた語彙環境は、スコープチェーンと呼ばれます。識別子の解決は、この連鎖に沿って一致する識別子を探す処理である。
識別子の解決は一方向にのみ行われる。このため、外側の語彙環境から内側の語彙環境を覗き込むことはできない。
を決定する上で、3つの適切な要素があります。 スコープ の 識別子 をJavaScriptで作成します。
識別子の宣言方法の一部を紹介します。
-
var
,let
とconst
- 関数パラメータ
- キャッチブロックパラメータ
- 関数宣言
- 名前付き関数式
-
グローバルオブジェクトに暗黙的に定義されたプロパティ(すなわち、欠落している
var
非厳密モード時) -
import
ステートメント -
eval
一部の場所の識別子を宣言することができます。
- グローバルコンテキスト
- 関数本体
- 通常のブロック
- 制御構造(ループ、if、whileなど)の先頭。
- 制御構造本体
- モジュール
宣言のスタイル
変数
を使用して宣言された識別子は
var
関数スコープを持つ
ただし、グローバルコンテキストで直接宣言された場合は、グローバルオブジェクトのプロパティとして追加され、グローバルスコープを持つようになります。この場合、それらはグローバルオブジェクトのプロパティとして追加され、グローバルスコープを持ちます。
eval
関数を使用することができます。
let と const
を使用して宣言された識別子は
let
と
const
ブロックスコープを持つ
ただし、グローバルコンテキストで直接宣言された場合は、グローバルスコープを持つ。
注
let
,
const
と
var
はすべてホイスト
. これは、論理的な定義位置が、そのスコープ(ブロックまたは関数)を囲む最上位にあることを意味します。しかし
let
と
const
は、ソースコード上で宣言されたポイントを制御が通過するまで、読み出しや代入ができません。この間の期間を時間的不感地帯といいます。
function f() {
function g() {
console.log(x)
}
let x = 1
g()
}
f() // 1 because x is hoisted even though declared with `let`!
関数パラメータ名
関数パラメータ名は、関数本体にスコープされます。これには若干の複雑さがあることに注意してください。デフォルトの引数として宣言された関数は パラメータ一覧 であり、関数本体ではありません。
関数の宣言
関数宣言は、strict モードではブロックスコープ、non-strict モードでは関数スコープを持ちます。注意:非厳密モードは、各ブラウザの歴史的な実装に基づく複雑なルールです。
名前付き関数式
名前付き関数式は自分自身にスコープされます(例:再帰のため)。
グローバルオブジェクトに暗黙的に定義されたプロパティ
非厳密モードでは、グローバルオブジェクトに暗黙的に定義されたプロパティは、グローバルスコープを持ちます。ストリクトモードでは、これらは許可されません。
評価
で
eval
文字列を使用して宣言された変数は
var
は現在のスコープに配置され、もし
eval
が間接的に使われている場合、グローバルオブジェクトのプロパティとして使われます。
例
以下のようにすると、名前
x
,
y
および
z
は関数の外では意味を持ちません。
f
.
function f() {
var x = 1
let y = 1
const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)
に対してReferenceErrorを投げます。
y
と
z
には適用されません。
x
の可視性が低下するためです。
x
はブロックによって制約されない。のような制御構造の本体を定義するブロックは、ブロックの制約を受けない。
if
,
for
および
while
と同じような動作をします。
{
var x = 1
let y = 1
const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope
以下では
x
がループの外側に見えるのは
var
は関数スコープを持っています。
for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)
...このような動作があるため、[ ]で宣言された変数のクローズには注意が必要です。
var
をループで使用することができます。変数のインスタンスは1つだけです。
x
ここで宣言されているのは、論理的にはループの外側に位置するものです。
次のように印刷されます。
5
を5回表示し、その後
5
に対して6回目の
console.log
ループの外側で
for(var x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop
次のように印刷されます。
undefined
なぜなら
x
はブロックスコープされています。コールバックは非同期に1つずつ実行されます。の新しい動作は
let
という名前の異なる変数を閉じる各匿名関数を意味します。
x
(を使用した場合とは異なり
var
というように、整数
0
を通して
4
が印刷されます。
for(let x = 0; x < 5; ++x) {
setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined
以下は
ReferenceError
の可視性が低下するため
x
はブロックの制約を受けません。しかし、このブロックは
undefined
というのは、変数が初期化されていないためです(
if
ステートメント)。
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
の先頭で宣言された変数は
for
を使用したループ
let
はループの本体にスコープされています。
for(let x = 0; x < 10; ++x) {}
console.log(typeof x) // undefined, because `x` is block-scoped
以下のようにすると
ReferenceError
の可視性が低いため
x
はブロックによって制約されます。
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
を使用して宣言された変数です。
var
,
let
または
const
はすべてモジュールにスコープされています。
// module1.js
var x = 0
export function f() {}
//module2.js
import f from 'module1.js'
console.log(x) // throws ReferenceError
を使用して宣言された変数は、グローバルオブジェクトのプロパティを宣言することになります。
var
は、グローバルコンテキストの中で、グローバルオブジェクトのプロパティとして追加されます。
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
と
const
の場合、グローバルオブジェクトにプロパティは追加されませんが、グローバルスコープを持ちます。
let x = 1
console.log(window.hasOwnProperty('x')) // false
関数のパラメータは、関数本体で宣言されていると考えてよいでしょう。
function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function
キャッチブロックのパラメータは、キャッチブロック本体にスコープされます。
try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block
名前付き関数式は、式そのものにのみスコープが設定されます。
(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression
非厳密モードでは、グローバルオブジェクトに暗黙的に定義されたプロパティは、グローバルにスコープされます。ストリクトモードでは、エラーが発生します。
x = 1 // implicitly defined property on the global object (no "var"!)
console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true
非厳密モードでは、関数宣言は関数スコープを持ちます。厳密モードでは、ブロックスコープを持ちます。
'use strict'
{
function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped
ボンネットの中の仕組み
スコープを定義するのは レキシカル 識別子が有効なコードの領域。
JavaScriptでは、すべての関数オブジェクトに、非表示の
[[Environment]]
の参照であり、その参照は
字句環境
の
実行コンテキスト
(スタックフレーム) 内で作成されました。
関数を呼び出すと、非表示の
[[Call]]
メソッドが呼び出されます。このメソッドは新しい実行コンテキストを作成し、新しい実行コンテキストと関数オブジェクトのレキシカル環境との間のリンクを確立します。これは
[[Environment]]
関数オブジェクトの値を
外部参照
フィールドを、新しい実行コンテキストの字句環境上に作成します。
なお、この新しい実行コンテキストと関数オブジェクトのレキシカル環境との間のリンクは クロージャ .
このように、JavaScript では、スコープは外部参照によって "chain" にリンクされた lexical 環境によって実装されています。この語彙環境の連鎖をスコープチェーンと呼び、識別子の解決は次のようにして行われます。 チェーンを検索する に一致する識別子を探す。
検索する もっと .
関連
-
nodejs unhandledPromiseRejectionWarning メッセージ
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] let "と "var "の使い分けは?
-
[解決済み] JavaScriptでオブジェクトをディープクローンする最も効率的な方法は何ですか?
-
[解決済み] とは何ですか! (not not)演算子とは何ですか?
-
[解決済み] 関数内でグローバル変数を使用する
-
[解決済み] C#がforeachで変数を再利用するのは理由があるのか?
-
[解決済み】JavaScriptの関数にデフォルトのパラメータ値を設定する
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
vue3レスポンシブ対応のためのsetup+ref+reactive
-
vueにおけるv-forループオブジェクトのプロパティ
-
Vueのフィルタの説明
-
Vueの一般的な組み込みディレクティブの説明
-
[解決済み] Error : 未定義のプロパティ 'map' を読み取ることができません。
-
[解決済み】gulp anythingを実行するたびに、アサーションエラーが発生します。- タスク関数を指定する必要があります
-
[解決済み】(Google Map API) Geocodeは以下の理由で成功しませんでした。REQUEST_DENIED
-
[解決済み】React Uncaught Error: 対象コンテナが DOM 要素でない [重複]。
-
フロントエンド null のプロパティ 'disabled' を読み取れない 問題が解決された
-
jq は html ページとデータを動的に分割する。