JS - 실행컨텍스트

Jaa-van·2023년 4월 6일
0
post-thumbnail

실행컨텍스트

@실행 컨텍스트

실행한 코드의 환경 정보를 모아놓은 ~객체~

활성화되는 시점에서
1. 선언된 변수를 위로 끌어올린다 ( 호이스팅 )
2. 외부 환경 정보를 구성한다
3. this 값을 실행한

@ 콜스택

  • Stack
    => lasi in, first our // LIFO

  • Queue
    => first in first out // FIFO

콜스택 이라는 방법으로 코드의 환경과 순서를 보장한다

함수를 통해서 실행 컨텍스트를 구성한다

@실행 컨텍스트 객체 구성요소

  1. variable environment (VE)
  • 현재 컨텍스트 내의 식별자 정보( record )
    ( let a = 3 에서의 ' a ' )
  • 외부 환경 정보 ( outer )
  • 선언 시점 lexica environment 의 snapshot
    ( 처음 컨텍스트 선언 시점에서 고정된다 ) ( snapshot 을 유지한다 )
  1. lexica environment (LE)
    variable environment 와 완벽하게 똑같으나
    변경사항을 실시간으로 반영한다
    ( snapshot 을 유지하지 않는다 )

~% 실행 컨텍스트를 생성할 때 VE 에 정보를 담은 다음 LE 에 복사해서 변경사항을 반영한다~

  1. This Binding
    this 식별자가 바라봐야할 객체

@ record 와 호이스팅

record = 식별자 정보

수집 대상 정보

  • 함수에 지정된 매개변수 식별자
  • 함수 자체
  • 변수 식별자 등...

순서대로 수집한다 // 실행하는 것은 별개다

@호이스팅

변수 정보 수집 과정을 이해하기 쉽게 설명한 ' 가상 개념 '

~식별자 정보를 맨 위로 끌어올리는 행위~
( record 를 수집하는 과정을 의미 )

호이스팅 규칙
1. 매개변수나 변수는 선언부를 호이스팅한다


function a(x) {
  console.log(x);
  var x;
  console.log(x);
  var x = 2;
  console.log(x);
}
a(1); // 1 1 2 

var x 다음 console.log(x) 가 undefined 가 아닌 1 이 찍히는 이유

호이스팅을 통해서

function a(x) {
    var x
    var x
    var x
    x = 1
    console.log( x )
    console.log( x )
    x = 2
    console.log( x )
}

이렇게 선언부를 호이스팅 하기 때문에 그렇다

  1. 함수 선언은 전체를 호이스팅 한다
function a () {
    console.log( b ) // error
    var b = ' bbb '
    console.log( b ) // 'bbb'
    functino b () {}
    console.log( b ) // function b
}

이렇게 결과를 예측할 수 있지만

호이스팅 적용 후

function a () {
    var a
    function b () {}
    console.log( b ) // function b
    b = ' bbb '
    console.log( b ) // 'bbb'
    console.log( b ) // 'bbb'
}

실제 결과는 이렇게 나온다

@ 함수 선언문과 표현식

~함수 선언문~은 정의부만 존재하고 할당 명령이 없다
function a () { ... ... ... }

~함수 표현식~은 별도 변수에 함수를 할당한다
var b = function () { ... ... ... }

이 두 방법은 호이스팅 면에서 많이 다르다

  • 함수 선언문 의 경우 function 전체가 호이스팅 된다
  • 함수 표현식 은 var b 의 선언부만 호이스팅 된다

함수 선언문의 경우 function 이 가장 위로 호이스팅 된 이후 전체 영역에서 영향을 끼치기 때문에 아래 줄에 적어도 안전하지 않을 수 있다

따라서 함수 표현식을 활용하는 것을 추천

@스코프

식별자에 대한 유효 범위를 의미한다

@스코프 체인

outer 는 현재 호출된 함수가 선언될 당시의 lexical Environment 를 참조한다

A 는 전역 컨텍스트의 LE( 환경 정보 ) 를 OUTER 로 갖게 된다
B 는 A의 LE( 환경 정보 ) 를 OUTER 로 갖게 된다

이 당시의 환경을 참조하기 위함

var a = 1;
var outer = function() {
    var inner = function() {
        console.log(a); // 3
        var a = 3;
    };
    inner();
    console.log(a); // 1
};
outer();
console.log(a); //  1

여기서 두번째 console.log( a ) 가 1이 찍히는 것은 inner 컨텍스트가 이미 종료되었고 outer 컨텍스트에서 스코프 체인에 의해서 상위 컨텍스트인 전역 컨텍스트의 환경을 가져와서 a = 1 이 찍히는 것이

this

다양한 상황에서 다른 의미로 사용된다

@전역 환경에서의 this

this 는 실행 컨텍스트가 생성될 때 결정된다 ( binding 된다 )

JS 를 돌리는 환경
1. 노드
2. 브라우저

~브라우저의 전역컨텍스트 에서의 this 는 WINDOW 라는 객체~ 이다

~노드의 전역컨텍스트 에서의 this 는 GLOBAL 이라는 객체~ 이다

@함수 vs 메서드 에서의 this

함수와 메서드의 차이는 ~독립성~ 이다

함수는 독립적으로 실행되는 것에 반해
메서드는 종속적으로 실행된다

따라서

  • 함수에서의 this 는 전역 객체 ( WINDOW , GLOBAL )
  • 메서드에서의 this 는 호출의 주체

