検索
ホームページCMS チュートリアル&#&プレスJavaScript のスコープを理解する

JavaScript のスコープを理解する

Aug 31, 2023 pm 08:17 PM
JavaScript スコープスコープチェーンスコープのルール

JavaScript のスコープを理解する

スコープ、または変数を配置する場所を決定する一連のルールは、プログラミング言語の最も基本的な概念の 1 つです。実際、これは非常に基本的なものであるため、私たちはこれらのルールがどれほど微妙であるかを簡単に忘れてしまいがちです。

JavaScript エンジンがスコープをどのように「考える」かを正確に理解することで、ホイスティングの記述から生じる可能性のあるよくある間違いを回避し、クロージャに集中する準備を整え、二度とホイスティングを記述しないという事態に一歩近づくことができます。

...とにかく、持ち上げたり閉じたりすることが理解できるようになります。

この記事では次のことについて学びます:

    JavaScript のスコープの基本知識
  • インタープリタがどの変数がどのスコープに属するかを決定する方法
  • 巻き上げの実際の動作原理
  • ES6 キーワード
  • letconst がゲームをどのように変えるか
詳しく見てみましょう。

ES6 についてさらに詳しく知り、構文と機能を活用して JavaScript コードを改善および簡素化する方法に興味がある場合は、次の 2 つのコースをチェックしてみてはいかがでしょうか:

語彙範囲

これまでに JavaScript 行を書いたことがある場合は、

変数を定義する場所によって、変数を 使用できる場所が決まることがわかるでしょう。変数の可視性がソース コードの構造に依存するという事実は、lexical scope と呼ばれます。 JavaScript でスコープを作成するには 3 つの方法があります:

    関数を作成します
  1. 。関数内で宣言された変数は、入れ子になった関数内を含め、その関数内でのみ表示されます。
  2. let
  3. または const を使用します。 コード ブロック内で変数を宣言します。このような宣言はブロック内でのみ表示されます。 作成
  4. キャッチ
  5. ブロック。信じられないかもしれませんが、これは実際に 本当に 新しいスコープを作成します。 リーリー 上記のコード スニペットは、3 つのスコープ設定メカニズムすべてを示しています。 Node または Firefox で実行できますが、Chrome はまだ let
  6. とうまく連携しません。

これらのそれぞれについて詳しく説明します。まず、JavaScript がどの変数がどのスコープに属するかを決定する方法を詳しく見てみましょう。 コンパイルプロセス: 鳥瞰図

JavaScript を実行すると、正しく動作するために 2 つのことが起こります。

まず、ソース コードをコンパイルします。

次に、コンパイルされたコードが実行されます。
  1. コンパイル
  2. ステップ
中に、JavaScript エンジン:

すべての変数名を書き留めます それらを適切なスコープに登録します

    独自の価値観のためのスペースを確保する
  1. 実行中のみ
  2. JavaScript エンジンは、変数参照の値をその割り当てられた値と等しくなるように実際に設定します。それまでは、
  3. 未定義
です。

ステップ 1: コンパイル リーリー コンパイラーの役割を段階的に理解しましょう。

まず、

var first_name = "Peleke"

という行を読み取ります。次に、変数が保存される

scope

を決定します。私たちはスクリプトの最上位にいるため、

グローバル スコープ内にあることが認識されます。次に、変数 first_name をグローバル スコープに保存し、その値を unknown に初期化します。 2 番目に、コンパイラは function Popup (first_name) を含む行を読み取ります。 function

キーワードは行の最初にあるため、関数の新しいスコープを作成し、関数の定義をグローバル スコープに登録し、内部を調べて変数宣言を見つけます。

案の定、コンパイラはそれを見つけました。関数の最初の行に var last_name = "Sengstacke" があるため、コンパイラは変数 last_name

