본문 바로가기
👩‍💻 Programming/JavaScript

'this'를 갈고 닦자!

by codingBear 2022. 4. 7.
728x90
반응형

 이번 글은 'JavaScriptの理解を深めた人がさらにもう1歩先に進むための本(JavaScript를 깊게 이해한 사람이 한 걸음 더 나아가기 위한 책)의 Chapter 3. thisを極めよう!(this를 갈고 닦자!)'를 바탕으로 작성하였습니다.


함수에서 호출했을 때의 'this'

 함수에서 호출했을 때 this는 global object를 가리킨다. 대부분의 경우 JavaScript는 브라우저에서 동작하기 때문에 global object는 'window object'를 가리키는 것이다.

function checkThis() { 
    console.log(this);  // Window ※ 브라우저에서 실행했을 경우
    this.val = 'test';  // global scope member로 추가됨 
} 
 
checkThis(); 
console.log(val);  // test

 '함수 안에서 this를 참조할 경우 global object를 참조하게 된다'는 점만 빼면 특별히 어려운 부분은 없다.


Method에서 호출했을 때의 'this'

 method에서 호출했을 때 this는 '그 method가 속하는 object'를 가리킨다.

let obj = { 
    val: 'hoge', 
    checkThis: function() { 
        console.log(this);  // Object {val: 'hoge'} 
        this.val = 'fuga'; 
    } 
}; 
 
obj.checkThis(); 
console.log(obj.val);  // fuga

 checkThis method 안에서 호출한 this는 'method가 속한 obj object'를 가리킨다는 점을 알 수 있다.

 다음 예도 살펴보자.

let obj2 = { 
    val: 'hoge', 
    checkThis: function() { 
        console.log(this.val);  // hoge 
        this.val = 'fuga'; 
 
        function innerCheckThis() { 
            console.log(this.val);  // undefined 
        } 
 
        innerCheckThis(); 
    } 
}; 
 
obj2.checkThis(); 
console.log(obj2.val);  // fuga

 checkThis method에서 호출한 this는 obj2를 가리키기에 출력 결과는 'hoge'가 된다. 그리고 innerCheckThis 함수에서 호출한 this는 global object를 가리키기 때문에 출력 결과는 'undefined'(strict mode에서는 error)가 된다.

 중요한 점은 'method'이냐 '함수'이냐 하는 점이다. method와 함수가 섞인 코드에서 출력 결과의 혼선을 막기 위해 자주 쓰이는 기술이 있다. 사전에 this를 변수에 담아두는 것이다.

let obj3 = { 
    val: 'hoge', 
    checkThis: function() { 
        let self = this; 
        console.log(self.val);  // hoge 
 
        function innerCheckThis() { 
            console.log(self.val);  // hoge 
        } 
 
        innerCheckThis(); 
    } 
}; 
 
obj3.checkThis();

 위와 같은 코드 작성 방식은 실전에 매우 유용하다. this를 담아두는 변수명은 self, _this, that 등이 널리 쓰인다.


Constructor에서 호출했을 때의 'this'

 constructor에서 호출했을 때의 this는 'constructor가 생성한 instance'를 가리킨다.

let MyObj = function(val) { 
    this.val = val; 
} 
 
MyObj.prototype.checkThis = function() { 
    console.log(this.val); 
} 
 
let myObj = new MyObj('hoge'); 
myObj.checkThis();  // hoge 
 
let myObj2 = new MyObj('fuga'); 
myObj2.checkThis();  // fuga

 위 예제 코드는 이른바 'class like'한 코드를 JavaScript에서 구현하기 위해 흔히 작성하는 패턴이다. 'new 연산자로 호출된 constructor는 자신이 생성한 instance를 호출한 곳으로 반환한다'는 말은 'constructor로 호출한 this는 반환한 instance에 속한다'는 말이다.


call, apply로 호출했을 때의 'this'

 call method와 apply method는 'this'를 '속박'하기 위한 method이다. 상황에 따라 기능이 달라지는 this에게 '항상 지정한 대로 행동하라'고 강제한다.

 자세한 사항은 예제를 보며 익혀보자.

function sum(val1, val2) { 
    console.log(this.val + val1 + val2); 
} 
 
