원인, 일반적인 객체지향 언어에서 this는 클래스에서만 사용할 수 있음. 클래스로 생성한 인스턴스 객체를 의미함. 그러나 js는 어디서든 사용할 수 있고 상황에 따라 this가 바라보는 대상이 달라짐. 함수와 객체(메서드)의 기준이 느슨한 js에서 this는 이 둘을 구분하는 “거의” 유일한 기능

  1. 생성 시점
    1. 실행 컨텍스트가 생성될 때 == 함수가 호출될 때
  2. this가 바라보는 대상
    1. 함수가 호출되는 방식에 따라 달라짐

    2. 전역 공간에서

      1. 전역 객체

      2. 개념상 전역 컨텍스트를 생성하는 주체가 전역 객체이기 때문

      3. 자바스크립트 런타임 환경에 따라 다른 이름과 정보를 가지고, 브라우저 환경에서는 window, Node.js 환경에서는 global

        // browser 환경
        console.log(this === window) // true
        // Node.js 환경
        console.log(this === global) // true
        
      4. 특이한 점

        1. window.a = 1 와 var a = 1 의 동작 방식이 유사하게 됨
        2. 하지만 delete 하려는 경우 처음부터 전역 객체의 프로퍼티로 생성된 경우에는 가능하지만, 전역 변수로 선언된 경우에는 불가능. (일종의 방어 전략)
    3. 메서드로서 호출할 때 그 메서드 내부에서의 this

      1. 메서드란
        1. 어떤 객체의 프로퍼티로서 “호출된” 함수
        2. 메서드를 호출한 주체의 객체를 담는 것임!
      2. 메서드 내부에서의 this
    4. 함수로서 호출할 때 그 함수 내부에서의 this

      1. 메서드 내부에서 지역 함수를 선언하고 바로 실행하는 경우 그 지역 함수 내에서 this가 사용되면 이 this는 전역객체가 됨 왜냐면 “실행할 때” 메서드로서 호출하지 않았으니까 주체 객체랄게 없었으니까..
    5. 콜백함수를 호출할 때 그 함수 내부에서의 this

      1. 콜백함수를 인자로 받은 함수가 그 콜백함수를 어떻게 사용할 것인지 정의해놓은것에 따라 다름
      2. ex)
      배열.forEach(v⇒ { console.log(this); }) // 전역객체
      
      const $div = document.querySelector("#root");
      $div.addEventListener("click", function () {console.log(this);});  // $div
      
    6. 바꾸는 법

      1. call(this, arg)
      2. apply(this, arg1, arg2, …)
      3. bind(this, arg1, arg2, …) ⇒ bound func
    7. 화살표 함수는 왜?

      1. 메서드 내부에서의 this의 경우에서의 혼란을 줄여줌

      2. == bind 필요없이 스코프 체인 상 가장 가까운 객체를 따라감

      3. ex)

        // 선언식
        var obj1 = {
        	outerFunc: function () {
        		console.log(this);
        		var innerFunc = function () {
        			console.log(this);
        		};
        		innerFunc(); // window
        		// 선언식 바인딩 하려면
        		var boundInnerFunc = function () {
        			console.log(this);
        		}.bind(this);
        		boundInnerFunc(); // obj1;
        	}
        }
        obj1.outerFunc(); // obj1
        
        // 표현식 (화살표함수)
        var obj1 = {
        	outerFunc: () => {
        		console.log(this);
        		var innerFunc = () => {
        			console.log(this);
        		};
        		innerFunc(); // obj1
        	}
        }
        obj1.outerFunc(); // obj1