티스토리 뷰

728x90

문제

프로젝트를 진행하던 중에 this와 관련된 문제를 맞닥뜨렸습니다. 공부를 해도 해도 모르는 게 또 나오네요... 🤮🤮🤮

map() 함수 내의 콜백에서 사용된 this가 계속 undefined가 되어 버그가 나는 상황이었습니다.

 

당시 상황을 간략하게 보여주자면,

class T {
    constructor() {
        this.arr = [1, 2, 3];
    };

    printThis() {
        console.log(this);
    };

    bug() {
        this.arr.map(this.printThis);
    };
}

const obj = new T();
console.log('========= printThis() =========');
obj.printThis();
console.log('========= bug() =========');
obj.bug();

이런 상황입니다.

 

printThis() 메소드는 this를 출력하고, bug() 메소드는 printThis()를 map()의 인자로 넘겨 실행합니다.

이때 실행 결과는 다음과 같습니다.

 

// result
========= printThis() =========
T { arr: [ 1, 2, 3 ] }
========= bug() =========
undefined
undefined
undefined

 

원인과 해결법

this는 대부분의 경우 동적으로 바인딩됩니다.

같은 class 내에서 사용한 터라 의식적으로 생각하지 않았는데, 생각해보면 당연히 의도대로 동작하지 않는 게 정상입니다.

정확한 원인은 생각보다 명확했습니다. Array.prototype.map()의 콜백 내의 this는 기본적으로 undefined로 바인딩된다고 합니다.

 

해결법엔 여러가지가 있습니다.

 

1. map()의 두 번째 인자

Array.prototype.map()은 사실 매개변수가 두 개입니다.

첫 번째 매개변수는 실행할 콜백이고, 두 번째 매개변수로 콜백의 this로 바인딩될 객체 thisArg를 받습니다. thisArg로 class의 this를 넣어주면 간단히 해결할 수 있습니다.

 

class T {
    constructor() {
        this.arr = [1, 2, 3];
    };

    printThis() {
        console.log(this);
    };

    bug() {
        this.arr.map(this.printThis, this);
    };
}

 

2. Function.prototype.bind()

bind()는 함수의 공통 메소드입니다. 인자로 thisArg를 받으며, bind() 메소드를 실행한 함수에 thisArg가 this로 바인딩된 새로운 함수를 반환합니다.

map()의 콜백으로 클래스의 this가 바인딩된 메소드를 넘겨줍시다.

 

class T {
    constructor() {
        this.arr = [1, 2, 3];
    };

    printThis() {
        console.log(this);
    };

    bug() {
        this.arr.map(this.printThis.bind(this));
    };
}

 

3. Arrow Function

화살표 함수의 this 바인딩은 조금 다릅니다. 동적으로 바인딩되어 자체 this를 가지는 일반 함수와는 다르게, 화살표 함수는 함수가 선언된 위치에서 상위 lexical scope의 this를 따라갑니다.

 

코드를 살펴보면 다음과 같습니다.

class T {
    constructor() {
        this.arr = [1, 2, 3];
    };

    printThis() {
        console.log(this);
    };

    bug() {
        this.arr.map(() => this.printThis());
    }
}

map()의 콜백으로 익명 화살표 함수를 만들고 내부에서 this.printThis()를 실행합니다.

화살표 함수 내의 thisclass T의 this와 같고, this 바인딩 규칙에 따라 화살표 함수 내에서 실행된 printThis() 내부의 this는 해당 메소드를 실행한 주체인 화살표 함수의 this가 됩니다.

 

정리하며

몇 가지 방법들을 생각해봤지만 역시 map()에서 공식적으로 지원하는 첫 번째 방법이 좋지 않을까 싶습니다. 나머지 방법은 this 바인딩을 지원하지 않는 함수에 콜백을 넘길 때 생각해봄직한 방법입니다.

 

이런 문제들을 만날 때마다 역시 자바스크립트라는 언어 자체에 대한 근본적인 원리를 알고 있어야 원활한 디버깅을 할 수 있겠다는 생각이 많이 듭니다.

 

레퍼런스

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/map

 

Array.prototype.map() - JavaScript | MDN

map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다.

developer.mozilla.org

 

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함