프린세스 다이어리

자바스크립트 this란 무엇인가 본문

FE

자바스크립트 this란 무엇인가

개발공주 2021. 11. 28. 12:15
728x90
this란 함수를 호출할 때 생성되는 실행 컨텍스트 객체다.

 

this가 가리키는 대상은 어떻게 this가 호출되는지에 따라 다르다. 아래 상황별로 this의 의미를 나눌 수 있다. 

 

전역 공간에서의 this

메서드로서 호출할 때 내부에서의 this

함수로서 호출할 때 내부에서의 this

콜백 함수 호출 시 내부에서의 this

생성자 함수 내부에서의 this


1. 전역 공간에서의 this

전역 공간에서 this는 전역 객체를 가리킴. (this 콘솔에 찍으면 전역객체 나온다는 뜻)

console.log(this)            // {alert: f(), atob: f(), blur: f(), ...}
console.log(window)          // {alert: f(), atob: f(), blur: f(), ...}
console.log(this === window) // true 

→ this를 콘솔에 찍으나, window를 콘솔에 찍으나 똑같음.

 

특이점은 우리가 var a = 1; 라고 변수 선언하고 1 할당하면, 자바스크립트 엔진은 전역 객체 안의 프로퍼티로 a를 할당함

var a = 1
console.log(a)         // 1
console.log(window.a)  // 1
console.log(this.a)    // 1

→ a를 직접 콘솔에 찍어도 1이 나오는 건 스코프 체인에서 a를 검색하다가 발견한 프로퍼티이기 때문

→ window.a나 this.a가 1인 건 전역 객체 안의 프로퍼티에 a가 있기 때문

 

그러나 ES6 이후 var 대신 let과 const를 쓰면 다르게 나온다.

let a = 1
console.log(a)         // 1
console.log(window.a)  // undefined
console.log(this.a)    // undefined

 


2. 메서드로서 호출할 때 그 메서드 내부에서의 this

일단 함수로서 호출하는 경우와 메서드로서 호출하는 경우를 구분하는 방법 소개

var handleThis = function (x) {
    console.log(this, x)
}
handleThis(1)              // Window {...} 1

var obj = {
    thisMethod: handleThis
}
obj.thisMethod(2)          // { thisMethod: f } 2
obj['thisMethod'](2)       // { thisMethod: f } 2

→ 호출한 익명함수는 똑같은데, 변수에 담아서 호출한 경우객체의 프로퍼티에 할당해서 호출한 경우에 this가 달라짐.

→ 함수로서 호출하는 경우와 메서드로서 호출하는 경우를 구분하는 방법은 함수 앞에 점이 있느냐 없느냐를 확인하는 것. 점 앞에 객체가 명시돼 있기 때문

 

메서드 내부에서의 this는 어떤 값일까?

var obj = {
  methodA: function () {
    console.log(this)
  },
  inner: {
    methodB: function () {
      console.log(this)
    }
  }
}

obj.methodA()             // { methodA: f, inner: {...} }       === obj
obj['methodA']()          // { methodA: f, inner: {...} }       === obj

obj.inner.methodB()       // { methodB: f }               === obj.inner
obj.inner['methodB']()    // { methodB: f }               === obj.inner
obj['inner'].methodB()    // { methodB: f }               === obj.inner
obj['inner']['methodB']() // { methodB: f }               === obj.inner

→ 메서드 내부에서의 this는 호출한 주체에 대한 정보임. 여기서 호출한 주체란 함수명(프로퍼티명) 바로 앞의 객체임. 즉 마지막 점 앞에 명시된 객체를 말함.

 


3. 함수로서 호출할 때 그 함수 내부에서의 this

그냥 함수로서 호출할 때는 this가 지정되지 않음.

var obj1 = {
  outer: function() {
    console.log(this)  // obj1 
    var innerFunc = function () {
      console.log(this)  // Window, obj2
    }
    innerFunc()  // 2번 
    var obj2 = {
      innerMethod: innerFunc
    }
    obj2.innerMethod()  // 3번
  }
}

obj1.outer()  // 1번

