ホームページ >ウェブフロントエンド >jsチュートリアル >JS スコープに関する 5 つの落とし穴 (概要)

JS スコープに関する 5 つの落とし穴 (概要)

青灯夜游
青灯夜游転載
2020-12-25 17:32:065154ブラウズ

JS スコープに関する 5 つの落とし穴 (概要)

JavaScript では、ブロック、関数、またはモジュールによって変数のスコープが作成されます。たとえば、if コード ブロックは、変数 message のスコープを作成します。

if (true) {
  const message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError

message## は、## のスコープ内でアクセスできます。 #if コード ブロック #。ただし、スコープ外では変数にアクセスできません。 さて、これでスコープについて簡単に説明しました。さらに詳しく知りたい場合は、私の記事

JavaScript スコープの簡単な言葉で説明する

を読むことをお勧めします。 ここでは、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]); // &#39;red&#39;, &#39;blue&#39;, &#39;white&#39;
}
console.log(l); // ???
console.log(i); // ???

l

変数と i 変数を出力するとどうなりますか?

答え

##console.log(l)

は番号

3 を出力しますが、console.log(i )ReferenceError をスローします。 l

変数は、

var ステートメントを使用して宣言されます。すでにご存じのとおり、var 変数 は、コード ブロックではなく、関数本体 のスコープによってのみ制限されます。 対照的に、変数 i

let ステートメントを使用して宣言されます。 let 変数はブロック スコープであるため、ifor ループ スコープ内でのみアクセスできます。 修正

l

宣言を

var l = Colors.length から const l = Colors に変更します。長さ###。これで、変数 lfor ループ本体にカプセル化されました。 2. コード ブロックでの関数宣言

次のコード セグメント内:
// ES2015 env
{
  function hello() {
    return &#39;Hello!&#39;;
  }
}

hello(); // ???
Call

hello()

は次のようになります。 ?

(コード スニペットは ES2015 環境で実行されます)

回答

コード ブロックは関数宣言のスコープを作成するため、 ES2015 環境で実行される hello() を呼び出すと、

ReferenceError: hello is not generated

が発生します。 興味深いことに、ES2015 より前の環境では、上記のコード スニペットを実行してもエラーはスローされません。 ###なぜなのかご存知ですか?以下のコメント欄に答えを書いてください。

3. モジュールはどこでインポートできますか?

コード ブロックでモジュールをインポートできますか?

if (true) {
  import { myFunc } from &#39;myModule&#39;; // ???
  myFunc();
}
回答

上記のスクリプトはエラーをトリガーします:

'import' と 'export' はトップレベル にのみ表示される可能性があります。

モジュールは、モジュール ファイルの最上位スコープ (

モジュール スコープ とも呼ばれます) にのみインポートできます。

修正

モジュールは常にモジュール スコープからインポートします。もう 1 つの良い習慣は、ソース ファイルの先頭に

import ステートメントを配置することです。
import { myFunc } from &#39;myModule&#39;;

if (true) {
  myFunc();
}

ES2015 のモジュール システムは静的です。コードを実行するのではなく、JavaScript ソース コードを分析してモジュールの依存関係を特定します。したがって、

import ステートメントは実行時に実行されるため、コード ブロックまたは関数に含めることはできません。

4. 関数パラメータのスコープ

次の関数について考えてみましょう:

let p = 1;

function myFunc(p = p + 1) {
  return p;
}

myFunc(); // ???
myFunc()

を呼び出すと何が起こるか?

回答

関数

myFunc() を呼び出すと、エラーがスローされます: ReferenceError: 初期化前に 'p' にアクセスできません

これは、関数のパラメーターに独自のスコープ (関数スコープとは別) があるために発生します。引数 p = p 1let 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(); // ???

是否可以在块作用域之外访问 greetGreeter(考虑 ES2015 环境)

答案

functionclass 声明都是块作用域的。所以在代码块作用域外调用函数 greet() 和构造函数 new Greeter() 就会抛出 ReferenceError

6. 总结

必须注意 var 变量,因为它们是函数作用域的,即使是在代码块中定义的。

由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import 语法(以及 export)。

函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。

在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。

希望这些陷阱能够帮你巩固作用域知识!

英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/

作者:Dmitri Pavlutin

更多编程相关知识,请访问:编程入门!!

以上がJS スコープに関する 5 つの落とし穴 (概要)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。