Knowledge/javascript

javascript this와 bind

TakeKnowledge 2019. 9. 15. 13:46
반응형

생성자 패턴을 활용하다 보면 javascript의 this가 내 맘 같지 않은 상황을 자주 마주 한다.

예를 들자면 이런 것이다.

 

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
31
32
function promotionObj(id) {
    
        this.registerEvents(id);
        
}
 
promotionObj.prototype = {
        
        // 등록 이벤트
        registerEvents : function(id){
            // 뒤로 가기 버튼
            var backBtn = document.querySelector(".btn_back");
            
            backBtn.addEventListener("click",function(){
                this.backToDetail(id);
            });
        },
        
        
        // 상세보기로 이동
        backToDetail : function(id){
            location.href = "/reservation/detail?id="+id;
        }    
        
}
 
document.addEventListener('DOMContentLoaded'function() {
    
    // promotion 객체 생성
    promotion = new promotionObj(initObj.id);
    
});

 

'promotion 객체를 생성할 때 객체내의 registerEvents 함수를 실행하여

backBtn에 backToDetail을 실행하는 클릭 이벤트 리스너를 달아라' 라는 내용의 코드인데

실행해보면 의도대로 작동되지 않는다.

 

원인을 찾아보면 this.backToDetail(id); 부분이 문제다.

분명 같은 객체 안에 있으니 this.bakcToDetail()이라고 하면 같은 객체안에 있는 backToDetail 함수를 가르킬 것 같지만 console.log를 활용해 확인하면 this가 undefinded라고 출력되기 떄문이다. 왜일까?

 

이는 javascript에서 this의 특성때문이다.

 

javascript의 this는 현재 실행 문맥에 따라 결정된다. 즉 해당 메소드의 호출자가 누구인지에 따라 this가 정해진다는 것.

registerEvents() 를 예로 들자면 이 메소드를 호출하는 것은 명백히 promotionObj 객체기 때문에

this를 사용했을 때 이 this가 promotionObj를 가르키고 있음을 알고 의도대로 잘 작동한다.

 

그런데 자세히 보면 backBtn.addEventListener("click",function(){  this.backToDetail(id); }); 는 registerEvents() 메소드 내부에 있다. 즉 registerEvents() 내부에서 실행될 뿐 이 메소드 자체에 대한 호출자가 명확하지 않고 그게 이 함수에서 this가 undefinded로 확인되는 이유다. 그럼 어떻게 해야할까?

 

복잡한 원인에 비해 해결책은 간단하다. this는 실행 문맥에 따라 결정되니 상위 메소드(?) registerEvents()의 실행 문맥을  backBtn.addEventListener("click",function(){  this.backToDetail(id); }); 에 bind해주면된다. 설명은 되려 복잡하니 아래 코드를 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
promotionObj.prototype = {
        
        // 등록 이벤트
        registerEvents : function(id){
            // 뒤로 가기 버튼
            var backBtn = document.querySelector(".btn_back");
            
            backBtn.addEventListener("click",function(){
                this.backToDetail(id);
            }.bind(this));
        }
 
}
 

 

불필요한 부분을 지운 것을 제외하고 위 코드와 달라진 건 .bind(this)가 추가된 것 밖에 없다.

이것만 추가하면 this가 promotionObj 객체를 가르켜 코드가 의도대로 잘 작동한다.

 

사실 이게 유일한 해결책은 아니다.  ES6의 arrow 함수를 사용하면 bind(this)를 사용하지 않고도 this를 잘 활용할 수 있다고 한다. 근데 이 부분은 잘 모르니 추후 다시 공부해서 포스팅 하는 걸로 하고 this와 bind를 이해하는데 큰 도움을 준 블로그와 edwith 강좌 해당편의 링크를 남기며 포스팅을 마친다

 

참고 

 

 

https://blueshw.github.io/2018/03/12/this/

 

blueshw.github.io

 

[LECTURE] 3) bind메소드로 this제어하기 : edwith

들어가기 전에 this 키워드는 this를 사용하고 있는 함수가 어떻게 불리는가에 따라서 달라지는 경우가 있습니다. 이때 그 함수의 context가 참고하는 객체를 원하는 것으로 ... - moons

www.edwith.org

 

반응형