<code class="inline"> class= のスコープに保存します。 「 inline">popup ではなく をグローバル スコープに設定し、その値を unknown に設定します。 関数内には変数宣言がなくなったため、コンパイラはグローバル スコープに戻ります。これ以上変数宣言がないので、このフェーズは完了です。 実際にはまだ何も実行していないことに注意してください。 この時点でのコンパイラの仕事は、全員の名前を確実に認識することだけであり、全員が何をするかは気にしません。

この時点で、プログラムは次のことを認識しています:

  1. 全局范围内有一个名为 first_name 的变量。
  2. 全局范围内有一个名为 popup 的函数。
  3. popup 范围内有一个名为 last_name 的变量。
  4. first_namelast_name 的值均为 undefined

它并不关心我们是否在代码中的其他地方分配了这些变量值。引擎在执行时会处理这个问题。

第 2 步:执行

在下一步中,引擎再次读取我们的代码,但这一次,执行它。

首先,它读取行 var first_name = "Peleke"。为此,引擎会查找名为 first_name 的变量。由于编译器已经用该名称注册了一个变量,引擎会找到它,并将其值设置为 "Peleke"

接下来,它读取行 function popup (first_name)。由于我们没有在这里执行该函数,因此引擎不感兴趣并跳过它。

最后,它读取行 popup(first_name)。由于我们在这里执行一个函数,因此引擎:

  1. 查找 popup 的值
  2. 查找 first_name 的值
  3. popup 作为函数执行,并传递 first_name 的值作为参数

当执行 popup 时,它会经历相同的过程,但这次是在函数 popup 内。它:

  1. 查找名为 last_name 的变量
  2. last_name 的值设置为等于 "Sengstacke"
  3. 查找 alert,将其作为函数执行,并以 "Peleke Sengstacke" 作为参数

事实证明,幕后发生的事情比我们想象的要多得多!

既然您已经了解了 JavaScript 如何读取和运行您编写的代码,我们就准备好解决一些更贴近实际的问题:提升的工作原理。

显微镜下的吊装

让我们从一些代码开始。

bar();

function bar () {
    if (!foo) {
        alert(foo + "? This is strange...");
    }
    var foo = "bar";
}

broken(); // TypeError!
var broken = function () {
    alert("This alert won't show up!");
}

如果运行此代码,您会注意到三件事:

  1. 在分配之前,您可以引用 foo,但其值为 undefined
  2. 您可以在定义之前调用 已损坏的,但您会收到 TypeError
  3. 可以在定义之前调用 bar,它会按需要工作。

提升是指 JavaScript 使我们声明的所有变量名称在其作用域内的任何地方都可用,包括在我们分配给它们之前 .

代码段中的三种情况是您在自己的代码中需要注意的三种情况,因此我们将一一逐步介绍它们。

提升变量声明

请记住,当 JavaScript 编译器读取像 var foo = "bar" 这样的行时,它会:

  1. 将名称 foo 注册到最近的范围
  2. foo 的值设置为未定义

我们可以在赋值之前使用 foo 的原因是,当引擎查找具有该名称的变量时,它确实存在。这就是为什么它不会抛出 ReferenceError

相反,它获取值 undefined,并尝试使用该值执行您要求的任何操作。通常,这是一个错误。

记住这一点,我们可能会想象 JavaScript 在我们的函数 bar 中看到的更像是这样:

function bar () {
    var foo; // undefined
    if (!foo) {
        // !undefined is true, so alert
        alert(foo + "? This is strange...");
    }
    foo = "bar";
}

如果您愿意的话,这是提升的第一条规则:变量在其整个范围内都可用,但其值为 undefined ,直到您代码分配给他们。

常见的 JavaScript 习惯用法是将所有 var 声明写入其作用域的顶部,而不是首次使用它们的位置。用 Doug Crockford 的话来说,这可以帮助您的代码阅读更像它运行

仔细想想,这是有道理的。当我们以 JavaScript 读取代码的方式编写代码时,为什么 bar 的行为方式非常清楚,不是吗?那么为什么不一直这样写呢?

提升函数表达式

