JS スコープに関する 5 つの落とし穴 (概要)
JavaScript では、ブロック、関数、またはモジュールによって変数のスコープが作成されます。たとえば、if
コード ブロックは、変数 message
のスコープを作成します。
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
message## は、## のスコープ内でアクセスできます。 #if
コード ブロック #。ただし、スコープ外では変数にアクセスできません。 さて、これでスコープについて簡単に説明しました。さらに詳しく知りたい場合は、私の記事
を読むことをお勧めします。 ここでは、JavaScript スコープが予想とは異なる動作をする 5 つの興味深い状況を紹介します。範囲についての理解を深めたり、単に面接の準備をするために、これらのケースを研究することもできます。
1.for var ループ内の変数 次のコード スニペットを考えてみましょう:
const colors = ['red', 'blue', 'white']; for (let i = 0, var l = colors.length; i < l; i++) { console.log(colors[i]); // 'red', 'blue', 'white' } console.log(l); // ??? console.log(i); // ???
l
変数と i
変数を出力するとどうなりますか?
##console.log(l)
は番号3 を出力しますが、
console.log(i ) は
ReferenceError をスローします。
l
var ステートメントを使用して宣言されます。すでにご存じのとおり、
var 変数
は、コード ブロックではなく、関数本体 のスコープによってのみ制限されます。
対照的に、変数 i
let ステートメントを使用して宣言されます。
let 変数はブロック スコープであるため、
i は
for ループ スコープ内でのみアクセスできます。
修正
l
宣言をvar l = Colors.length から
const l = Colors に変更します。長さ###。これで、変数 l
が for
ループ本体にカプセル化されました。 2. コード ブロックでの関数宣言
次のコード セグメント内: // ES2015 env
{
function hello() {
return 'Hello!';
}
}
hello(); // ???
ログイン後にコピーCall
hello()// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
は次のようになります。 ?
(コード スニペットは ES2015 環境で実行されます)回答
コード ブロックは関数宣言のスコープを作成するため、 ES2015 環境で実行される hello() を呼び出すと、
ReferenceError: hello is not generated が発生します。 興味深いことに、ES2015 より前の環境では、上記のコード スニペットを実行してもエラーはスローされません。 ###なぜなのかご存知ですか?以下のコメント欄に答えを書いてください。
3. モジュールはどこでインポートできますか?
コード ブロックでモジュールをインポートできますか?if (true) {
import { myFunc } from 'myModule'; // ???
myFunc();
}
ログイン後にコピー回答
上記のスクリプトはエラーをトリガーします: if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
'import' と 'export' はトップレベル にのみ表示される可能性があります。
モジュールは、モジュール ファイルの最上位スコープ (モジュール スコープ とも呼ばれます) にのみインポートできます。
import ステートメントを配置することです。import { myFunc } from 'myModule';
if (true) {
myFunc();
}
ログイン後にコピー
ES2015 のモジュール システムは静的です。コードを実行するのではなく、JavaScript ソース コードを分析してモジュールの依存関係を特定します。したがって、import { myFunc } from 'myModule'; if (true) { myFunc(); }
import ステートメントは実行時に実行されるため、コード ブロックまたは関数に含めることはできません。
4. 関数パラメータのスコープ
let p = 1;
function myFunc(p = p + 1) {
return p;
}
myFunc(); // ???
ログイン後にコピーmyFunc()
を呼び出すと何が起こるか? let p = 1; function myFunc(p = p + 1) { return p; } myFunc(); // ???
回答
myFunc() を呼び出すと、エラーがスローされます: ReferenceError: 初期化前に 'p' にアクセスできません
。これは、関数のパラメーターに独自のスコープ (関数スコープとは別) があるために発生します。引数
p = p 1 は
let p = p 1
p = p 1
を詳しく見てみましょう。
p を定義します。次に、JavaScript はデフォルト値式
p 1
p は作成されていますが、まだ初期化されていません (外部スコープ
let から変数にアクセスできません) p = 1)。したがって、初期化前に
p がアクセスされたというエラーがスローされます。
修正
let p = 1 の名前を変更し、関数パラメータ の名前も変更します。 p = p 1
。関数パラメーターの名前を変更することを選択しましょう:
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
関数パラメーターの名前が
p から q
に変更されます。myFunc() が呼び出されるとき、パラメータは指定されていないため、パラメータ
q はデフォルト値
p 1 に初期化されます。
p 1 を評価するには、外部スコープの変数
p にアクセスします:
p 1 = 1 1 = 2。
5. 函数声明与类声明
以下代码在代码块内定义了一个函数和一个类:
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
是否可以在块作用域之外访问 greet
和 Greeter
? (考虑 ES2015 环境)
答案
function
和 class
声明都是块作用域的。所以在代码块作用域外调用函数 greet()
和构造函数 new Greeter()
就会抛出 ReferenceError
。
6. 总结
必须注意 var
变量,因为它们是函数作用域的,即使是在代码块中定义的。
由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import
语法(以及 export
)。
函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。
在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。
希望这些陷阱能够帮你巩固作用域知识!
英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/
作者:Dmitri Pavlutin
更多编程相关知识,请访问:编程入门!!
以上がJS スコープに関する 5 つの落とし穴 (概要)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









