이벤트 핸들러 내부의 this

ursr_log·2020년 12월 11일
7
post-thumbnail

(참고 사이트)
MDN - HTML onevent attributes
MDN - DOM onevent handlers
(참고서적) 모던 자바스크립트 Deep Dive 자바스크립트의 기본 개념과 동작 원리 - 이웅모 저

이벤트 핸들러 내부의 this에 대하여

<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" onclick="console.log(this)" />
  </body>
</html>

위 코드의 버튼을 클릭했을 때 console창에 this가 어떻게 출력될까?
먼저 this에 대해 살펴봐야 한다.

함수 내부의 this

자바스크립트의 this는 함수가 호출되는 방식에 따라 this에 바인딩될 값, 즉 this 바인딩이 동적으로 결정된다.

함수 호출 방식에 따른 this 바인딩

this 바인딩(this에 바인딩될 값)은 함수 호출 방식, 즉 함수가 어떻게 호출되었는지에 따라 동적으로 결정된다.

  1. 일반 함수 호출
    • 일반 함수로 호출하면 함수 내부의 this에는 전역 객체가 바인딩된다.
      (this는 객체의 프로퍼티나 메서드를 참조하기 위한 자기 참조 변수이므로 객체를 생성하지 않는 일반 함수에서 this는 의미가 없기 때문에 기본적으로 this에는 전역 객체(global object)가 바인딩된다.)
  2. 메서드 호출
    • 메서드 내부의 this에는 메서드를 호출한 객체, 즉 메서드를 호출할 때 메서드 이름 앞의 마침표(.) 연산자 앞에 기술한 객체가 바인딩된다.
  3. 생성자 함수 호출
    • 생성자 함수 내부의 this에는 생성자 함수가 (미래에) 생성할 인스턴스가 바인딩된다.

이렇게 함수 내부의 this바인딩은 함수의 호출 방식에 따라 다르게 결정된다.

그런데 이러한 내용대로라면 <input type="button" value="click" id="btn" /> 이 요소를 클릭했을 때 출력되는 this는 전역객체인 window가 나와야 할 것 같은데 클릭한 input 요소가 출력된다.

왜 클릭한 input요소가 출력되는 것일까?

이벤트 핸들러 내부의 this

이번엔 다음의 코드를 실행해서 input요소를 클릭해보자.

<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" onclick="printThis()" />
    <script>
      function printThis() {
        console.log(this);
      }
    </script>
  </body>
</html>

이번엔 console.log(this)를 실행하는 printThis함수를 선언해서 이벤트 핸들러 어트리뷰트의 값으로 "printThis()"를 넣었다.
이번에도 전과 같이 요소를 클릭하면 console창에 input요소가 나타날까?

이번에는 window객체가 출력됐다. 왜 this가 다르게 나타나는 걸까?
그렇다면 이벤트 핸들러 프로퍼티방식addEventListener 메서드 방식으로도 적용해보자.

1-1 이벤트 핸들러 프로퍼티 방식
<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" />
    <script>
      //이벤트 핸들러 프로퍼티 방식
      document.getElementById('btn').onclick = printThis;
      function printThis() {
        console.log(this);
      }
    </script>
  </body>
</html>
1-1 결과

1-2 addEventListener 메서드 방식
<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" />
    <script>
      //addEventListener 메서드 방식
      document.getElementById('btn').addEventListener('click', printThis);
      function printThis() {
        console.log(this);
      }
    </script>
  </body>
</html>
1-2 결과

이벤트 핸들러 프로퍼티방식addEventListener 메서드 방식모두 클릭 이벤트의 결과로 클릭한 요소가 출력됐다.

이벤트 핸들러 내부의 this가 DOM요소가 되는 이유

유사한 여러개의 요소가 있고 모두 같은 이벤트 핸들러가 등록되었을 때 그 이벤트 핸들러는 이벤트가 일어난 요소를 참조하는 것이 편할 것이다.
이것이 이벤트 핸들러 내부의 this가 이벤트가 일어난 요소를 가리키는 이유이다.
따라서 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.
그러므로 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.

그런데, <input type="button" value="click" id="btn" />과 같이 html요소에 인라인형식으로 써주는 이벤트 핸들러 어트리뷰트 방식은 어째서 동작이 다른 것 같아 보일까?
이벤트 핸들러 어트리뷰트 값은 내부적인 다른 동작이 따르기 때문이다.

이벤트 핸들러 어트리뷰트 방식의 내부 동작

이벤트 핸들러 어트리뷰트 방식은 이벤트 핸들러 어트리뷰트 값으로 함수 호출문을 할당하면 이벤트 핸들러가 등록된다. 이때 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미한다.

<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" onclick="printThis()" />
    <script>
      function printThis() {
        console.log(this);
      }
    </script>
  </body>
</html>

위의 onclick="printThis()"어트리뷰트는 파싱되어 이벤트 핸들러 어트리뷰트 이름과 같은 이름의 함수를 암묵적으로 생성하고, 이벤트 핸들러 어트리뷰트 이름과 동일한 이벤트 핸들러 프로퍼티에 할당한다.
해당 내용의 코드를 표현해보면 다음과 같다.

<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" onclick="printThis()" />
    <script>
      function printThis() {
        console.log(this);
      }
      
      
      // 내부 동작에 대한 의사코드
      
      // 이벤트 핸들러 어트리뷰트이름과 같은 이름의 onclick 함수 생성
      function onclick(event) {
      	printThis();
      }
      
      // 동일한 이름인 onclick프로퍼티에 암묵적으로 생성한 onclick함수를 할당한다.
      document.querySelector('#btn').onclick = onclick;
    </script>
  </body>
</html>

위와 같이 printThis함수는 암묵적으로 생성된 onclick함수내에서 '일반함수'로서 호출된다.
다시 말하자면 이벤트 핸들러에 등록된 함수는 printThis함수가 아닌 암묵적으로 생성된 onclick함수이며, onclick함수안에서 printThis함수는 일반함수로서 호출되어 printThis함수 내부의 this는 전역객체가 되는 것이다. 따라서 클릭 이벤트 발생 후 console창에는 window가 출력되는 것이다.
또한, 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리키기 때문에 암묵적으로 생성된 onclick함수 내부의 this또한 이벤트를 바인딩한 DOM 요소를 가리킨다.(다시 말하지만 이벤트 핸들러에 등록된 함수는 printThis함수가 아닌 암묵적으로 생성된 onclick함수이다.)

이는 아래의 코드를 실행하여 확인해보면 알 수 있다.

<!DOCTYPE html>
<html>
  <body>
    <input type="button" value="click" id="btn" onclick="console.log(this)" />
  </body>
</html>

실행해보면 input요소가 출력되는 것을 확인할 수 있을 것이다.

결론

  1. 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소이다.
  2. 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미한다는 것에 주의하자.

0개의 댓글