事实上,当我们在定义之前尝试执行 broken 时,我们得到了 TypeError,这只是第一条提升规则的一个特例。

我们定义了一个名为 broken 的变量,编译器会在全局范围内注册该变量,并将其设置为等于 undefined。当我们尝试运行它时,引擎会查找 broken 的值,发现它是 undefined,并尝试将 undefined 作为函数执行.

显然,undefined 不是一个函数,这就是为什么我们得到 TypeError

提升函数声明

最后,回想一下,在定义 bar 之前,我们可以调用它。这是由于第二条提升规则:当 JavaScript 编译器找到函数声明时,它会使其名称和定义在其作用域的顶部可用。再次重写我们的代码:

function bar () {
    if (!foo) {
        alert(foo + "? This is strange...");
    }
    var foo = "bar";
}

var broken; // undefined

bar(); // bar is already defined, executes fine

broken(); // Can't execute undefined!

broken = function () {
    alert("This alert won't show up!");
}

同样,当您编写作为JavaScript读取时,它更有意义,您不觉得吗?

查看:

  1. 变量声明和函数表达式的名称在其整个范围内都可用,但它们的值在赋值之前为 undefined
  2. 函数声明的名称​​和定义在其整个范围内都可用,甚至在其定义之前。

现在让我们来看看两个工作方式稍有不同的新工具:letconst

<strong>let</strong><strong></strong>const 和临时死区强>强>

var 声明不同,使用 letconst 声明的变量 被编译器提升。

至少,不完全是。

还记得我们如何调用 已损坏的,但却因为尝试执行 undefined 而收到 TypeError 吗?如果我们使用 let 定义 broken,我们就会得到 ReferenceError,而不是:

"use strict"; 
// You have to "use strict" to try this in Node
broken(); // ReferenceError!
let broken = function () {
    alert("This alert won't show up!");
}

当 JavaScript 编译器在第一遍中将变量注册到其作用域时,它对待 letconst 的方式与处理 var 的方式不同。

当它找到 var 声明时,我们将该变量的名称注册到其范围,并立即将其值初始化为 undefined

但是,使用 let,编译器将变量注册到其作用域,但不会初始化其值为 undefined。相反,它会使变量保持未初始化状态,直到引擎执行您的赋值语句。访问未初始化变量的值会抛出 ReferenceError,这解释了为什么上面的代码片段在运行时会抛出异常。

let 声明和赋值语句的顶部开头之间的空间称为临时死区。该名称源自以下事实:即使引擎知道名为 foo 的变量(位于 bar 范围的顶部),该变量是“死的”,因为它没有值。

...还因为如果您尝试尽早使用它,它会杀死您的程序。

const 关键字的工作方式与 let 相同,但有两个主要区别:

  1. 使用 const 声明时,必须分配一个值。
  2. 不能为使用 const 声明的变量重新赋值。

这可以保证 const始终拥有您最初分配给它的值。

// This is legal
const React = require('react');

// This is totally not legal
const crypto;
crypto = require('crypto');

块范围

letconstvar 在另一方面有所不同:它们的范围大小。

当您使用 var 声明变量时,它在作用域链的尽可能高的位置可见 - 通常是在最近的函数声明的顶部,或者在全局范围,如果您在顶层声明它。

但是,当您使用 letconst 声明变量时,它会尽可能本地可见 - > 在最近的街区内。

块是由大括号分隔的一段代码,如 if/else 块、for 循环,以及显式“阻止”的代码块,如本段代码所示。

"use strict";

{
  let foo = "foo";
  if (foo) {
      const bar = "bar";
      var foobar = foo + bar;

      console.log("I can see " + bar + " in this bloc.");
  }
  
  try {
    console.log("I can see " + foo + " in this block, but not " + bar + ".");
  } catch (err) {
    console.log("You got a " + err + ".");
  }
}

try {
  console.log( foo + bar ); // Throws because of 'foo', but both are undefined
} catch (err) {
  console.log( "You just got a " + err + ".");
}

