Execution contexts, Hoisting, Scopes, and Closures

실행 컨텍스트, 호이스팅, 스콥프와 클로저스에 대한 이해

Execution Contexts

Execution context는 자바스크립트가 가지고 있는 프로세스로 컴퓨터가 코드를 읽을 때 한번에 전체를 읽는 것이 아닌 몇몇의 조각으로 나누어 읽는 방식이다. 이는 자바스크립트가 복잡한 코드를 해석, 작성, 관리, 그리고 실행하게 해준다.

Global Execution Context

자바스크립트를 실행할때 가장 먼저 실행되는 컨텍스트이다. 아무런 코드가 없어도 이 컨텍스트를 호출할 수 있다. 이 컨텍스트의 Creation Phase에서 다음과 같은 4가지 일이 일어난다.

Global Execution Context
Phase: Creation
window: global object // 브라우저
this: window

:: 예시 1

var name = "hi";
var handle = "@hiyo";

function getUser(){
  return {
   name: name,
    hand: handle
  };
}

:::: 예시 1의 Operation log

(Global Execution Context Creation Phase), Program >

Global Execution Context
Phase: Creation
window: global object
this: window // 'this'와 'window'는 무슨일이 있어도 실행된다.
name: undefined
handle: undefined // name과 함께 변수 값으로 메모리에 저장되지 않았다.
getUser: fn() // 메모리에 저장된다.

(Global Execution Context Execution Phase), VariableDeclaration > Literal > VariableDeclaration > program //name에 "hi"가 입력 > VariableDeclaration > Literal > VariableDeclaration > program //handle에 "@hiyo"가 입력 >

Global Execution Context
Phase: Execution
window: global object
this: window
name: "hi"
handle: "@hiyo"
getUser: fn()

VariableDeclaration > program > FunctionDeclaration > program > Finished