let obj1 = {val: 1}; 
let obj2 = {val: 2}; 
 
sum.call(obj1, 1, 1);  // 3(1+1+1로 실행) 
sum.call(obj2, 1, 1);  // 4(2+1+1로 실행) 
 
sum.apply(obj1, [1, 1]);  // 3(1+1+1로 실행) 
sum.apply(obj2, [1, 1]);  // 4(2+1+1로 실행)

 call method와 apply method는 함수에 argument를 전할 때 작성하는 방법 말고는 똑같다. 둘 다 '지정한 object를 this로 삼아 함수를 실행한 결과'를 얻을 수 있다.


bind했을 때의 'this'

 call method와 apply method 이외에도 bind method를 사용하여 this를 '속박'하는 방법도 있다.

 사용법은 다음과 같다.

function sum(val1, val2) { 
    console.log(this.val + val1 + val2); 
} 
 
let obj1 = {val: 1}; 
let obj2 = {val: 2}; 
 
let obj1Sum = sum.bind(obj1); 
let obj2Sum = sum.bind(obj2, 2, 2); 
 
obj1Sum(1, 1);  // 3(1+1+1로 실행) 
obj2Sum();  // 6(2+2+2로 실행)

 예제에 나와 있듯 bind method에는 두 종류가 있다. 하나는 'this만을 지정하는 방법'이고 다른 하나는 'this와 함께 미리 argument까지 지정하는 방법'이다. 둘 다 기능은 같으니 용도에 맞춰 쓰면 된다.

 참고로 bind method는 setTimeout 함수나 addEventListener 함수 등 콜백 함수로서 this에 의존하는 함수를 사용하고 싶을 때 매우 유용하다.

 bind method를 살펴본 김에 다른 쓰임새도 알아보자. 바로 'argument의 속박'이다.

function sum(val1, val2, val3) { 
    console.log(val1 + val2 + val3); 
} 
 
let sumA = sum.bind(null, 1, 2, 3);  // argument를 전부 속박 
let sumB = sum.bind(null, 1);  // 일부 argument만 속박
 
sumA();  // 6(1+2+3으로 실행) 
sumB(2, 3);  // 6(1+2+3으로 실행) 
sumB(4, 5);  // 10(1+4+5로 실행)

Arrow 함수에서 호출했을 때의 'this'

 마지막으로 ES6에 추가된 arrow 함수로 호출했을 때 this의 기능에 대해 알아보겠다. arrow 함수에서는 함수를 정의할 때 생성된 context(scope)의 this를 항상 참조한다. 즉 arrow 함수가 정의된 곳에 따라 함수 내부의 this 값이 고정된다는 것이다.

let obj = { 
    val: 'hoge', 
    show: function () { 
        console.log(this);  // Object {val: 'hoge'} 
 
        let fncA = function () { 
            console.log(this);  // Window ※브라우저에서 작동했을 경우
        }; 
        fncA(); 
 
        let fncB = () => { 
            console.log(this);  // Object {val: 'hoge'} 
        }; 
        fncB(); 
    } 
}; 
 
obj.show();

 fncA와 fncB의 실행 결과를 보면 차이점을 확연히 알 수 있다. 앞서 설명했듯 일반적으로 '함수가 호출했을 때의 this는 global object'를 가리킨다. 하지만 'arrow 함수로 호출했을 때의 this는 함수를 정의했을 때의 context를 참조'하기 때문에 결과적으로 'method로 호출했을 경우'와 같은 결과를 반환한다.


함께 보기

  • 함수 장인이 되는 길 from Secrets of the JavaScript Ninja

https://gdk01.tistory.com/49?category=1050007 

 

함수 장인이 되는 길_함수 호출에 대한 이해 from Secrets of the JavaScript Ninja

이번 글은 'Secrets of the JavaScript Ninja'의 'Chapter 4. Funtions for the journeyman: understanding function invocation'를 바탕으로 작성하였습니다. 핵심 Concepts function의 내재된 parameter 2가지:..

gdk01.tistory.com

  • this MDN

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

 

this - JavaScript | MDN

A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode.

developer.mozilla.org

 

728x90
반응형

댓글