console.log( foobar ); // Works fine

如果您在块内使用 constlet 声明变量,则该变量在块内可见,且仅 分配后。

但是,使用 var 声明的变量在尽可能远的地方可见 - 在本例中是在全局范围内。

如果您对 letconst 的具体细节感兴趣,请查看 Rauschmayer 博士在《探索 ES6:变量和范围》中对它们的介绍,并查看有关它们的 MDN 文档。

词法 <strong>this</strong> 和箭头函数

从表面上看,this 似乎与范围没有太大关系。事实上,JavaScript 并没有根据我们在这里讨论的范围规则来解析 this 的含义。

至少,通常不会。众所周知,JavaScript 不会根据您使用该关键字的位置来解析 this 关键字的含义:

var foo = {
    name: 'Foo',
    languages: ['Spanish', 'French', 'Italian'],
    speak : function speak () {
        this.languages.forEach(function(language) {
            console.log(this.name + " speaks " + language + ".");
        })
    }
};

foo.speak();

我们大多数人都认为 this 表示 fooforEach 循环内,因为这就是它在循环之外的含义。换句话说,我们期望 JavaScript 能够解析 this 词法的含义。

但事实并非如此。

相反,它会在您定义的每个函数中创建一个 this,并根据您如何调用该函数来决定其含义 -不是您定义它的位置

第一点类似于在子作用域中重新定义任何变量的情况:

function foo () {
    var bar = "bar";
    function baz () {
        // Reusing variable names like this is called "shadowing" 
        var bar = "BAR";
        console.log(bar); // BAR
    }
    baz();
}

foo(); // BAR

bar 替换为 this,整个事情应该立即清楚!

传统上,要让 this 按照我们期望的普通旧词法范围变量的方式工作,需要以下两种解决方法之一:

var foo = {
    name: 'Foo',
    languages: ['Spanish', 'French', 'Italian'],
    speak_self : function speak_s () {
        var self = this;
        self.languages.forEach(function(language) {
            console.log(self.name + " speaks " + language + ".");
        })
    },
    speak_bound : function speak_b () {
        this.languages.forEach(function(language) {
            console.log(this.name + " speaks " + language + ".");
        }.bind(foo)); // More commonly:.bind(this);
    }
};

speak_self 中,我们将 this 的含义保存到变量 self 中,并使用该变量来得到我们想要的参考。在 speak_bound 中,我们使用 bind永久this 指向给定对象。

ES2015 为我们带来了一种新的选择:箭头函数。

与“普通”函数不同,箭头函数不会通过设置自己的值来隐藏其父作用域的 this 值。相反,他们从词汇上解析其含义。

换句话说,如果您在箭头函数中使用 this,JavaScript 会像查找任何其他变量一样查找其值。

首先,它检查本地范围内的 this 值。由于箭头函数没有设置一个,因此它不会找到一个。接下来,它检查 this 值的范围。如果找到,它将使用它。

这让我们可以像这样重写上面的代码:

var foo = {
    name: 'Foo',
    languages: ['Spanish', 'French', 'Italian'],
    speak : function speak () {
        this.languages.forEach((language) => {
            console.log(this.name + " speaks " + language + ".");
        })
    }
};   

如果您想了解有关箭头函数的更多详细信息,请查看 Envato Tuts+ 讲师 Dan Wellman 的有关 JavaScript ES6 基础知识的精彩课程,以及有关箭头函数的 MDN 文档。

结论

到目前为止,我们已经了解了很多内容!在本文中,您了解到:

  • 变量在编译期间注册到其作用域,并在执行期间与其赋值相关联。
  • 在赋值之前引用使用 let
