[JS] this

Minyoung's Conference·2022년 5월 18일
0

JavaScript

목록 보기
8/28
post-thumbnail

thisthis가 실행되는 함수가 포함되어져 있는 객체를 지칭한다.

그래서 thisbradley로 바뀌어도 전혀 상관없지만 bradley라는 이름은 충분히 바뀔 수 있는 데이터이기 때문에 this로 사용한다.
(또한, 객체 데이터가 또 다른 객체에 할당될 수도 있다.)

const bradley = {
	name: 'minyoung',
  	normal : function () {
    	console.log(this.name)
    },
  	arrow: () => {
    	console.log(this.name)
    }
}
bradley.normal()  // minyoung
bradley.arrow()   // undefined

일반 함수에서는 호출 위치에 따라 this를 정의한다.
화살표 함수는 자신이 선언된 함수 범위에서 this를 정의한다. 호출 위치와 전혀 상관없다.
선언될 때 this가 무엇인지 충분히 알 수 있다.
로직 환경에 따라 다양한 부분을 지칭할 수 있다. 위 로직에서는 name을 지칭하는 부분이 없기에 undefined로 반환된다.
화살표함수는 함수가 만들어지는 위치에서 정의된다는 것을 기억하자.

const rachael = {
	name: 'kayeon',
  	normal: bradley.normal,
  	arrow: bradley.arrow 
}
// rachael.normal() // kayeon
// rachael.arrow() // undefined

위 함수에서 normal, arrow는 중괄호가 따로 없기 떄문에 호출되는 함수가 아니다. rachaelnormalbradley.normal 함수가 할당된다.
rachael.arrow 역시 화살표함수이기에 호출된 위치와 상관없이 똑같이 undefined가 나오게 된다.

const timer = {
  name: 'bradley',
  timeout: function(){
    setTimeout(function () {
    	console.log(this.name)
    }, 2000))
  }
}
timer.timeout() //undefined

setTimeout 함수는 인자를 두 개 받는다. (콜백함수, 시간) 위 코드를 실행해보면,
콘솔창에 undefined가 뜨게 된다.
setTimeout의 콜백함수 내부의 콘솔의 this일반함수로 만들어져 있으며, 일반함수는 호출 위치에서 this가 정의된다. 그래서 thissetTimeout 함수의 어딘가에서 실행이 될 것이며, timer 객체의 name으로서 this가 실행되길 원했다면 화살표 함수로 만들어주어야 한다.

const timer = {
  name: 'bradley',
  timeout: function(){
    setTimeout(() => {
    	console.log(this.name)
    }, 2000)
  }
}
timer.timeout() //bradley

그러면 결과값으로 bradley가 나오게 된다.
화살표함수를 감싸고 있는 timeout 이라는 함수 범위로 this가 정의된다.
또한 timeout이라는 일반함수timer라는 객체 데이터를 가리킨다.
그렇기 때문에, 결국 thistimer가 된다. 그래서 화살표함수를 써서
자신이 선언된 함수 범위에 this를 주로 정의하고 사용한다.

일반함수와 화살표함수를 제대로 이해하고 있어야 자바스크립트 클래스에서
this일반함수로 만들지 화살표함수로 만들지 명확하게 결정할 수 있다.

THIS 추가 내용

(인프런 "코딩인터뷰를 저격하는 JS 스나이퍼 양성학교" 강의 참조)

this가 존재하는 이유

var myDinner = {
	name : "김치찌개",
	menu : function() {
		console.log("오늘 저녁은 " + myDinner.name)
	}
}

myDinner.menu();

위와 같은 객체에서, menu함수는 myDinner변수의 이름이 수정될 경우나 
menu 함수 자체를 다른 객체에서 사용하고 싶을 경우, 사용이 불편하다.
myDinner가 아니라 yourDinner가 된다면, 
console.log 내의 myDinner도 yourDinner로 바뀌어야한다. 
menu의 value 함수만 따로 빼서 쓰더라도 그 객체에서 myDinner를 가지고 있는지 알 수 없다.
그렇기 떄문에 함수를 재사용하기가 매우 힘들어진다.

this를 통해 함수를 재사용해보자. 

어떤 객체에서도 사용할 수 있는 글로벌 함수를 만들어보자.

function menuGlobal() {
  console.log("오늘 저녁은" + this.name);
}

var myDinner = {
  name: "김치찌개",
  menu: menuGlobal
}

myDinner.menu();  // "오늘 저녁은 김치찌개"

myDinner라는 객체 내에서 호출된 menuGlobal이라는 함수가 자신을 호출한 객체인 this.name 을 가져오는 것을 볼 수 있다. 

myDinner가 아닌 다른 객체를 만들어 보자.

var yourDinner = {
  name : "된장찌개",
  menu : menuGlobal
}

yourDinner.menu();  // "오늘 저녁은 된장찌개"

이렇게 다른 객체에서도 재사용 가능한 함수가 되었다. 이게 this의 핵심적인 기능이다. 
"this를 이용하면 함수를 다른 객체에서도 재사용할 수 있다"

이제 this를 제어할 수 있어야 한다.
(일반적으로 this의 값은 자동으로 할당되지만, 상황에 따라 제어할 수 있어야 한다.)

this를 제어하는 함수를 소개한다. 

1. call()
- call 메서드는 this의 값을 바꿀 수도 있고, 함수를 실행할 때 사용할 인수도 전달할 수 있다.

function menuGlobal(item) {
  console.log("오늘 저녁은 " + item + this.name);
}

var myDinner = {
  name: "김치찌개"
}

var yourDinner = {
  name: "된장찌개"
}

// menuGlobal함수에 call 메소드를 사용한다. 
// call의 첫번째 인수로 함수에 this를 바라볼 객체를 넣어준다.(지정)