typedef struct は、構造体の使用を簡素化するために構造体型のエイリアスを作成するために C 言語で使用されます。構造体の別名を指定することで、新しいデータ型を既存の構造体に別名付けします。利点としては、可読性の向上、コードの再利用、型チェックなどが挙げられます。注: エイリアスを使用する前に構造体を定義する必要があります。エイリアスはプログラム内で一意であり、宣言されているスコープ内でのみ有効である必要があります。

Java における変数の期待値の例外は、変数の初期化、null 値の使用、およびローカル変数のスコープの認識によって解決できます。

JavaScript クロージャーの利点には、変数スコープの維持、モジュール化コードの有効化、遅延実行、およびイベント処理が含まれますが、欠点としては、メモリ リーク、複雑さの増加、パフォーマンスのオーバーヘッド、およびスコープ チェーンの影響が挙げられます。

C++ の #include プリプロセッサ ディレクティブは、外部ソース ファイルの内容を現在のソース ファイルに挿入し、その内容を現在のソース ファイル内の対応する場所にコピーします。主に、コード内で必要な宣言を含むヘッダー ファイルをインクルードするために使用されます。たとえば、標準入出力関数を組み込むための #include <iostream> などです。

C++ スマート ポインターのライフ サイクル: 作成: スマート ポインターは、メモリが割り当てられるときに作成されます。所有権の譲渡: 移動操作を通じて所有権を譲渡します。リリース: スマート ポインターがスコープ外に出るか、明示的に解放されると、メモリが解放されます。オブジェクトの破壊: ポイントされたオブジェクトが破壊されると、スマート ポインターは無効なポインターになります。

できる。 C++ では、ネストされた関数の定義と呼び出しが可能です。外部関数は組み込み関数を定義でき、内部関数はスコープ内で直接呼び出すことができます。ネストされた関数により、カプセル化、再利用性、スコープ制御が強化されます。ただし、内部関数は外部関数のローカル変数に直接アクセスすることはできず、戻り値の型は外部関数の宣言と一致している必要があります。内部関数は自己再帰的ではありません。

JavaScript では、this のポインティング タイプには、1. グローバル オブジェクト、2. 関数呼び出し、4. イベント ハンドラー、5. アロー関数 (this の外側の継承) が含まれます。さらに、bind()、call()、および apply() メソッドを使用して、これが何を指すかを明示的に設定できます。

Vue では、let と var の間で変数を宣言するときのスコープに違いがあります。 スコープ: var にはグローバル スコープがあり、let にはブロック レベルのスコープがあります。ブロックレベルのスコープ: var はブロックレベルのスコープを作成しません。let はブロックレベルのスコープを作成します。再宣言: var は同じスコープ内の変数の再宣言を許可しますが、let は許可しません。