→ 1번: obj1.outer() 함수는 메서드로서 호출한 것. 따라서 outer 함수의 this에는 점 앞의 객체인 obj1이 바인딩됨.

→ 2번: innerFunc()는 객체가 아닌 그냥 변수에 담아서 호출했으므로 this에는 아무것도 지정되지 않아 전역 객체가 콘솔에 찍힘.

→ 3번: obj2.innerMethod()는 위에서 선언한 innerFunc() 함수를 obj2라는 객체의 innerMethod라는 프로퍼티에 할당해서 호출했기 때문에 this에는 점 앞의 객체인 obj2가 바인딩됨.

 

함수 내부에서 직전 컨텍스트의 this를 바라보게 하는 방법

var obj1 = {
  outer: function() {
    console.log(this)  // { outer: f }
    var innerFunc = function () {
      console.log(this)  // Window
    }
    innerFunc()
    var self = this;
    var innerFunc2 = function () {
      console.log(self)  // { outer: f }
    }
    innerFunc2()
  }
}

obj1.outer()

→ 단순히 self (어떤 변수명이어도 상관 없으나, 컨벤션으로 self라고 쓰는 듯함) 변수에 this를 저장한 후, 함수 안에서 self를 출력해보면 된다

→ 단순히 상위 스코프의 this를 저장해서 내부함수에서 활용하기 위한 수단임

 

물론 arrow function을 쓰는 지금은 그런 우회는 필요 없다

var obj = {
  outer: function () {
    console.log(this)           // 1. { outer: f }
    var innerFunc = () => {
      console.log(this)         // 2. { outer: f }
    }
    innerFunc()
  }
}
obj.outer()

→ 우회할 필요 없이 함수 안에서 this 찍어 보면 바로 현재 컨텍스트의 this가 나옴

 


4. 콜백 함수 호출 시 그 함수 내부에서의 this

콜백 함수는 다음 장에서 자세히 다룰 것.

콜백 함수도 기본적으로 함수이기 때문에, 특별히 this를 지정하지 않으면 전역객체를 바라봄. 지정된 경우와 지정되지 않은 경우를 보겠음

// 1번 예시: setTimeout 함수
setTimeout( function () {
	console.log(this)
}, 300)

// 2번 예시: forEach 메서드
[1, 2, 3, 4, 5].forEach( function (x) {
	console.log(this, x)
})

// 3번 예시: addEventListener
document.body.innerHTML += '<button id="a">클릭</button>'
document.body.querySelector('#a').addEventListener('click', function (e) {
			console.log(this, e)
})

// '<div id="root"></div><button id="a">클릭</button>'

→ 1번에서는 내부 콜백 함수에서 따로 this를 지정하지 않으므로 전역객체 출력됨

→ 2번에서도 마찬가지로 내부 콜백 함수에서 this를 지정하지 않았으므로 전역객체와 배열요소 한개씩 5번 출력됨

→ 3번에서는 지정한 객체가 있으므로 점 앞의 엘리먼트와, 이벤트 객체 출력됨

 


5. 생성자 함수 내부에서의 this

생성자 함수란 어떤 공통된 성질을 지니는 객체들을 생성하는 데 사용하는 함수다.

생성자를 클래스(class), 클래스를 통해 만든 객체를 인스턴스(instance)라고 함 

생성자 함수 내부에서의 this는 새로 만들 구체적인 인스턴스 자신이 됨

var Cat = function (name, age) {
	this.bark = '애용'
	this.name = name
	this.age = age
}

var choco = new Cat('초코', 7)
var nabi = new Cat('나비', 5)
console.log(choco, nabi)

// Cat { bark: '애용', name: '초코', age: 7 }
// Cat { bark: '애용', name: '나비', age: 5 }

→ 생성자 함수 Cat을 호출하면, 일단 Cat에 대한 prototype 프로퍼티가 있는 객체가 만들어짐. 그리고 미리 준비된 공통 속성을 해당 객체(this)에 부여함.

→ 그래서 this가 각각 choco 인스턴스, nabi 인스턴스를 가리키는 것.

728x90
Comments