항해/TIL

TIL(2/7) / JS 3 (실행 컨텍스트, this)

yeeendy 2024. 2. 7. 22:09

실행 컨텍스트(Execute Context)

실행할 코드에 제공할 환경 정보들을 모아놓은 객체

실행 컨텍스트가 활성화되는 시점에 다음과 같은 일을 한다.

  • 선언된 변수를 위로 끌어올린다. (= 호이스팅)
  • 외부 환경 정보를 구성한다.
  • this 값을 설정한다.  

어려울 땐 콘솔로 디버깅하며 확인해보는 것이 도움된다!

 

 

예제) 생활코딩 참조

<script>
    n0='n0';
    var v0='v0';
    let l0='l0';
    const c0 = 'c0';
    console.log(v0, n0, l0, c0);
    console.log(window.v0, window.n0, window.l0, window.c0); // v0 n0 undefined undefined
    function fn2(){
        n2='n2';
        console.log(n0, n1, n2);
        var v2='v2';
        console.log(v0, v2);
        // console.log(v1)
        let l2='l2'; 
        console.log(l0, l2);
        // console.log(l1);
        const c2='c2;';
        console.log(c0, c2);
        // console.log(c1);
    }
    function fn1(){
        n1='n1';
        var v1='v1';
        let l1='l1';
        const c1='c1';
        fn2();
    }
    fn1();
    console.log(n2);
    // console.log(v2, l2, c2);
    </script>

n0 - Global Scope에 쌓인다

var로 선언한 v0도 - Global Scope에 쌓인다

letconst로 선언한 l0, c0은 Script Scope에 쌓인다

 

Global에서 가져온 변수는 Window.v0해도 출력된다.

 

console.log(window.v0, window.n0, window.l0, window.c0);

출력값으로 => v0 n0 undefined undefined 가 뜨는 이유

--> l0, c0은 Script Scope에 존재하기 때문에 window로 불러오면 참조할 값이 없다.

 

Call Stack에는 전역으로 anonymous(global context, 안 사라짐)함수가 쌓인다

Call Stack에 쌓인 함수로 해당 스코프를 참조할 수 있다.

 

 

global execute context에서 실행될 때

a = 1 Global
var a = 1 Global
let a = 1 Script
const a = 1 Script

 

function execute context에서 실행될 때

a = 1 Global
var a = 1 Local
let a = 1 Local
const a = 1 Local

 

this (call, apply, bind)

this : 전역 공간에서 this는 전역 객체를 가리킴 (window 혹은 global)

apply, call, bind : 함수를 상속받아 사용 가능한 메서드들

this 바인딩이란? this가 가리킬 객체를 표출하는 것을 의미

 

apply와 call은 인수전달 방식만 다를 뿐 동일하게 작동한다.

function getThisBinding(){
	console.log(arguments);
    return this;
}
const thisArg = { a : 1 };

console.log(getThisBinding.apply(thisArg, [1, 2, 3]));
console.log(getThisBinding.call(thisArg, 1, 2, 3));
// 출력값은 동일
// [Arguments] { '0': 1, '1': 2, '2': 3 }
// { a: 1 }

 

apply 메서드는 호출할 함수의 인수를 배열로 묶어 전달한다.

call 메서드는 호출할 함수의 인수를 쉼표로 구분한 리스트 형식으로 전달한다.

 

apply, call을 어디에 사용하려고?

-> arguments(위의 예시) 객체와 같은 유사 배열 객체배열 메서드를 사용하는 경우!

  • arguments 객체는 배열이 아니기 때문에 Array.prototype.slice같은 배열의 메서드를 사용할 수 없다.
    하지만 apply와 call 메서드를 이용하면 가능하다.
function converArgsToarray() {
	console.log(arguments);
    //arguments 객체를 배열로 변환
    //Array.prototype.slice를 인수 없이 호출하면 배열의 복사본을 생성한다.
    const arr = Array.prototype.slice.call(arguments);
    console.log(arr);
    
    return arr;
}

converArgsToarray(1, 2, 3) // [1, 2, 3]

 

 

bind 메서드는 apply와 call 메서드와 달리 함수를 호출하지 않는다.

다만 첫 번째 인수로 전달한 값으로 this 바인딩이 교체된 함수를 새롭게 생성해 반환한다.

  1. bind는 this를 적용한 함수를반환해준다
  2. bind를 이용하면 매개변수를 부분 적용시켜준다.
  3. bind를 이용하면 bound라는 접두어가 붙기 때문에 bind해줘서 반환한 함수를 식별할 수 있다

call이 아니라 bind를 쓰는 이유는 추후에 어떤 방식으로도 사용하기 위해서

const person = {
	name: 'Lee',
    foo(callback){
    	// bind 메서드로 callback 함수 내부의 this 바인딩을 전달
        setTimeout(callback.bind(this), 100)
    }
}

person.foo(function() {
	console.log(`Hi my name is ${this.name}.`); // Hi my name is Lee
})

 

함수 호출 방식 this 바인딩
일반 함수 호출 전역 객체
메서드 호출 메서드를 호출한 객체
생성자 함수 호출 생성자 함수가 (미래에) 생성할 인스턴스
Function.prototype.apply/call/bind 메서드에 의한
간접 호출
Function.prototype.apply/call/bind 메서드에
첫 번째 인수로 전달한 객체

참조) Deep Dive