class="inline">const 声明的变量会引发 ReferenceError,并且此类变量是范围到最近的块。
  • 箭头函数 允许我们实现 this 的词法绑定,并绕过传统的动态绑定。
  • 您还了解了提升的两条规则:

    • 第一条提升规则:函数表达式和 var 声明在其定义的整个范围内都可用,但其值为 undefined 直到您的赋值语句执行。
    • 第二条提升规则:函数声明的名称及其主体在定义它们的范围内可用。

    下一步最好是利用 JavaScript 作用域的新知识来理解闭包。为此,请查看 Kyle Simpson 的 Scopes & Closures。

    最后,关于 this 有很多话要说,我无法在此介绍。如果该关键字看起来仍然像是黑魔法,请查看此和对象原型来了解它。

    それまでの間、これまでに学んだことを活用して、書き方の間違いを減らしてください。

    JavaScript を学ぶ: 完全ガイド

    Web 開発者として始めたばかりの場合でも、より高度なトピックを探索したい場合でも、JavaScript の学習に役立つ完全なガイドを作成しました。

    以上がJavaScript のスコープを理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

    声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    ブログをwordpress.comからwordpress.orgに簡単に移動する方法ブログをwordpress.comからwordpress.orgに簡単に移動する方法Apr 18, 2025 am 11:33 AM

    ブログをwordpress.comからwordpress.orgに移動しますか? 多くの初心者はWordPress.comから始めますが、すぐに制限を実現し、自己ホストされたWordPress.orgプラットフォームに切り替えたいと考えています。 このステップバイステップガイドでは、WordPress.comからWordPress.orgにブログを適切に移動する方法を示します。 なぜwordpress.comからwordpress.orgに移行するのですか? WordPress.comを使用すると、誰でもアカウントを作成できます

    IFTTT(およびその他)でWordPressとソーシャルメディアを自動化する方法IFTTT(およびその他)でWordPressとソーシャルメディアを自動化する方法Apr 18, 2025 am 11:27 AM

    WordPress Webサイトやソーシャルメディアアカウントを自動化する方法をお探しですか? 自動化を使用すると、WordPressのブログ投稿やFacebook、Twitter、LinkedIn、Instagramなどで自動的に共有できます。 この記事では、IFTTT、Zapier、およびUncanny Automatorを使用してWordPressとソーシャルメディアを簡単に自動化する方法を紹介します。 WordPressとソーシャルメディアを自動化する理由 WordPreを自動化します

    WordPressでカスタムメニュー項目の制限を修正する方法WordPressでカスタムメニュー項目の制限を修正する方法Apr 18, 2025 am 11:18 AM

    ほんの数日前、ユーザーの1人が異常な問題を報告しました。問題は、彼がカスタムメニュー項目の限界に達することです。メニュー項目の制限に到達した後に保存するコンテンツはまったく保存されません。この問題を聞いたことがないので、ローカルインストールで試してみることにしました。 200を超えるメニュー項目が作成され、保存されました。効果はとても良いです。 100個のアイテムをドロップダウンリストに移動し、それらを非常によく保存します。その後、サーバーに関係している必要があることがわかりました。さらなる研究の後、他の多くの人が同じ問題に遭遇したようです。深く掘り下げた後、この問題を強調したTRACチケット(#14134)を見つけました。よく読んでください

    WordPressのカスタム分類法にカスタムメタフィールドを追加する方法WordPressのカスタム分類法にカスタムメタフィールドを追加する方法Apr 18, 2025 am 11:11 AM

    WordPressのカスタム分類法にカスタムメタフィールドを追加する必要がありますか? カスタム分類法により、カテゴリとタグ以外にコンテンツを整理できます。他のフィールドを追加してそれらを説明するのが便利な場合があります。 この記事では、作成した分類法に他のメタフィールドを追加する方法を紹介します。 カスタムメタフィールドはいつカスタム分類法に追加する必要がありますか? WordPressサイトで新しいコンテンツを作成すると、2つのデフォルトの分類法(カテゴリとタグ)を使用して整理できます。 一部のWebサイトは、カスタム分類法の使用から恩恵を受けます。これらを使用すると、コンテンツを他の方法で並べ替えることができます。 例えば、

    Windowsライブライターを使用してWordPressにリモートで公開する方法Windowsライブライターを使用してWordPressにリモートで公開する方法Apr 18, 2025 am 11:02 AM

    Windows Live Writerは、デスクトップからWordPressブログに直接投稿することができる多用途のツールです。これは、ブログを更新するためにWordPress管理パネルにログインする必要がないことを意味します。このチュートリアルでは、Windows Live Writerを使用してWordPressブログのデスクトップ公開を有効にする方法を紹介します。 WordPressにWindows Live Writerをセットアップする方法 ステップ1:WordprでWindows Live Writerを使用するには

    WordPress Visual Editorで白いテキストと欠落ボタンを修正する方法WordPress Visual Editorで白いテキストと欠落ボタンを修正する方法Apr 18, 2025 am 10:52 AM

    最近、ユーザーの1人が非常に奇妙なインストールの問題を報告しました。投稿を書くとき、彼らは彼らが書くものは何も見ることができません。ポストエディターのテキストは白だからです。さらに、すべてのVisual Editorボタンが欠落しており、VisualからHTMLに切り替える機能も機能しません。この記事では、WordPress Visual Editorで白いテキストと欠落したボタンの問題を修正する方法を示します。 初心者に注意:他のWebサイトのスクリーンショットで見られる隠されたボタンを探しているなら、キッチンシンクを探しているかもしれません。 Kitchen Sinkアイコンをクリックして、下線、​​Wordからのコピーなどの他のオプションを表示する必要があります。

    WordPressのユーザーメールでアバターを表示する方法WordPressのユーザーメールでアバターを表示する方法Apr 18, 2025 am 10:51 AM

    WordPressのユーザーメールでアバターを表示しますか? Gravatarは、ユーザーのメールアドレスをオンラインアバターに接続するネットワークサービスです。 WordPressは、コメントセクションに訪問者のプロフィール写真を自動的に表示しますが、サイトの他の領域に追加することもできます。 この記事では、WordPressのユーザーメールでアバターを表示する方法を紹介します。 グラバタルとは何ですか、なぜそれを表示する必要がありますか? Gravatarは、世界的に認識されているアバターの略で、画像をメールアドレスにリンクできるようになります。 ウェブサイトがサポートしている場合

    WordPressのデフォルトのメディアアップロード場所を変更する方法WordPressのデフォルトのメディアアップロード場所を変更する方法Apr 18, 2025 am 10:47 AM

    WordPressのデフォルトのメディアアップロード場所を変更しますか? メディアファイルを他のフォルダーに移動すると、Webサイトの速度とパフォーマンスが向上し、バックアップをより速く作成することができます。また、自分に最適な方法でファイルを整理する自由を提供します。 この記事では、WordPressのデフォルトのメディアアップロード場所を変更する方法を紹介します。 デフォルトのメディアアップロード場所を変更する理由 デフォルトでは、WordPressは/wp-content/uploads/folderにすべての画像およびその他のメディアファイルを保存します。 このフォルダーには、さまざまな年と数ヶ月の子供がいます

    See all articles

    ホットAIツール

    Undresser.AI Undress

    Undresser.AI Undress

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

    AI Clothes Remover

    AI Clothes Remover

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

    Undress AI Tool

    Undress AI Tool

    脱衣画像を無料で

    Clothoff.io

    Clothoff.io

    AI衣類リムーバー

    AI Hentai Generator

    AI Hentai Generator

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

    ホットツール

    Dreamweaver Mac版

    Dreamweaver Mac版

    ビジュアル Web 開発ツール

    メモ帳++7.3.1

    メモ帳++7.3.1

    使いやすく無料のコードエディター

    mPDF

    mPDF

    mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

    Safe Exam Browser

    Safe Exam Browser

    Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

    SAP NetWeaver Server Adapter for Eclipse

    SAP NetWeaver Server Adapter for Eclipse

    Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。