-
JavaScript에서의 thisLanguage/JavaScript 2024. 3. 6. 19:23
this는 다른 언어에서는 자기 자신을 가리키는 참조 변수로 사용되는 경우가 많다.
그래서 JavaScript에서도 이와 유사하게 사용하려고 하였으나 예상한 대로 코드가 동작하지 않는 문제가 발생하였다.
class CategoryController extends CommonController { constructor() { super(new CategoryModel()); } loadCategories(req, res) { categoryModel.loadCategories((err, result) => { this.requestCallback(err, result, res); }); } }위 코드에서 CategoryController는 CommonController를 확장해서 만들어진 객체이다.
CommonController에는 requestCallback이라는 함수가 존재하여 CategorytController에서 상속받아 사용하기 위해 this를 통해 함수를 호출하려고 했다.
그러나 예상과 다르게 this를 찾지 못하여 오류가 발생하였다.
이를 해결하기 위해 JavaScript에서의 this를 간단히 학습해 보았다.
JavaScript에서의 this
JavaScript에서 this는 다른 언어와 조금 다르게 동작한다.
대부분의 this 값은 함수를 호출하는 방법에 의해 결정된다.
또한, 함수를 어떻게 호출했는지 상관하지 않고 this 값을 설정할 수 있는 방법 또한 존재한다.
Global context에서의 this
this는 JavaScript의 모든 곳에 존재하기 때문에, 전역 환경과 일반 함수에서 무엇을 가리키는지 간단히 확인할 수 있다.
console.log(this) function foo(){ console.log(this); } foo();
위의 코드를 통해 전역 변수와 일반 함수에서 this를 출력하면 둘 다 window가 나오는 것을 알 수 있다.
window는 모든 전역변수, 함수, DOM을 보관하고 관리하는 전역객체이다.
※ Node.js에서는 global이 출력된다.
※ 만약 strict mode라면 일반함수에서 호출한 this는 undefined가 출력된다.
Function Context에서의 this
함수 안의 this는 호출 방식에 따라 가리키는 대상이 달라진다.
function foo(){ console.log(this); } const obj = { list : [1, 2, 3], foo, } const obj2 = } a: "object2", } foo(); // window obj.foo(); // obj foo.call(obj2); // obj2 foo.apply(obj2); // obj2위의 코드에서 같은 foo를 호출했음에도 출력되는 this가 달라지는 것을 알 수 있었다.
foo()는 전역 함수이기 때문에 window.foo()로도 바꿀 수 있다.
그렇기 때문에 foo()는 window를 obj.foo()는 obj를 가리키는 것이다.
또한, call과 apply 메서드를 사용하여 가리키고자 하는 this를 지정해 줄 수 있다.
const obj = { printThis(){ setTimeout( function(){ console.log(this); }, 2000); } } const obj2 = { printThis2(){ setTimeout( () => { console.log(this); }, 2000); } } obj.printThis(); // window obj2.printThis2(); // obj2위 코드는 화살표 함수를 이용했을 때 this가 가리키는 대상이 달라지는 경우이다.
위의 코드에서는 비동기 함수인 setTimeout을 사용하고 있다.
원인을 생각해 보았는데, setTimeout에서 콜백 큐로 콜백 함수를 넘겨주는데, 이때 콜백 큐가 전역 환경인 window에 포함되어 있기 때문이 아닐까라고 생각한다.
그래서 setTimeout의 지연 시간이 지난 후에 콜백 큐에서 함수를 가져오면 일반 함수에서 this를 출력하는 것과 같이 window가 나오는 것이지 아닐까라고 예상한다.
추후 다시 알아볼 예정이다.
이러한 현상을 해결하기 위해서는 화살표 함수를 사용할 수 있다.
화살표 함수에서의 this는 자신을 감싸고 있는 정적 범위를 가리킨다.
화살표 함수는 this가 존재하지 않기 때문에 상위 환경으로 올라가 this를 가져오게 된다.
따라서 obj2.printThis2()의 결과는 obj2가 나오는 것을 알 수 있다.
bind 메서드
위와 같은 호출방식에 구애받지 않고 this가 값을 설정하려면 bind 메서드를 사용하면 된다.
function foo(){ console.log(this.a); } foo(); // undefined const bar = foo.bind({a: "fie" }); bar(); // fie const baz = bar.bind({a: "foe" }); // bind는 한번만 동작 baz(); // fie위의 코드를 보면 기존의 foo는 this가 window를 가리키기 대문에 this.a를 찾지 못해 undefined를 출력한다.
bar는 foo의 this를 bind를 이용하여 a가 fie인 객체로 설정해 주었기 때문에 실행시키면 fie가 나오는 것을 알 수 있다.
baz의 경우 bar에 한번 더 bind를 이용하여 this 값을 바꾸어 주려고 하고 있다.
그러나 bind는 한 번만 적용되기 때문에 baz를 실행시키면 foe가 아닌 fie가 출력되는 것을 알 수 있다.
해결 방법
화살표 함수로 해결
그래서 처음 마주친 문제를 해결하기 위해서는 여러 방법을 사용해 볼 수 있다.
처음 코드에서 this가 가리키는 것은 CategoryController가 아닌 categoryModel이다.
this를 상위 환경인 CategoryController를 가리키도록 하기 위해 메서드를 화살표 함수로 호출하는 방법이 있다.
class CategoryController extends CommonController { constructor() { super(new CategoryModel()); } // loadCategories(req, res) { loadCategories = (req, res) => { categoryModel.loadCategories((err, result) => { this.requestCallback(err, result, res); }); } }메서드를 정의할 때 function 형태가 아닌 화살표 함수로 정의하여 this가 CategoryController를 가리키도록 하여 문제를 해결할 수 있다.
bind 메서드로 해결
router.get('/load', categoryController.loadCategories.bind(categoryController));아니면, 간단하게 해당 메서드를 사용하는 곳에서 bind를 이용하여 this가 가리키는 값을 고정시킬 수 있다.
참고
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/this
https://youtu.be/fllhA9yGSYE?si=RdKN5n4inRgBY2UT
'Language > JavaScript' 카테고리의 다른 글
fetch를 학습하다 JavaScript 동작원리를 학습한 건에 대하여... (1) 2023.11.19