🪴 개요
this 하면 떠오르는 언어인 Java의 this는 객체인 자기 자신을 가리키는 참조 변수입니다. 주로 매개변수와 자신(객체)이 가지고 있는 변수명이 같을 때, 둘을 구분하기 위해 'this.변수명'으로 사용됩니다.
그러나 자바스크립트의 this는 Java와 같은 언어에서의 쓰임과 다른 점이 있습니다.
자바스크립트의 this는 함수를 호출한 방식에 따라 this에 바인딩 될 객체가 동적으로 결정됩니다.(함수를 선언할 때 결정되지 않음 = 정적으로 결정되지 않음)
따라서 this는 함수를 '호출한 객체'를 가리킵니다.
바인딩(binding)이란 프로그램을 구성하는 어떤 요소의 실제 값 또는 프로퍼티를 결정짓는 행위입니다.
this의 경우, 특정 객체나 값을 가리키도록 하는 행위인 것입니다.
- cjkangme.log
함수를 호출하는 방식은 다음과 같습니다.
- 일반 함수 호출
- 메서드(객체 내부 함수) 호출
- 생성자 함수 호출
- apply/call/bind 호출
- 화살표 함수 호출
예시와 함께 하나씩 살펴보겠습니다.
🪴 일반 함수 호출
일반 함수를 호출하는 경우, this는 전역 객체에 바인딩됩니다.
전역 객체는 일반적으로 다음과 같은 객체를 의미합니다.
- 브라우저 콘솔에서는 window 객체
- 터미널(Node.js)에서는 global 객체
예를 들어, 브라우저의 콘솔에서 다음과 같은 코드를 실행해보면
function example () {
console.log(this);
}
example();
전역 객체인 window 객체가 출력되는 것을 볼 수 있습니다.
🪴 메서드 호출
객체의 프로퍼티로 존재하는 함수인 메서드를 호출하는 경우, this는 해당 메서드를 호출한 = 해당 메서드가 들어있는 객체에 바인딩됩니다.
예를 들어, 브라우저의 콘솔에서 다음과 같은 코드를 실행해보면
const obj1 = {
name: "Emma",
sayHi: function() {
console.log(`Hi, I'm ${this.name}`);
},
};
const obj2 = {
name: "Harry",
sayHi: function() {
console.log(`Hi, I'm ${this.name}`);
},
};
obj1.sayHi();
obj2.sayHi();
this는 메서드를 호출한 객체를 가리키고, 각각의 객체가 가지고 있는 name 프로퍼티를 출력하는 것을 볼 수 있습니다.
그러나 아래와 같이 함수 프로퍼티를 다른 변수에 할당해준 뒤 브라우저의 콘솔에서 해당 함수를 실행하면
const obj1 = {
name: "Emma",
sayHi: function() {
console.log(`Hi, I'm ${this}`); // this.name이 아닌 this를 출력하도록 변경
},
};
const func = obj1.sayHi; // func에 obj1의 sayHi() 함수가 할당됨
/*
위의 코드와 같은 의미
const func = function() {
console.log(`Hi, I'm ${this}`);
}
*/
func();
func 함수가 변수에 할당되어 일반 함수를 호출한 것과 같아지고, func 함수는 더 이상 메서드가 아니기 때문에
전역 객체인 window 객체가 출력되는 것을 볼 수 있습니다.
🪴 생성자 함수 호출
new 연산자와 함께 생성자 함수를 호출하는 경우, 생성자 함수 내부에 있는 this는 생성자 함수를 통해 만들어진 인스턴스(자기 자신)에 바인딩됩니다.
생성자 함수가 무엇인지 모르겠다면 다음 링크를 참고해주세요.
예를 들어, 브라우저의 콘솔에서 다음과 같은 코드를 실행해보면
function Person(name) {
this.name = name;
}
const person = new Person('Emma');
console.log(`Hi, I'm ${person.name}`);
this는 new 연산자로 생성된 인스턴스 객체(자신)를 가리키고, 해당 객체가 가지고 있는 name 프로퍼티를 출력하는 것을 볼 수 있습니다.
그러나 우리가 생성자 함수라고 부르는 함수는 일반 함수와 형식적 차이가 존재하지 않아
new 연산자를 붙여서 호출할 때만 생성자 함수로 동작하고, new 연산자 없이 호출하면 해당 함수는 일반 함수로 동작합니다.
따라서 일반 함수의 this는 전역 객체에 바인딩되기 때문에 window 객체(브라우저에서 실행 시)를 가리키게 됩니다.
예를 들어, 브라우저의 콘솔에서 다음과 같은 코드를 실행해보면
function Person(name) {
// new없이 호출하는 경우, 전역객체에 name 프로퍼티를 추가
this.name = name;
};
// 일반 함수의 this는 전역객체를 가리킨다.
const me = Person('Emma');
console.log(window.name); // Emma
console.log(me.name); // TypeError: ~ undefined
new 연산자를 붙이지 않아 생성자 함수인 Person은 일반 함수로 동작하고, this는 window 객체를 가리키게 됩니다.
따라서 me가 아닌 window 객체에서 name 프로퍼티에 접근했을 때 Emma가 출력되는 것을 볼 수 있습니다.
🪴 apply/call/bind 호출(명시적 바인딩)
apply(), call(), bind() 메서드는 명시적으로 this가 가리키는 객체를 바꿔주는 메서드입니다.
따라서 this는 메서드의 첫 번째 인자로 전달된 객체에 바인딩 됩니다.
참고로 bind 메서드는 첫 번째 인자로 전달된 객체를 this로 설정한 새로운 함수를 반환합니다.
예를 들어, 브라우저의 콘솔에서 다음과 같은 코드를 실행해보면
function person() {
console.log(this);
}
const obj1 = {
name: "Emma",
};
person(); // window or global
person.apply(obj1); // obj1
person.call(obj1); // obj1
const newPerson = person.bind(obj1);
newPerson(); // obj1
apply(), call(), bind() 메서드로 this를 obj1으로 바인딩 시켜준 코드들만 obj1을 출력하는 것을 볼 수 있습니다.
🪴 화살표 함수 호출
화살표 함수는 자신만의 this 바인딩을 가지지 않고, 정의된 위치에서의 상위 스코프의 this를 가리킵니다.
즉, 함수가 생성되기 직전에 유효한 객체에 바인딩 됩니다.
예를 들어, 객체 메서드로 화살표 함수를 사용하면
const obj1 = {
name: "Emma",
sayHi: () => {
console.log(`Hi, I'm ${this}`);
},
};
obj1.sayHi();
자신을 호출한 객체가 아닌 상위 스코프를 출력하는 것을 볼 수 있습니다.
따라서 화살표 함수에서 this를 사용하려면 일반 함수로 작성해야 합니다.
읽어주셔서 감사합니다:)