Skip to content

Instantly share code, notes, and snippets.

@kajirikajiri
Last active June 27, 2020 07:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kajirikajiri/499234cc820318e036adae178af00155 to your computer and use it in GitHub Desktop.
Save kajirikajiri/499234cc820318e036adae178af00155 to your computer and use it in GitHub Desktop.
JS本

Engine/Scope Conversation

function foo(a) { console.log( a ); // 2 } foo( 2 );

Let’s imagine the above exchange (which processes this code snippet) as a conversation. The conversation would go a little something like this: Engine: Hey Scope, I have an RHS reference for foo. Ever heard of it? Scope: Why yes, I have. Compiler declared it just a second ago. It’s a function. Here you go. Engine: Great, thanks! OK, I’m executing foo. Engine: Hey, Scope, I’ve got an LHS reference for a, ever heard of it? Scope: Why yes, I have. Compiler declared it as a formal parameter to foo just recently. Here you go. Engine: Helpful as always, Scope. Thanks again. Now, time to assign 2 to a. Engine: Hey, Scope, sorry to bother you again. I need an RHS lookup for console. Ever heard of it? Scope: No problem, Engine, this is what I do all day. Yes, I’ve got console. It’s built-in. Here ya go. Engine: Perfect. Looking up log(..). OK, great, it’s a function. Engine: Yo, Scope. Can you help me out with an RHS reference to a. I think I remember it, but just want to double-check. Scope: You’re right, Engine. Same variable, hasn’t changed. Here ya go. Engine: Cool. Passing the value of a, which is 2, into log(..).

上記のやり取り(このコードスニペットを処理する)を会話として想像してみましょう。会話は次のようになります。Engine: ねえスコープ、foo の RHS リファレンスがあるんだけど、聞いたことある?聞いたことある?Scope. もちろん、知っています。コンパイラがさっき宣言したんだ。関数だよ。はい、どうぞ。エンジン。いいね、ありがとう! OK、fooを実行します。Engine: おい、Scope、LHSのリファレンスがあるんだけど、聞いたことある?Scope. あるよ。つい最近、コンパイラがfooの正式なパラメータとして宣言したんだ。これをどうぞ。エンジン:いつも助かるよ、スコープ。ありがとうございます。エンジン: おい、スコープ、またお邪魔しました。consoleのRHS検索が必要なんだ。聞いたことある?スコープ いいんですよ、エンジンさん、これが私の仕事ですから。はい、コンソールを持っています。内蔵されています。ここに行くよ。エンジン。完璧だ log(...)を探します。OK、素晴らしい、これは関数です。エンジン: よお、スコープ。覚えていると思いますが、再確認したいので、RHSの参照先を教えてもらえませんか?スコープ. そうだな、エンジン 同じ変数で変わってません はい、どうぞ エンジン いいね 2であるaの値をlog(...)に渡しています。

Quiz Check your understanding so far. Make sure to play the part of Engine and have a “conversation” with Scope: function foo(a) { var b = a; return a + b; } var c = foo( 2 );

  1. Identify all the LHS look-ups (there are 3!).
  2. Identify all the RHS look-ups (there are 4!).

クイズ ここまでの理解度を確認してください。Engineの役割を果たし、Scopeとの「会話」ができるようにしておきましょう。 関数 foo(a) { var b = a. a + b を返します。 } var c = foo( 2 ).

  1. すべての LHS ルックアップを識別します (3 つあります!)。
    1. RHSのルックアップをすべて確認する (4つ!)

LHS: c=foo, foo(a), b=a RHS: foo(2), b=a, a .. , .. b

Nested Scope We said that Scope is a set of rules for looking up variables by their identifier name. There’s usually more than one scope to consider, however. Just as a block or function is nested inside another block or function, scopes are nested inside other scopes. So, if a variable cannot be found in the immediate scope, Engine consults the next outercontaining scope, continuing until is found or until the outermost (a.k.a., global) scope has been reached. Consider the following: function foo(a) { console.log( a + b ); } var b = 2; foo( 2 ); // 4 The RHS reference for b cannot be resolved inside the function foo, but it can be resolved in the scope surrounding it (in this case, the global). So, revisiting the conversations between Engine and Scope, we’d overhear: Engine: “Hey, Scope of foo, ever heard of b? Got an RHS reference for it.” Scope: “Nope, never heard of it. Go fish.” Engine: “Hey, Scope outside of foo, oh you’re the global scope, OK cool. Ever heard of b? Got an RHS reference for it.” Scope: “Yep, sure have. Here ya go.” The simple rules for traversing nested scope: Engine starts at the currently executing scope, looks for the variable there, then if not found, keeps going up one level, and so on. If the outermost global scope is reached, the search stops, whether it finds the variable or not.

スコープとは、変数を識別子名で調べるためのルールの集合であると言いました。しかし、通常は複数のスコープを考慮する必要があります。ブロックや関数が別のブロックや関数の中に入れ子になっているように、スコープも他のスコープの中に入れ子になっています。そのため、変数がすぐのスコープで見つからない場合、Engine は次の外側を含むスコープを参照し、見つかるまで、または最外側 (別名グローバル) のスコープに到達するまで継続します。次のように考えてみましょう: function foo(a) { console.log( a + b ); } var b = 2; foo( 2 ); // 4 b の RHS 参照は、関数 foo の内部では解決できませんが、その周囲のスコープ(この場合はグローバル)では解決できます。そこで、EngineとScopeの会話を再訪すると、こんな小耳に挟むことになります。Engine: 「ねえ、fooのスコープ、bって聞いたことある?RHSのリファレンスがあるよ。Scope. "スコープ:「いや、聞いたことないな。釣ってこい。" エンジン:「おい、fooのスコープ外のスコープ、おまえがグローバルスコープか、OKカッコいい。bって聞いたことある?RHSのリファレンスがあるよ。" スコープ。"エンジン:「うん、知ってるよ。はい、どうぞ。" 入れ子になったスコープを横断する簡単なルールです。エンジンは現在実行中のスコープから開始し、そこから変数を探し、見つからなければ1つ上のレベルに移動し続けます。一番外側のグローバルスコープに到達すると、変数が見つかるかどうかに関わらず、検索は停止します。

Building on Metaphors To visualize the process of nested scope resolution, I want you to think of this tall building: The building represents our program’s nested scope ruleset. The first floor of the building represents your currently executing scope, wherever you are. The top level of the building is the global scope. You resolve LHS and RHS references by looking on your current floor, and if you don’t find it, taking the elevator to the next floor, looking there, then the next, and so on. Once you get to the top floor (the global scope), you either find what you’re looking for, or you don’t. But you have to stop regardless.

メタファーの構築 入れ子になったスコープ解決のプロセスを可視化するために、この高い建物を思い浮かべてほしい。 この建物は、プログラムの入れ子になっているスコープのルールセットを表しています。ビルディングの 1 階は、現在実行中のスコープを表しています。ビルディングの最上階がグローバルスコープです。LHS と RHS の参照を解決するには、現在の階を探して、見つからなければエレベーターで次の階に行き、そこを探して、次の階を探す、というようにします。最上階(グローバルスコープ)に着いたら、探しているものが見つかるか、見つからないかのどちらかです。しかし、関係なく立ち止まらなければなりません。

Errors Why does it matter whether we call it LHS or RHS? Because these two types of look-ups behave differently in the circumstance where the variable has not yet been declared (is not found in any consulted scope). Consider:

  1. See the MDN’s break down of Strict Mode function foo(a) { console.log( a + b ); b = a; } foo( 2 ); When the RHS look-up occurs for b the first time, it will not be found. This is said to be an “undeclared” variable, because it is not found in the scope. If an RHS look-up fails to ever find a variable, anywhere in the nested scopes, this results in a ReferenceError being thrown by the engine. It’s important to note that the error is of the type ReferenceError. By contrast, if the engine is performing an LHS look-up, and it arrives at the top floor (global scope) without finding it, if the program is not running in “Strict Mode,”1 then the global scope will create a new variable of that name in the global scope, and hand it back to Engine. “No, there wasn’t one before, but I was helpful and created one for you.” “Strict Mode,” which was added in ES5, has a number of different behaviors from normal/relaxed/lazy mode. One such behavior is that it disallows the automatic/implicit global variable creation. In that case, there would be no global scoped variable to hand back from an LHS look-up, and Engine would throw a ReferenceError similarly to the RHS case. Now, if a variable is found for an RHS look-up, but you try to do something with its value that is impossible, such as trying to execute-as-function a nonfunction value, or reference a property on a null or undefined value, then Engine throws a different kind of error, called a TypeError. ReferenceError is scope resolution-failure related, whereas TypeEr ror implies that scope resolution was successful, but that there was an illegal/impossible action attempted against the result.

エラー なぜLHSとRHSのどちらと呼ぶかが重要なのでしょうか?なぜなら、この2つのタイプのルックアップは、変数がまだ宣言されていない (参照されているスコープでは見つからない) 場合には、異なる振る舞いをするからです。 例えば、次のような場合を考えてみましょう。

  1. MDNのStrict Modeのブレークダウンを参照してください。 関数 foo(a) { console.log( a + b ). b = a. } foo( 2 )。 bに対して初めてRHSルックアップが発生したときには、見つかりません。これは、スコープ内で見つからないため、「未申告」変数と呼ばれています。RHS 検索がネストされたスコープのどこかで変数を見つけられなかった場合、エンジンによって ReferenceError がスローされます。このエラーは ReferenceError 型であることに注意してください。対照的に、エンジンが LHS 検索を実行していて、それが見つからずに最上階(グローバル スコープ)に到着した場合、プログラムが "Strict Mode "1 で実行されていない場合、グローバル スコープ内にその名前の新しい変数が作成され、それをエンジンに引き渡します。"いや、今まではなかったんだけど、助かったから作ってあげたよ" "ES5で追加された "Strict Mode "は、通常/リラックス/レイジーモードとは異なるいくつかの挙動を持っています。その一つが、グローバル変数の自動/暗黙の作成を無効にするという挙動です。この場合、LHS ルックアップから返すグローバル スコープ変数は存在せず、Engine は RHS の場合と同様に ReferenceError をスローします。RHS ルックアップで変数が見つかったが、その値で不可能なことをしようとした場合、関数として非関数値を実行したり、NULL 値や未定義値のプロパティを参照したりすると、Engine は TypeError と呼ばれる別の種類のエラーをスローします。ReferenceError はスコープ解決の失敗に関連していますが、TypeError はスコープ解決は成功したが、その結果に対して不正な/不可能なアクションが試みられたことを意味します。

Review Scope is the set of rules that determines where and how a variable (identifier) can be looked up. This look-up may be for the purposes of assigning to the variable, which is an LHS (lefthand-side) reference, or it may be for the purposes of retrieving its value, which is an RHS (righthand-side) reference. LHS references result from assignment operations. Scope-related assignments can occur either with the = operator or by passing arguments to (assign to) function parameters. The JavaScript engine first compiles code before it executes, and in so doing, it splits up statements like var a = 2; into two separate steps: 1. First, var a to declare it in that scope. This is performed at the beginning, before code execution. 2. Later, a = 2 to look up the variable (LHS reference) and assign to it if found. Both LHS and RHS reference look-ups start at the currently executing scope, and if need be (that is, they don’t find what they’re looking for there), they work their way up the nested scope, one scope (floor) at a time, looking for the identifier, until they get to the global (top floor) and stop, and either find it, or don’t. Unfulfilled RHS references result in ReferenceErrors being thrown. Unfulfilled LHS references result in an automatic, implicitly created global of that name (if not in Strict Mode), or a ReferenceError (if in Strict Mode).

スコープとは、変数(識別子)がどこでどのように検索されるかを決定するルールのセットです。このルックアップは、変数への代入を目的としたもので、LHS (leftfand-side) 参照である場合と、値を取得する目的で、RHS (righthand-side) 参照である場合があります。LHS参照は代入操作の結果です。スコープに関連した代入は、=演算子を使用するか、関数パラメータに引数を渡す(代入する)ことで発生します。JavaScriptエンジンは、実行する前にまずコードをコンパイルし、その際にvar a = 2;のようなステートメントを2つの別々のステップに分割します。1. まず、var aは、そのスコープ内でそれを宣言します。これはコードを実行する前に、最初に実行されます。2. 2.その後、変数(LHS参照)を検索し、見つかった場合には、それに割り当てるために、a = 2。LHS 参照と RHS 参照の両方のルックアップは、現在実行中のスコープから開始し、必要に応じて (つまり、そこで探しているものが見つからない場合)、入れ子になったスコープを 1 つずつ上に上げて、識別子を探し、グローバル (最上階) に到達して停止するまで、それを見つけるか、見つけないかのどちらかになります。満たされていない RHS 参照は、ReferenceErrors がスローされます。満たされていない LHS 参照は、その名前のグローバルが自動的に暗黙的に作成されるか (厳密モードでない場合)、または ReferenceError (厳密モードの場合) になります。

next CHAPTER 2 Lexical Scope 13 page 104 / 1073

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment