[바코 Prep TIL] this에 대해 알아보자

Jessie H·2022년 6월 11일
0

바닐라코딩 프렙

목록 보기
3/6
post-thumbnail

자바스크립트 'this'

말 그대로 이것!!

우리가 글을 읽거나 말을 할때 쓰는 그거 이거는 문맥으로 파악해야 제대로 이해하고 사용할 수 있다. 자바스크립트의 this도 마찬가지다.
this는 부른 놈 누구???!!!를 살펴보면 된다. 부른 놈은 당연히 실행된 함수를 봐야 알 수 있다.

따라서, this를 알기 위해서는 반드시 함수 실행문을 봐야한다. 그래야 누가 불렀는지 잘 알 수 있기 때문이다.

this를 구분할 수 있는 함수 실행 조건은 다음과 같다.

1. Regular function call 일반 함수 실행 => Global Object{}

'strict mode'일 때는 undefined

<script>
const name = 'Juliana';

function printName() {
  console.log(this.name);
}

printName();
/**
*일반 함수 실행
*this는 global Object
*window.name = 'Juliana' 이므로 출력값은 Juliana
*/
</script>


2. Dot Notation(Object Method Call)

.앞에 오는 객체가 this가 된다.

예시1 냉장고에는 무슨 음식이 있을까?

<script>
const fridge = {
  inneritem: "cheesecake",
  food: function () {
    console.log(this.inneritem);
  },
};

fridge.food();
/**
* Dot Notation 실행
* this =fridge
* fridge.food() = function(){console.log(fridge.inneritem)};
* 출력값은 cheesecake
*/
</script>
<script>
//위의 내용 동일
// vs 일반 함수 호출

const doWeHaveSth = fridge.food;
doWeHaveSth();//일반 함수 실행 -> this는 {} 따라서 undefined
</script>

예시2-1 소희랑 민우는 롤코를 탈 수 있을까?

롤러코스터 규정상 키가 140이 넘어야 탈 수 있다(verifyRide 참고)

<script>
const height = 180;

function verifyRide() {
  return this.height > 140;
}

const sohee = {
  name: "sohee",
  height: 140,
  verifyRide: verifyRide,
};

const minwoo = {
  name: "minwoo",
  height: 150,
  verifyRide: verifyRide,
};

const rollercoaster = {
  canRide: (rider) => {
    if (!rider.verifyRide) {
      return console.log("Can't ride!");
    }
    return console.log("Go Ride!");
  },
};

rollercoaster.canRide(sohee);
rollercoaster.canRide(minwoo);
</script>

놀랍게도 여기서는 소희와 민우 보다 Go Ride가 된다.
소희는 140이 안넘는데도 탈 수 있다고 한다....(위험해!!)


예시 2-2 소희랑 민우는 롤코를 탈 수 있을까?

<script>
const height = 180;

function verifyRide() {
  return this.height > 140;
}

const sohee = {
  name: "sohee",
  height: 140,
  verifyRide: verifyRide,
};

const minwoo = {
  name: "minwoo",
  height: 150,
  verifyRide: verifyRide,
};

const rollercoaster = {
  rider: (rider) => {
    if (!rider.verifyRide()) {
      return console.log("Can't Ride!");
    }
    return console.log("Go Ride!");
  },
};

rollercoaster.rider(sohee);
rollercoaster.rider(minwoo);
</script>

이 코드에선 소희는 탈 수 없고 민우만 탈 수 있다.

소희는 왜 두 코드에서 다른 결과가 나온 걸까??


예시 2-1

소희 객체가 가지고 있는 verifyRide는 verifyRide 일반함수를 가리킨다.

verifyRide 함수는 this가 global scope를 가리키고, global scope의 height는 180으로 verifyRide 함수이 반환값이 true가 된다. 따라서 Go Ride!가 출력된다.

<script>
const height = 180;

function verifyRide() {
  return this.height > 140;
}

const sohee = {
  name: "sohee",
  height: 140,
  verifyRide: verifyRide,
};

const rollercoaster = {
  canRide: (rider) => { // rider = {name: "sohee", height: 140, verify: function}
    if (!rider.verifyRide) {
    //sohee.verifyRide = function verifyRide, 즉 일반함수 실행 
    //function verifyRide 내의 this는 global scope가 되고 this.height = 180이 된다
    // 180 > 140 이고 반환값이 true가 되어 if 조건을 만족하지 않는다.
      return console.log("Can't Ride!");
    }
    return console.log("Go Ride!");//반환값
  },
};

rollercoaster.rider(sohee);//Go Ride!

</script>

예시 2-2

소희 객체의 verifyRide는 여전히 function verifyRide를 가리킨다.
하지만 이번엔 rollercoaster의 조건이 sohee.verifyRide(), 즉 소희 객체 내의 메소드가 아니라, dot notation 실행값이 false일 때를 말한다.
즉, sohee.verifyRide()라는 dot nation 함수를 호출하기 때문에 return 값이 sohee.height > 140으로 되고, false이기 때문에 "Can't Ride!"가 출력된다.

<script>
const height = 180;

function verifyRide() {
  return this.height > 140;
}

const sohee = {
  name: "sohee",
  height: 140,
  verifyRide: verifyRide,
};

const rollercoaster = {
  rider: (rider) => {
    if (!rider.verifyRide()) {
    //sohee.verifyRide() = return 140>140 false 반환
      return console.log("Can't Ride!");//조건 충족
    }
    return console.log("Go Ride!");
  },
};

rollercoaster.rider(sohee);//Can't Ride!

</script>

한 끗 차이로 이렇게 결과가 달라질 수 있으니 this를 쓸 때는 항상 유의해야한다.



3. call, apply, bind

이거 3개로 실행되는 함수는 괄호 안에 있는 것이 this로 지정된다.

위 3개는 this 바인딩 함수라고 한다.
자바스크립트는 실행하는 함수에 따라 this가 바뀔 수 있는데 this를 고정시키고 싶을 때 쓸 수 있는 것이 바로 this 바인딩 함수이다.

call

첫번째 파라미터로 오는 것을 this로 지정하여 함수를 실행할 수 있다.

<script>
const letsCook = {
   menu: function(){
     return this.howCooked + "" + this.ingredients
   }
}

const recipe1 = {
	howCooked: "fried",
    ingredients: "eggs"
}

const recipe2 = {
     howcooked : "grilled",
     ingredients: "meat"
}


letsCook.menu.call(recipe1);//this = recipe1, 출력값은 fried eggs
letsCook.menu.call(recipe2);//this = recipe2, 출력값은 grilled meat
</script>

두번째부터 오는 파라미터는 실행하는 함수의 파라미터로 전달된다.
(두번째 파라미터부터는 갯수 상관없이 넣을 수 있다)

<script>
function multiply(a,b,c){

}

const myFruit = {
   name: 'Apple';
}

multiplay(myFruit, 1, 2, 3);
//Apple
//6
</script>

apply

파라미터는 총 2개를 받는다.
첫번째 파라미터는 call 처럼 this로 지정된다.
두번째 파라미터는 반드시 배열이어야 한다.
두번째 파라미터의 요소들은 실행하는 함수의 파라미터로 전달된다.

<script>
function multiply(a, b, c) {
  console.log(this.name);
  console.log(a * b * c);
}

const myFruit = {
  name: "Apple",
};


multiply.apply(myFruit, [1, 2, 3]);
//Apple
//6

검색을 하다보니 apply는 prototype과 같이 쓸 일이 많은 것 같다.
나중에 prototype을 배우고 나면 함께 활용하는 법도 같이 정리해야 할 것 같다.


bind

괄호 안에 있는 객체를 this로 지정하여 새로운 함수를 반환한다.
반환만 되고 실행이 되지 않는다는 점에서 call, apply와 다르다.

<script>
function leftIceCream() {
  return this.name + ": " + this.num + "개 남았습니다.";
}

const icecream1 = {
  name: "수박바",
  num: 3,
};

const icecream2 = {
  name: "더위사냥",
  num: 5,
};

const countIceCream = leftIceCream.bind(icecream1);
//icecream1이라는 객체에 leftIceCream이라는 함수 메서드를 대입해 icecream1.leftIceCream을 만든 것과 같은 효과
//물론 새로운 함수를 만들어 반환하기 때문에 실제로 icecream1에 leftIceCream 메소드가 들어가지 않는다.
//따라서 countIceCream = 새로운 만들어낸 icecream1.leftIceCream을 넣은 효과가 됨

console.log(countIceCream());
//countIceCream을 실행한다는 것은 새로 만들어낸 icecream1.leftIceCream()하는 것과 같으므로
//dot notation 실행 경우에 해당하므로 this는 icecream1이 된다.
//따라서 icecream1.name + ": " + icecream1.num + "개 남았습니다" 가 출력된다.
//출력값은 수박바: 3개 남았습니다.

</script>

bind는 this를 지정하고 새로운 함수를 반환만 하고 실행만 하지 않는다.
이는 call과 비교해보면 확실히 알 수 있다.
위의 예제를 bind와 call을 사용하여 다시 비교해보자.

<script>
//bind 사용 시
console.log(leftIceCream.bind(icecream1));

//반환값은 ƒ leftIceCream() {
//  return this.name + ": " + this.num + "개 남았습니다.";
//} 즉, 함수가 반환되는 것이지 실행이 되지는 않는다.

console.log(leftIceCream.bind(icecream1)());
//수박바: 3개 남았습니다.
//반드시 실행은 따로 해주어야 실행이 된다.

</script>
<script>
//call 사용 시
console.log(leftIceCream.call(icecream1))
//수박바: 3개 남았습니다.

</script>


4. 'new' 사용

new와 함께 함수를 사용할 경우 이 함수를 생성자 함수라고 한다.
새로운 빈 객체를 반환하기 때문이다.

이 new로 만들어진 객체의 this는 새로운 객체를 생성했을 때 대입했던 것이 된다.
말로 적으니 정말 설명하기 어려운 것 같다...

<script>
function IceCream(name, price) {
  this.name = name;
  this.price = price;
}

const 메로나 = new IceCream("메로나", 500);//새로운 객체 생성

console.log(IceCream("설레임", 1000));//일반 함수이므로 this.name, this.price 모두 undefined
console.log(메로나);//IceCream {name: "메로나", price: 500}
</script>


출처: 바닐라코딩 프렙코스 강의 내용 중 일부(only 개념)

https://nykim.work/71

https://kamang-it.tistory.com/entry/JavaScript07this-this%EB%B0%94%EC%9D%B8%EB%93%9C%ED%8E%B8bindcallapply

https://velog.io/@devmin/TIL-%ED%95%A8%EC%88%98-%EB%A9%94%EC%86%8C%EB%93%9C-5ok29tthyz

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-Call-Bind-Apply





풀어보면 좋은 'this' 문제

https://dmitripavlutin.com/javascript-this-interview-questions/

https://dev.to/liaowow/take-this-quiz-understand-how-this-works-in-javascript-44dj

profile
코딩 공부 기록장

0개의 댓글