% 메서드 내부에서 함수를 호출한다 하여도 예외 없이 무조건 this 는 전역 객체를 가리킨다

var obj1 = {
    outer: function() {
        console.log(this); // (1)
        var innerFunc = function() {
            console.log(this); // (2), (3)
        }
        innerFunc(); // (2)

        var obj2 = {
            innerMethod: innerFunc
        };
        obj2.innerMethod(); // (3)
    }
};
obj1.outer();

여기서 (1) 에서 this 는 obj1.outer() 를 통해서 호출된 this 이기 때문에 메서드 로서의 호출, 즉 호출의 주체인 obj1 을 가리킨다

두 번째로 console.log 에 찍히는 this 는 innerFuni() 즉 (2) 에 있는 함수로서의 호출로 this 를 불러왔기 때문에 예외없이 전역 객체를 가리킨다

세 번째로 console.log 에 찍히는 (3) 에 있는 obj2.innerMethod() 에서 호출된 이후 찍히는 console.log(this) 는 메서드로 호출된 this 이기 때문에 호출의 주체인 obj2 를 가리킨다

@this 우회 방법

메서드로 활용하지 않았다고 전역객체를 바라보는 this 가 합리적이지 않다고 여겨 우회하는 방법이 생겨났다

  1. 내부 스코프에 존재하는 this 에 다른 변수를 할당하는 방법
  2. 화살표 함수 ( ES6 에서 this binding 여부 때문에 도입되었다고 해도 과언이 아니다)

화살표 함수는 this binding 과정을 생략한다
( 전에 있던 this 의 환경을 그대로 유지하여 가져온다 )

var obj = {
    outer: function() {
        console.log(this); // (1) obj
        var innerFunc = () => {
            console.log(this); // (2) obj
        };
        innerFunc(); (2)
    }
}

obj.outer();

=> ~(2) 에서 this 는 함수 (2) 의 함수인 함수에서 호출된 this 이지만,~
~innerFunc 가 화살표 함수 이기 때문에 this binding 이 생략되어서~
~전에 가지고 있던 this 인 obj 를 가리키게 된다~

%화살표 함수와 보통 함수의 가장 큰 차이점은 this binding 여부이

%콜백함수도 함수다
=> 따라서 콜백함수는 기본적으로 전역객체를 바라보게 되어있는데
콜백함수에 별도로 this 를 지정해준 경우를 예외로 가지고 있다

@생성자 함수 내부의 this

생성자 함수 -> 인스턴스를 만들기 위한 일종의 틀

여기서 instance 를 새로 만들 때 마다 this 는 그 instance 의 주체를 가리키게 된다

var Cat = function (name, age) {
    this.bark = '야옹';
    this.name = name;
    this.age = age;
};

var choco = new Cat('초코', 7); //this : choco
var nabi = new Cat('나비', 5);  //this : nabi

@명시적 this binding

// call, apply, bind ( 이 메서드 들을 살펴보게 된다 )

call & apply 는 메서드를 입력하는 즉시 함수를 호출한다
bind 는 함수를 this binding 해서 새로운 함수를 return 한다

let func = function (a, b, c) {
  console.log(this, a, b, c);
};

// func(1, 2, 3);

func.call({ x: 1 }, 4, 5, 6); // this 에 {x:1} 객체를 명시적으로 할당해준다

let obj = {
  a: 1,
  method: function (x, y) {
    console.log(this.a, x, y);
  },
};

// method 안에 있는 this 는 항상 obj 를 가리킬 수 밖에 없다
obj.method(2, 3); // 1 2 3 ( 1은 onj.a 에서 온 값 )

obj.method.call({ a: 4 }, 5, 6); // 4 5 6 ( this 에 a:4 라는 객체를 할당했기 때문에 this.a => 4 )

// apply 는 call 가 완전히 똑같은 기능을 한다
// 하지만 뒤에 있는 매개변수를 [] 로 넣어줘야 한다

obj.method.apply({ a: 4 }, [5, 6]); // 4 5 6

~bind의 목적~

  1. 함수에 this 를 미리 적용한다
  2. 부분 적용 함수
let func = function (a, b, c) {
  console.log(this, a, b, c);
};

// ex) 1
let bindFunc1 = func.bind({ x:1 })

bindFunc( 5, 6, 7); // {x:1} 5 6 7

// ex) 2
let bindFunc2 = func.bind({x:1} , 4, 5)

bindFunc2(10) // {x:1} 4 5 10

=> 위와 같이 미리 들어갈 요소를 지정해줄 수도 있다

  1. name 프로퍼티에 유의미한 이름이 들어간다
func.name // func
bindFunc.name //~bound~ func

@유사 배열 객체

유사 배열 객체의 조건

  • 반드시 length 가 존재해야 한다 ( 필수 )
  • index 번호가 0부터 시작해서 1씩 증가해야 한다 ( 가급적이면 )

배열은 아니지만 call, apply 를 통해서 배열의 메서드를 차용하게 할 수 있다
=> 이는 call&apply 가 즉시 실행함수 이기 때문에 this binding 을 하는 자리에 유사배열객체를 넣어 줌으로서 가능하게 해준다

Array.from() 에 유사배열객체를 넣으면 [] 로 만들어서 반환해준다
ES6 의 새로운 기능

0개의 댓글

관련 채용 정보