TIL [this 키워드 수정 / subclass dance party해설]

1. new 키워드를 쓰면 인스턴스 === this이다.

class DancerClass{
  constructor(top, left, timeBetweenSteps){
    this.top = top;
    this.left = left;
    this.timeBetweenSteps = timeBetweenSteps;
  }
}  
let dancer = new DancerClass(300, 500, 3000);
dancer // { top: 300, left: 500, timeBetweenSteps: 3000 }

여기서는 dancer가 this가 되는 것이다.

2. 일반 함수안의 this === global(window)를 의미한다.

function myFunction(){
  console.log(this === globalThis); // true
}

myFunction(); 

3. 객체안의 메서드로 있는 함수안의 this === 객체

var myObject = {
  myFunction: function(){
    console.log(this === myObject); // true
  }
};

myObject.myFunction();

4. 새로운 변수에 객체안의 메서드를 할당해서 실행하는 경우 메서드 안의 this !== 객체

var myObject = {
  myFunction: function(){
    console.log(this === myObject); // false
  }
};
var myFunction = myObject.myFunction;
myFunction(); 

5. setTimeout에 콜백 함수로 들어가는 함수안의 this

var myObject = {
  myFunction: function(){
    console.log(this === myObject); // true
    setTimeout(function(){
      console.log(this === myObject); // false
      console.log(this === global); // true
    }, 0);
  }
};

겉보기엔 setTimout의 콜백 함수안의 this는 객체를 가리킬 것 같지만 사실 global을 가리킨다. setTimeout은 외부에 있는 함수이고 setTimout 함수가 있는 주소를 빌려온 것이기 때문에 사실상 myObject 객체안에 없다고 봐도 무방하다. 따라서 obj.func()밖의 모든 this는 global이라는 규칙답게 setTimeout의 콜백 함수 안의 this도 global을 의미한다.

-이해하기 쉽게 재호님께서 직접 그려주신 그림 🙇‍🙇‍🙇‍

6. call, apply, bind 우리가 인위적으로 this에 객체를 대입시키는 역할을 한다.

var myObject = {
  myFunction: function(a, b){
    console.log(a + ' ' + b); // 'hello world'
    console.log(this === myObject); // false
    console.log(this === myOtherObject) // true
  }
};

var myOtherObject = {};

myObject.myFunction.call(myOtherObject, 'hello', 'world');

원래대로라면 this는 myObject가 맞지만, call로 인해 인위적으로 myOtherObject 라는 객체를 넣어줌으로 this는 myOtherObject가 되었다.

7. 화살표 함수

let obj = {};
obj.func = () => { 
  return this;
}


obj.func() // global

여기서 this가 obj를 가리킬 것 같지만 global을 가리킨다. 이런 경우 화살표 함수 안의 this는 대부분 global이라고 보면 된다.

"객체의 메서드로 호출이 되면 this === 객체, 그 외의 모든 것은 global이다."

obj.func()에서 func()앞에 .이나 []로 감싸져있지 func()안의 모든 this는 global이라는 뜻이다.

이것만 외워도 된다!!

그럼 오늘 나를 죽고싶게 만들었던 subclass dance party를 찢어보자.

1. class 형에서 DancerClass.js 파일 심층 분석

이 class를 가지고 인스턴스를 생성해 보자.

let dancer = new DancerClass(30, 50, 2000);

이러면 dancer라는 인스턴스가 생성되고 모양은 이럴것이다.

{top: 30, left: 50, timeBetweenSteps: 2000, $node: span.dancer}

바로 객체이다. 모든 객체의 국룰은 __proto__가 있다는 것이다. 이 __proto__는 어디에 연결되어 있을까? 당연히 이 객체를 만들어 낸 DancerClass라는 클래스(함수)의 prototype에 연결되어 있을것이다.
실제로 콘솔창에 찍어보면

이렇게 우리가 클래스안에서 만든 메서드 createDancerElement, step, setPosition이 아주 귀엽게 잘 보관되어있다. 왜 다들 아는 사실을 이렇게 길게 설명했는지 이제 말하겠다.

바로 이 코드... 죽이겠다 널.. setTimeout(this.step.bind(this), ...)이런 흉물스러운 문법을 다 봤나. 진짜 오늘밤에 이해하지 못했으면 억울해서 사망했을 것이다. 그럼 바로 해설에 들어가겠다. setTimeout은 step이라는 메서드 안에서 썼지만 사실은 외부에서 끌어온 함수이다. 이 함수안에 this.step이라고 적는다면 이 this는 'obj.func()에서 func()앞에 .이나 []로 감싸져있지 func()안의 모든 this는 global이라는 뜻이다.'의 규칙에 의해 this가 global을 향하게 된다. 이런 젠장. global에는 눈을 씻고 찾아봐도 우리가 원하는 step이라는 메서드가 없을 뿐더러, 있더라도 우리가 원하는 step이 아니다.

그래서 나온 해법이 this.step.bind(this)이다. bind안의 this는 인스턴스이다. 즉 아까 dancer라는 인스턴스를 생성했으니 이 this는 dancer가 된다. 결국 해석해보면 나는 this.step이라는 메서드(함수)안에 나오는 모든 this를 dancer로 바꿔주겠다는 뜻이다. 이렇게 하면 setTimeout안의 this.step이 global을 향하는게 아니라 dancer를 향하게 되고 dancer객체 안에 .__proto__로 연결된 DancerClass.prototype 안에 step 메서드를 불러오게 된다.

좋은 웹페이지 즐겨찾기