menuGlobal.call(myDinner, "묵은지"); // 오늘 저녁은 묵은지김치찌개
 
// this는 myDinner를 바라보게 된다.
// 그리고 두번째 인자로 item에 해당하는 인자를 전달한다.


menuGlobal.call(yourDinner, "차돌"); // 오늘 저녁은 차돌된장찌개

// menuGlobal함수가 콜을 한다. yourDinner라는 객체를, item에 차돌을 넣어서.

2. apply()
- apply메서드는 함수를 실행할 때 배열로 묶어 한번에 전달합니다.

function menuGlobal(item1,item2){
  [item1,item2].forEach(function(el){
    console.log("오늘 저녁은" + el + this.name)
  }, this);
}

var myDinner = {
  name: "김치찌개"
}

var yourDinner = {
  name : "된장찌개"
}

menuGlobal.apply(myDinner, [" 묵은지 ", " 우삼겹 "]); //"오늘 저녁은 묵은지 김치찌개", "오늘 저녁은 우삼겹 김치찌개"
menuGlobal.apply(yourDinner, [" 두부 ", " 애호박 "]); //"오늘 저녁은 두부 된장찌개", "오늘 저녁은 애호박 된장찌개"

- call()apply()의 차이
call은 함수를 실행할 때 전달할 인수를 하나 하나 전달한다면 (여러 개의 인수 전달 가능) 
apply는 전달할 인수를 배열로 묶어 한번에 전달한다.
그래서 인수를 두 개만 사용한다. (this를 지정할 객체, 전달할 인수 배열)
인수를 배열로 보낸다는 점 빼고 call과 apply는 동일한 기능을 수행한다.

3. bind()
- es5에서 추가되었다.
- this값을 어디서 사용하든 호출 객체가 바뀌지 않게 고정시켜버린다. 
 
function menuGlobal(item){
    console.log("오늘 저녁은" + item + this.name)
}

var myDinner = {
  name: "김치찌개"
}

var yourDinner = {
  name : "된장찌개"
}

var menuGlobalForMe = menuGlobal.bind(myDinner);
-  menuGlobalForMe 변수에는 menuGlobal 함수가 
	 myDinner를 바라보는 새로운 고정된 함수가 들어가게 된다. 

console.log(menuGlobalForMe("묵은지")); // "오늘 저녁은묵은지김치찌개"

menuGlobal 함수의 this값은 myDinner가 된다.

var menuGlobalForYou = menuGlobal.bind(yourDinner);

console.log(menuGlobalForYou("우삼겹")); 

그렇다면, bind()는 이렇게도 사용이 가능하다. 

myDinner객체에 새로운 키값 menuMine 이라는 키를 넣고 값을 menuGlobalForYou를 넣는다면,

myDinner.menuMine = menuGlobalForYou;

위 코드는 아래와 같다.

var myDinner = {
  name: "김치찌개",
  menuMine: menuGlobalForYou
}

그렇게되면, myDinner.menuMine()을 함수로 실행할 수 있게 된다. 

실행하면, 

console.log(myDinner.menuMine("묵은지")); // "오늘 저녁은 묵은지 된장찌개"

myDinner에서 사용하지만, 바라보는 객체는 yourDinner이다. 


# 화살표 함수와 this

- 화살표 함수의 This는 일반적인 This처럼 함수를 호출한 객체를 할당하지 않고, 
	바로 상위 스코프의 This에 할당하게 된다. 

- apply에서 확인했던 코드를 다시 가져와보겠다.

function menuGlobal(item1,item2){
  [item1,item2].forEach(function (el) {
    console.log("오늘 저녁은" + el + this.name)
  	}, this);
}

여기서, forEach문에서 두 번째 인수 this가 없으면
콘솔 내 this는 자신을 호출하는 함수를 불러오는 객체인 window를 바라보게 된다.
그래서 this를 써주어야 하지만, 화살표 함수를 사용하게 된다면,
두번 째 인수를 비워주어도 된다. 그렇다면 상위 스코프의 this는 무엇일까?
function menuGlobal(item1,item2){
	console.log(this);
 	[item1,item2].forEach((el) => {
    	console.log("오늘 저녁은" + el + this.name)
  	});
}
menuGlobal.apply(myDinner, [" 묵은지 ", " 우삼겹 "]);

// [object Object] {
	  name: "김치찌개"
	}
// "오늘 저녁은 묵은지 김치찌개"
// "오늘 저녁은 우삼겹 김치찌개"

둘 다 김치찌개로 나온다. 상위 스코프의 this는 myDinner를 바라보고 있다.
화살표 함수내의 this는 상위 스코프의 this를 바라본다.

[정리] 
forEach 내부의 this는 전역객체를 바라보고 있다.
화살표 함수를 만들어줌으로써, this는 상위 스코프를 바라보게된다. 

- this는 함수를 호출하는 객체를 의미한다. (객체.a에서 this는 객체)
- call, apply는 this에 할당되는 객체를 지정할 수 있다.
- Bind 메서드는 this에 할당되는 객체가 고정된 새로운 함수를 생성한다.
- 화살표 함수에서의 this는 상위 스코프의 객체를 할당받는다.
- this는 함수가 호출될 때 결정된다.
- 화살표함수는 bind() 메서드 사용 불가.
- 일반함수가 호출한 this는 전역객체, 화살표함수가 호출한 this는 한 단계 위 콘텍스트의 this를 가리킨다.
- 메소드가 일반함수면 this'.'앞의 객체, 화살표 함수면 window.

( call(), apply(), bind() 내용 더 보기 )

profile
안녕하세요, FE 개발자 김민영입니다.

0개의 댓글