:: 정리 {#hoisting}

:: 예시 2

console.log('name: ', name); // name: undefined
console.log('handle: ', handle); // handle: undefined
console.log('getUser ', getUser); // getUser: getUser(){return {name:name, handle:handle}}

var name = "hi";
var handle = "@hiyo";

function getUser(){
  return {
    name: name,
    hand: handle
  };
}

:: 정리

Function Execution Context

Function execution context는 다음의 항목을 제외하고는 Global execution context와 밑의 완벽히 동일한 테스크가 일어난다.

:: 예시 1

var name = "hi";
var handle = "@hiyo";

function getUser(){
  return {
    name: name,
    hand: handle
  };
}

getUser()

:::: 예시 1의 Operation log

(이전의 로그는 finished 제외한 Global Execution Context의 로그와 동일) >

Global Execution Context
Phase: Execution
window: global object
this: window
name: "hi"
handle: "@hiyo"
getUser: fn()

ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Creation
window: global object
this: window
name: "hi"
handle: "@hiyo"
getUser: fn()

        getUser Execution Context
        Phase: Creation
        arguments: { length: 0 }
        this: window

ADD getUser Execution Context = Phase: Creation, BlockStatement > START getUser Execution Context = Phase: Execution, ReturnStatement > ObjectExpression > Identifier > ObjectExpression > Identifier > ObjectExpression > Identifier > ObjectExpression > Identifier > ObjectExpression > ReturnStatement >

Global Execution Context
Phase: Execution
window: global object
this: window
name: "hi"
handle: "@hiyo"
getUser: fn()

REMOVE getUser Execution Context, CallExpression > ExpressionStatement > Program > Finished

:: 정리


Execution Stack

함수가 호출되면 언제나 새로운 Execution context가 생성되고 Execution stack에 추가된다. 언제나 함수가 종료되면 그 함수의 execution context가 끝이나고 execution stack에서 사라진다. 자바스크립트는 코드를 실행할 때 하나의 테스크가 일차원적으로 일어나는 ‘Single thread’이기에 이 Excution Stack이란 개념을 사용하한다. Single thread이기에 프로세스를 시각화하기 용이하다.

:: 예시1

function a () {
    console.log("In fn a")
    function b () {
        console.log("In fn b")
        function c () {
            console.log("In fn c")
        }
        c()
    }
    b()
}
a()

:::: 예시1의 Operation log

Global Execution Context
Phase: Creation
window: global object
this: window
a: fn()

Global Execution Context Phase: Creation, Program >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()

Global Execution Context Phase: Execution, FunctionDeclaration > Program > ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()
        a Execution Context
        Phase: Creation
        arguments: { length: 0 }
        this: window
        b: fn()

ADD a Execution Context Phase: Creation, BlockStatement > START a Execution Context Phase: Execution, ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Literal > CallExpression > CallExpression > ExpressionStatement > BlockStatement > FunctionDeclaration > BlockStatement > ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()
        a Execution Context
        Phase: Execution
        arguments: { length: 0 }
        this: window
        b: fn()
                b Execution Context
                Phase: Creation
                arguments: { length: 0 }
                this: window
                c: fn()

ADD b Execution Context Phase: Creation, BlockStatement > START b Execution Context Phase: Execution, ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Literal > CallExpression > CallExpression > ExpressionStatement > BlockStatement > FunctionDeclaration > BlockStatement > ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()
        a Execution Context
        Phase: Execution
        arguments: { length: 0 }
        this: window
        b: fn()
                b Execution Context
                Phase: Execution
                arguments: { length: 0 }
                this: window
                c: fn()
                        c Execution Context
                        Phase: Creation
                        arguments: { length: 0 }
                        this: window

ADD c Execution Context Phase: Creation, BlockStatement > START c Execution Context Phase: Execution, ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Literal > CallExpression > CallExpression > ExpressionStatement > BlockStatement >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()
        a Execution Context
        Phase: Execution
        arguments: { length: 0 }
        this: window
        b: fn()
                b Execution Context
                Phase: Execution
                arguments: { length: 0 }
                this: window
                c: fn()

REMOVE c Execution Context Phase: Execution, CallExpression > ExpressionStatement > BlockStatement >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()
        a Execution Context
        Phase: Execution
        arguments: { length: 0 }
        this: window
        b: fn()

REMOVE b Execution Context Phase: Execution, CallExpression > ExpressionStatement > BlockStatement > CallExpression > ExpressionStatement > Program >

Global Execution Context
Phase: Execution
window: global object
this: window
a: fn()

REMOVE a Execution Context Phase: Execution, Finished

:: 예시2 - Local variable

var name = "hi";
var handle = "@hiyo";

function getURL(handle){
  var twitterURL = 'http://twitter.com/';
  return twitterURL + handle;
}

getURL(handle)

:::: 예시2의 operation log

(생략) >

Global Execution Context
Phase: Execution
window: global object
this: window
name: "hi"
handle: "@hiyo"
getURL: fn()
        getURL Execution Context
        Phase: Creation
        arguments: { 0: "@hiyo", length: 1 }
        this: window
        twitterURL: undefined
        handle: "@hiyo"

START getURL Execution Context Phase: Creation// Hoist a value or assign defult value of undefined to twitterURL, BlockStatement > getURL Execution Context Phase: Creation, VariableDeclaration > Literal // 'http://twitter.com/' > VariableDeclaration > twitterURL assigned "http://twitter.com/", BlockStatement > (생략)


Scopes

위의 Local variable 예시는 Scope라는 주제를 보여준다. Scope는 변수 혹은 expression이 참조 가능한 컨텍스트를 뜻한다.

:: 예시1

function first () {
    var name = 'John'

    console.log(name)
}
function second () {
    var name = 'Jeremy'

    console.log(name)
}

console.log(name); // undefined :: a value from variable decoration in creation execution phase
var name = 'AK';
first(); // John
second(); // Jeremy
console.log(name); // AK

:::: 예시1의 operation log

Global Execution Context
Phase: Creation
window: global object
this: window
first: fn()
second: fn()
name: undefined

ADD Global Execution Context Phase: Creation, program > START Global Execution Context Phase: Execution, FunctionDeclaration // fn first > Program > FunctionDeclaration // fn second > program > ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Identifier > CallExpression > CallExpression > ExpressionStatement > Program > VariableDeclaration > Literal > VariableDeclaration > assigned "AK" on "name" in Global Execution Context Phase: Execution, Program > first();, ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
first: fn()
second: fn()
name: "AK"
        first Execution Context
        Phase: Creation
        arguments: { length: 0 }
        this: window
        name: undefined

ADD first Execution Context Phase: Creation, BlockStatement > START first Execution Context Phase: Execution, VariableDeclaration > Literal > VariableDeclaration > ASSIGN "name" "John", BlockStatement > ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Identifier > CallExpression > CallExpression > ExpressionStatement > BlockStatement > REMOVE first Execution Context Phase: Creatio, first();, CallExpression > ExpressionStatement > Program > second();, ExpressionStatement > CallExpression > Identifier > CallExpression > (생략)

:: 예시2

var name = 'Tyler';

function logname () {
    console.log(name);
}

logname ();

:::: 예시2의 operation log

Global Execution Context
Phase: Creation
window: global object
this: window
name: undefined
logname: fn()

ADD Global Execution Context Phase: Creation, Program > START Global Execution Context Phase: Execution, VariableDeclaration > Literal > VariableDeclaration > Assign 'name' 'Tyler', Program > FunctionDeclaration > Program > logname();, ExpressionStatement > CallExpression > Identifier > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
name: "Tyler"
logname: fn()
        logname Execution Context
        Phase: Creation
        arguments: { length: 0 }
        this: window

ADD logname Execution Context Phase: Creation, BlockStatement > START logname Execution Context Phase: Execution, ExpressionStatement > CallExpression > MemberExpression > Identifier > MemberExpression > Identifier > MemberExpression > CallExpression > Identifier > CallExpression > Print 'Tyler' on console, CallExpression > (생략)

:: 정리


Closures

함수 안에 함수가 있다면 바깥 쪽의 함수(outer function)의 execution context가 stack에서 제거되도 안 쪽의 함수(inner function)는 여전히 부모 함수(outer function)의 variable environment를 가져온 closure에서 엑세스할 수 있다.

var count = 0;
function makeAdder (x) {
    return function inner (y) {
        return x + y;
    };
}
var add5 = makeAdder(5);
count += add5(2);

:: 예시1의 operation log

Global Execution Context
Phase: Creation
window: global object
this: window
count: undefined
makeAdder: fn()
add5: undefined

ADD Global Execution Context Phase: Creation, Program > START Global Execution Context Phase: Execution, VariableDeclaration > (생략) > makeAdder(5), CallExpression > Identifier > CallExpression > READ 5, Literal > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
count: 0
makeAdder: fn()
add5: undefined
        makeAdder Execution Context
        Phase: Creation
        arguments: { 0: 5, length: 1 }
        this: window
        x: 5

ADD makeAdder Execution Context Phase: Creation, BlockStatement > START makeAdder Execution Context Phase: Execution, ReturnStatement > READ function inner (y){ ... }, FunctionExpression > ReturnStatement >

Global Execution Context
Phase: Execution
window: global object
this: window
count: 0
makeAdder: fn()
add5: undefined
        Closure Scope
        arguments: { 0: 5, length: 1 }
        this: window
        x: 5

**REMOVED makeAdder Execution Context, START Closure Scope, READ makeAdder(5), CallExpression > VariableDeclaration > add5: fn(), Program > ExpressionStatement > AssignmentExpression > Identifier > AssignmentExpression > CallExpression > Identifier > CallExpression > READ 2, Literal > CallExpression >

Global Execution Context
Phase: Execution
window: global object
this: window
count: 0
makeAdder: fn()
add5: fn()
        Closure Scope
        arguments: { 0: 5, length: 1 }
        this: window
        x: 5
                inner Execution Context
                Phase: Creation
                arguments: { 0: 2, length: 1 }
                this: window
                y: 2

ADD inner Execution Context Phase: Creation, BlockStatement > START inner Execution Context Phase: Execution, ReturnStatement > x + y, BinaryExpression > x, Identifier > BinaryExpression > y, Identifier > ***BinaryExpression > ReturnStatement >

Global Execution Context
Phase: Execution
window: global object
this: window
count: 0
makeAdder: fn()
add5: fn()
        Closure Scope
        arguments: { 0: 5, length: 1 }
        this: window
        x: 5

REMOVED inner Execution Context, CallExpression > AssignmentExpression > Global Execution Context count: 7, ExpressionStatement > Program >

Global Execution Context
Phase: Execution
window: global object
this: window
count: 7
makeAdder: fn()
add5: fn()

REMOVED Closure Scope // from the execution stack, Finished

:: 정리

Resources