[JavaScript] this 파헤치기

김진영·2022년 7월 17일
0

JavaScript

목록 보기
3/8
post-thumbnail
post-custom-banner

📋 this 파헤치기

다른 언어를 사용하다가 JavaScript를 처음 쓰는 사람들이 자주 헷갈려하는 개념중 하나가 바로 this이다.

다른 언어와는 달리 동작하기 때문인데, 일반적으로 thisclass에서만 사용하며, class로 생성한 인스턴스 객체를 의미하나, JavaScript에서는 그렇지 않고 this가 가르키는 대상이 항상 달라진다.

this를 먼저 간략하게 설명하자면, this는 자신이 속한 객체 또는 자신이 생성할 instance를 가리키는 자기 참조 변수(self-reference variable)이다.

또, this는 실행컨텍스트가 생성될 때 결정된다. 실행컨텍스트는 함수를 호출할 때 생성되므로, this는 함수를 호출할 때 결정된다.


📌 1. this의 동작 방식

우선 콘솔창에 this를 입력해보자.

this; // Window {}

전역 객체인 Window를 가리킨다.
(client(브라우저)에서는 Window Node.js에서는 global을 가리킴)

그렇다면 함수 안에서는?

function what() { console.log(this); };
what(); // Window {}

그렇다. 함수 안에서도 Window를 가리킨다.
이번엔 객체의 경우를 살펴보자.

let obj = {
  what: function() { console.log(this); },
};
obj.what(); // {what: ƒ}

let oh = obj.what;
oh(); // Window {}

메소드로 호출했을때, obj 객체를 가리킨다.
하지만 함수(oh)는 단순히 obj.what을 꺼내온 것이기 때문에 메소드라 할 수 없다. 객체가 함수를 호출해야 메소드라고 할 수 있다.

그냥 함수인 oh는 전역객체, Window를 가리킨다.

let obj = { name: 'kim' };
function what() {
  console.log(this);
}
what(); // Window
what.bind(obj).call(); // obj
what.call(obj); // obj
what.apply(obj); // obj

명시적으로 this를 바꾸는 함수 메소드인 bind, call, apply를 사용하면 this가 객체를 가리킨다.
이에 대해선 밑에 더 자세하게 후술하겠다.

이제 생성자 함수를 살펴보자.

function User(name,isAdmin) {
  this.name = name;
  this.isAdmin = isAdmin;
}
let newUser = User("Mike",false);
console.log(window.name); // Mike

여기서 new를 사용하지 않고 생성하면 this는 또 전역 객체를 가리키게 된다.

하지만 new를 사용한다면?

let user = new User("Mike",false);

console.log(user.name); // Mike
console.log(user.isAdmin); // false

this가 정상적으로 새롭게 생성된 instance를 가리키게 된다.

document.body.addEventListener('click',function(){
    console.log(this); // <body>
});

하지만 addEventListener같은 경우는, thisWindow가 아니라 <body>를 가리킨다.
바로 클릭으로 인해 이벤트가 발생할 때, 내부적으로 this가 바뀐 것이다. 내부적으로 바뀐 것이기 때문에 동작을 외울 수밖에 없다고 한다.

이런 경우엔 어떻게 해결해야할까?


📌 2. Arrow Function

위와 같은 문제는 ES6 화살표 함수 (arrow function)을 사용하면 해결할 수 있다.

document.body.addEventListener('click',() => {
    console.log(this); // Window {}
});

화살표 함수를 사용하게 되면, this의 대상이 어떤 객체가 호출했느냐로 결정되지 않는다.
함수 내부에 this는 없으며, scope chain의 가장 가까운 this로 대상을 결정한다.
다른 예시를 한번 살펴보자.

function User(name,isAdmin){
this.name = name;
this.isAdmin = isAdmin;
   
document.body.addEventListener('click',function() {
   console.log(this) // <body>
   console.log(this.name); // undefined
   console.log(this.isAdmin); //undefined
 });
}

const kendrick = User('Kendrick', true);

일반 함수를 사용했을 때, this는 <body>를 가리킨다.
화살표 함수를 사용해보겠다.

function User(name,isAdmin){
this.name = name;
this.isAdmin = isAdmin;
    
document.body.addEventListener('click',() => {
  	console.log(this); // Window {}
    console.log(this.name); // Kendrick
    console.log(this.isAdmin); // true
});
}
const a = User('Kendrick', true);

보이는 바와 같이 scope chain의 가장 가까운 this로 대상을 결정했다.


📌 3. 원하는 대상으로 this binding (명시적 binding)

📄 바인딩 (binding) 이란?

Javascript에서 this가 참조하는 것은 함수가 호출되는 방식에 따라 결정되는데, 이것을 "this binding"이라고 한다.
위에 작성한 화살표함수도 this를 새롭게 binding했다고 할 수 있다.

📄 call, apply

const obj = { name : "Tyson"};

const profile = function(age){
console.log(`Hi, my name is ${this.name} and I'm ${age} years old.`);
}

profile("54"); // Hi, my name is  and I'm 54 years old.
profile.call(obj,24); // Hi, my name is Tyson and I'm 24 years old.
profile.apply(obj,[24]); // Hi, my name is Tyson and I'm 24 years old.

callapply는 함수를 호출하는 함수이다. 그러나 그냥 실행하는 것이 아니라 첫 번째 인자에 this로 setting하고 싶은 객체를 넘겨주어 this를 바꾸고나서 실행한다.

profile("54")this는 전역객체 Window를 가리키고,
두 번째 실행인 profile.call(obj, 24)와 세 번째 실행인 say.apply(obj, "seoul")this를 obj로 대체한다.
그러자 정상적으로 대체된 값이 출력됐다.

callapply의 차이점은 단 하나이다.

.call(this, parameter1, parameter2 ...)
.apply(this, [parameter1], [parameter2] ...);

이렇게 apply는 두번째 인자부터는 배열에 담아야 한다는 점이다.

📄 bind

bind 함수는 함수가 가리키는 this만 바꾸고 실행하지는 않는다.
더 자세히 말하자면 this를 정의하고 나서 그 함수를 복사해 새로운 함수를 만들어 return하는 것이다.

const obj = { name : "Tyson"};

const profile = function(age){
console.log(`Hi, my name is ${this.name} and I'm ${age} years old.`);
}

const profile2 = profile.bind(obj);
profile2(24); // Hi, my name is Tyson and I'm 24 years old.

profile.call(obj,24); // Hi, my name is Tyson and I'm 24 years old. (결괏값이 위와 같다)

이를 보면, .bind(obj)(24).call(obj,24)는 같다는 것을 알 수 있다.

post-custom-banner

1개의 댓글

comment-user-thumbnail
2022년 7월 17일

this🤞

답글 달기