0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ブラウザの動作原理とスコープ

Posted at

スコープに関する問題集

以下のコードを実行すると、どのような結果が表示されるでしょうか?
ブラウザの動作原理を理解した上で、各問題の答えを最後に解説します。

//問題1
var n = 100;
function foo() {
    n = 200;
}
foo();
console.log(n); // 何が出力されますか?

//問題2
function foo(){
    console.log(n)
    var n= 200
    console.log(n)
}
var n = 100
foo()

//問題3
var n= 100
function foo1(){
    console.log(n)
    var n= 300
    console.log(n)
}
function foo2(){
    var n= 200
    console.log(n)
    foo1()
}
foo2()
console.log(n)

//問題4
var n = 200
function foo(){
    console.log(n)
    return
    var n = 100
}
foo()    

 
 

ブラウザの動作原理解説

 
基本動作の流れ
 
1、解析フェーズ:

  • JavaScriptエンジンはコードを解析し、変数や関数をメモリに登録します
  • varで宣言された変数はundefinedで初期化されます
     

2、関数宣言は完全な状態で登録されます

  • コードを上から順に実行
  • 変数への値の代入や関数呼び出しが行われます
     

変数宣言と実行の流れ(具体例)

console.log(num1); // step1

var num1 = 20;     // step2
var num2 = 30;     // step3
var result = num1 + num2; // step4

console.log(result); // step5

準備段階(解析時)
ブラウザはコードを実行する前に、次の準備を行います:
1、グローバルオブジェクト(GO)を作成
2、変数宣言を事前登録(値をundefinedで初期化)
3、関数宣言を完全な形で登録

globalObject = {
    // 組み込みオブジェクト
    String: class,
    Date: class,
    setTimeout: function,
    
    // 宣言された変数(初期値はundefined)
    num1: undefined,
    num2: undefined,
    result: undefined
}

コード実行

実行コンテキスト(GEC)を作成し、上から順に処理:

step1: console.log(num1)
num1を検索 → 現在の値はundefined
出力:undefined

step2:  var num1 = 20
num1に20を代入
メモリ更新:num1: 20

step3:  var num2 = 30
num2に30を代入
メモリ更新:num2: 30

step4:  var result = num1 + num2
計算:20 + 30 = 50
resultに50を代入
メモリ更新:result: 50

step5:  console.log(result)
resultの現在値は50
出力:50

最終的なメモリ状態

globalObject = {
    String: class,
    Date: class,
    setTimeout: function,
    
    // 更新された値
    num1: 20,
    num2: 30,
    result: 50
}

ブラウザの動作原理:関数の処理フロー

foo();
function foo() {
    console.log("foo");
}

このコードがエラーにならない理由:

準備段階で,関数宣言は完全な形で登録される
メモリ上に関数の実体(0xa00など)が作成される
グローバルオブジェクトに参照が保存される

上記の例は、

globalObject = {
    // ...(他のプロパティ)
    foo: 0xa00 // 関数実体への参照
}

0xa00はアドレスで、内部メモリに保存

//(foo)0xa00

[[scope]]:parent scope
excute body

上記のは、関数の解析です。

関数実行時の流れはこちらの通りです。

foo(123)
function foo(num){
    console.log(m)
    var m = 10
    var n = 20
    console.log("foo")
}

実行コンテキスト作成:

  • 新しい実行環境(FEC)がスタックに追加される
  • アクティベーションオブジェクト(AO)が作成される

初期状態:

//AO(activation object)

AO = {
    num: 123,  // 引数で受け取った値
    m: undefined, // var宣言はホイスティング
    n: undefined  // var宣言はホイスティング
}

関数の実行

  • console.log(m) → undefinedを出力

  • m = 10を実行 → AOが更新

  • n = 20を実行 → AOが更新

      AO = {
          num: 123,
          m: 10,
          n: 20
      }
    

スコープチェーンの実際

var message = "hello";

function bar() {
    var message = "bar";
    console.log(message); // "bar"
    foo();
}

function foo() {
    console.log(message); // "hello"
}

bar();

実行プロセス

1、bar()実行時:

  • barのAOにmessage: "bar"が登録
  • 自分のAOを優先して参照

 
2、foo()実行時:

  • foo内にmessage宣言がない
  • スコープチェーンで外側を探索
  • グローバルのmessage: "hello"を発見

最初の問題の説明

//質問1
var n = 100
function foo(){
    n= 200
}
foo()

console.log(n); // 結果: 200

グローバルオブジェクト(GO)に変数nと関数fooを登録
コード実行:
n = 100 でGOを更新
foo() を実行
関数内のn = 200:
関数内にnの宣言がない → スコープチェーンでGOのnを更新
console.log(n) は更新後のGOのnを参照


//質問2
function foo(){
    console.log(n) // undefined
    var n= 200
    console.log(n) // 200
}
var n = 100
foo()

GO: { n: undefined, foo: 0xa00 }
n = 100 でGOを更新
GO: { n: 100, foo: 0xa00 }
foo() のAOを作成(変数nをホイスティング)
AO: { n: undefined }
1回目のconsole.log(n) → undefined
n = 200 でAOを更新
AO: { n: 200 }
2回目のconsole.log(n) → 200


//質問3
var n= 100
function foo1(){
    console.log(n) // undefined
    var n= 300
    console.log(n) // 300
}
function foo2(){
    var n= 200
    console.log(n) // 200
    foo1()
}
foo2()
console.log(n) // 100

GO: { n: 100, foo1: 0xa00, foo2: 0xb00 }
foo2()の実行:
AOにn = 200を登録

AO(foo2): { n: 200 }
console.log(n) → 200

foo1()の実行:
ホイスティングによりAOを作成

AO(foo1): { n: undefined }
1回目のconsole.log(n) → undefined

n = 300でAOを更新

AO(foo1): { n: 300 }
2回目のconsole.log(n) → 300

最終出力:
グローバルのnは変更されていない

console.log(n); // 100


//質問4
var n = 200
function foo(){
    console.log(n); // undefined
    return
    var n = 100
}
foo()  

GO: { n: 200, foo: 0xa00 }
関数実行:
ホイスティングによりAOを作成

AO: { n: undefined }
console.log(n) → undefined

returnで関数終了(n = 100は実行されない)

重要なポイント:
var n はホイスティングされるが、代入は実行されない

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?