Webの基礎:スコープとJavaScriptクロージャ–入門書
JavaScript言語の最大の流行語の1つは、クロージャです。これは、FAANG企業での多くの就職の面接の質問の主題です。この記事では、クロージャとスコープについて説明し、その概念を簡単な例で説明してから、大手ハイテク企業の1人へのインタビューからのサンプル質問で締めくくります。
スコープ
誰かが何かがプロジェクトの範囲内にある、または範囲外であるとあなたに言うとき、それはどういう意味ですか?
その答えを考えるとき、潜望鏡や望遠鏡を思い浮かべたいと思います。これらの機器は、レンズの範囲内であらゆる種類のものを表示します。それはスコープにあります。 。 範囲外の場合 、レンズの直径を超えて見ることはできません。そして、直径の外側の何かに光を当てることはできません。 JavaScriptの3つの非常に重要で異なるタイプのスコープ(ローカル、グローバル、レキシカル)について説明するときに、これについて考える必要があります。
ローカルスコープ
ローカルスコープは、本日お話しする3つのスコープの中で最小のものです。関数を宣言すると、角かっこ({})内のすべてが関数に対してローカルであると見なされます。 JavaScriptエンジンが関数を読み取ると、変数が宣言されます。終了すると、変数が破棄されます。
function greeting() { var websiteName = 'Career Karma'; return `Hello ${websiteName}`; } console.log(greeting()); // Hello Career Karma console.log(websiteName); // ReferenceError: websiteName is not defined
ご覧のとおり、呼び出されたグリーティング関数の結果を「console.log()」すると、関数の実行後にwebsiteNameにアクセスできます。これにより、探していた「HelloCareerKarma」の文字列が得られます。 console.log()
関数内で宣言された変数は、定義されていないため、エラーがスローされます。
すでに述べたように、websiteNameが定義されていない理由は、変数が呼び出されたときに関数内に作成され、ターミナルステートメントの実行時に破棄されるためです。特別な設定がない限り、関数の外部にあるものは関数の内部にアクセスできません。
グローバルスコープ
この次のスコープは、ほとんどフレーズの直訳です。グローバルスコープは、関数の外部で宣言されたアイテムを取得し、すべてのスクリプト、メソッド、および関数がそれらにアクセスしてロジックに使用できるスペースにそれらを予約します。
let counter = 0; // global -- declared outside function const add = () => { // function declaration let counter = 0; // local -- declared inside function counter += 1; // counter increased by 1 -- which counter variable increased? return counter; } add(); // invoke add(); // three add(); // times console.log(counter) // is this 3 or 0? Why?
console.log()
の場合、上記のコードは何をしますか コードの最後にあるカウンター?何が起こると思いますか?
参加者の81%は、ブートキャンプに参加した後、自分たちの技術的な仕事の見通しについてより自信を持っていると述べました。今日のブートキャンプにマッチしましょう。
平均的なブートキャンプの卒業生は、ブートキャンプの開始から最初の仕事を見つけるまで、キャリアの移行に6か月も費やしませんでした。
コードを見ていきましょう。
- グローバル環境で宣言および開始されたカウンター変数。
- グローバル環境で宣言された関数を追加します。
- 追加が呼び出されます。
- ローカル環境で宣言および開始されたカウンター変数。
- ローカルカウンターが1増加します⇐なぜグローバルではなくローカルなのですか?
- カウンターが返されます。関数は終了します。
- 追加が再度呼び出されます
- 手順4〜6をもう一度実行します。
- 手順3〜6をもう一度繰り返します。
-
console.log(counter)
; ⇐何が返されますか?
カウンターが毎回1になると関数が終了するため、関数が実行されるたびにローカルカウンター変数が再宣言され、0で再開始されます。何が起こっても、カウンターは常にローカルレベルで1で停止します。
関数がスコープ内で変数を見つけた場合、その変数のグローバルスコープを参照しないため、グローバル変数は変更されません。したがって、console.log()
そのステートメントの環境内で最も近い定義済み変数がグローバル環境にあるため、0が出力されます。
レキシカルスコープ
字句スコープは、JavaScriptの最も基本的な概念の1つです。関数または変数の作成は、コードの特定の部分にアクセスでき、その後、コードの他の部分にはアクセスできないという考えです。それはすべて、各変数と関数の宣言がどこにあるかに依存します。
このコードブロックを見てみましょう。
const init = () => { // <== This is our outer function const var1 = 'Career'; // outer scope const second = () => { // <== This is our inner function const var2 = 'Karma'; // inner scope console.log(var1); // Career console.log(var2); // Karma return var1 + " " + var2; }; // console.log(var2); // undefined return second(); }; init();
ここに入れ子関数のセットがあります。 init()
関数はvar1という変数を宣言し、secondという関数を宣言し、second()
を呼び出します。 。
コンパイラーがこのコードを最初に通過するとき、私たちが持っているものを高レベルで調べます:
-
init()
機能 -
init()
を呼び出します
この時点では、init()関数内には他に何も表示されていません。関数が存在することがわかっているだけです。 init()関数が呼び出されると、コンパイラは関数内の内容を別の高レベルで調べます。
-
var1
-
second()
機能 -
second()
を呼び出します
init()
関数は、second()
内で何が起こっているかについて何も知りません。 ブロック。 字句環境にあるものだけを見ることができます –その周囲の状態。
入れ子関数はそれぞれ、ロシアのマトリョーシカ入れ子人形のセットのように、小さなコンテナに収められています(たとえば、それらが何であるかわからない場合は、ページの上部を参照してください)。人形は、コンテナ内で何が起こっているのか、親ですでに何が起こっているのか、宣言/読み取られているのかを知っているだけです。たとえば、最大の人形は、そのコンテナ内の次の人形が存在することだけを知っています。セット内の他の人形については何も知りません。字句環境にあるもの(その状態)と、すでに起こっていること(外側のスコープ)だけです。
本質的に、私たちは2つのことを知っています。
- 外側のスコープは内側のスコープを見ることができません。
- 内部スコープは外部スコープにアクセスできます。
外側のコープは内側のスコープで何が起こっているかを見ることができないため、これは一方向の関係であると安全に言えます。内側は外側のスコープから変数を表示して使用できますが、外側は内側を表示できません。これは字句スコープと呼ばれます 。
字句スコープの利点は、変数の値がコード内の配置によって決定されることです。関数は、最初にローカル環境内の変数の意味を探します。変数が見つからない場合は、その関数を定義した関数に移動します。そこに見つからない場合は、チェーンを上に移動して次の定義済み関数に移動します。
これはJavaScriptの非常に重要な概念になり、JavaScriptフレームワークとその機能について学ぶにつれて、何度も登場します。外側から下に渡すことはできますが、反対方向に「上」に渡すことはできません。これは、目前の主要なトピックである閉鎖に到達するため、非常に重要です。 。
閉鎖
クロージャの定義は、字句スコープの定義と非常によく似ています。 2つの主な違いは、クロージャは高階関数であり、字句スコープはそうではないということです。高階関数には、1つの基本的な特性があります。それは、関数を返すか、関数をパラメーターとして使用するかのいずれかです。
クロージャは、その関数が後で呼び出される場合でも、その字句スコープにアクセスできる関数です。
クロージャとレキシカルスコープの両方に独自の変数があり、親関数の変数とパラメータにアクセスでき、グローバル変数を使用できます。次のコードを見ていきましょう。
function greeting() { //outer scope (parent function) const userName = "CrrKrma1952"; // parent variable function welcomeGreeting() { // inner function console.log("Hello, " + userName); // accesses parent var return "Hello, " + userName; // terminal statement } return welcomeGreeting; // returns a function (which makes it HOF) } // end of greeting() const greetUser = greeting(); // greetUser(); // Hello, CrrKrma1952
-
greeting()
関数は存在しますが、内容はまだわかりません。 -
greetUser
存在しますが、内容はまだわかりません -
greetUser()
–これにより前の行が呼び出され、次に、greeting()関数が呼び出されます。 - userNameが宣言されました
-
welcomeGreeting()
存在しますが、内容はまだわかりません -
welcomeGreeting()
の下のreturnステートメント ブロックはまったく同じ関数を返します -
console.log(‘Hello, ‘ + userName)
;ここのconsole.logは、親スコープにアクセスしてuserNameの値を取得できます - 関数を終了し、コードブロック内の変数の意味を破棄する終端ステートメント。
このコードでは、親スコープに後でアクセスできるように、関数をネストして情報を渡します。
結論
この記事では、かなり大きなJavaScriptのテーマであるスコープとクロージャについて説明しました。私は、この主題に関するいくつかの異なる記事を分岐して読むことをお勧めします。これを教える方法は、さまざまな観点から得ることができます。つまり、もちろん、それを学ぶ方法はたくさんあります。この入門書がお役に立てば幸いです。閉鎖に関する研究を続けて頑張ってください!
-
JavaScriptの関数プロトタイプ
JavaScriptで作成された関数には、JavaScriptエンジンによって追加されたプロトタイププロパティが常にあります。プロトタイププロパティは、デフォルトでコンストラクタプロパティを含むオブジェクトです。関数protoypeには、-からアクセスできます。 functionName.prototype オブジェクトが関数コンストラクターを使用して作成されている場合、このプロトタイププロパティを使用して、その関数コンストラクターによって作成されたオブジェクト間でメソッドまたはプロパティを共有できます。 以下は、JavaScriptの関数プロトタイプのコードです- 例 <!DOCT
-
JavaScriptでの関数の借用。
call()、apply()、bind()は、JavaScriptのメソッドを借用するために使用されます。 以下は、JavaScriptでメソッドを借用するためのコードです- 例 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> &