서론
다른 객체지향 언어들과는 달리, 자바스크립트에서 this는 클래스 외에도 다양한 상황에서 사용된다. 이는 때로는 혼란을 야기할 수 있다. this는 기본적으로 실행 컨텍스트가 만들어질 때 결정되며, 함수 호출 시에 결정된다. 실행 중에는 이를 할당으로 변경할 수 없다.
다양한 상황에서의 this
전역 문맥
전역 공간에서 this는 전역 객체를 가르킨다. 예를 들어, 브라우저에서는 window, Node.js에서는 global이 전역 객체가 된다. 전역 공간에서 변수 a에 1을 할당하면 window.a와 this.a 모두 1이 출력된다. 이는 전역 변수가 사실 전역 객체의 프로퍼티로 동작하기 때문이다.
var a = 1;
console.log(a); // 1
console.log(window.a); // 1
console.log(this.a); // 1
전역 공간에서 변수를 선언하고 값을 할당하면, 자바스크립트는 이 변수를 실제로는 특정 객체의 프로퍼티로 처리한다. 예를 들어, var a = 1;과 같은 선언은 전역 객체의 프로퍼티로 인식되어, window.a 또는 this.a로 접근할 수 있게 된다. 이는 자바스크립트가 변수를 실행 컨텍스트의 LexicalEnvironment (L.E)의 프로퍼티로 저장하기 때문이다. 변수를 호출하면 L.E를 확인하여 일치하는 프로퍼티 값을 반환한다. 전역 컨텍스트에서 L.E는 전역 객체를 참조하므로, 전역 변수는 전역 객체의 프로퍼티로 동작한다.
함수 vs 메서드
메서드 내부와 함수 내부에서 각각 this가 어떻게 동작하는지 알아보기 전에 함수와 메서드의 차이에 대해 알아보자. 프로그래밍 언어에서 함수와 메서드는 정해진 동작을 수행하지만, 둘 사이에는 중요한 차이가 있다. 함수는 그 자체로 독립적으로 기능을 수행하지만 메서드는 특정 객체와 연관되어 그 객체의 동작을 수행한다. 따라서 메서드는 항상 어떤 객체의 일부로 존재하며, 그 객체에 대한 참조를 포함하게 된다. 이 객체 참조가 바로 this이다. 따라서 메서드는 자신이 속한 객체의 상태를 읽거나 변경할 수 있다.
var food = {
name: "계란말이",
getName: function() {
return `${this.name}`;
}
};
console.log(food.getName()); // 계란말이
var food = {
name: "계란말이"
};
function getName(foodObject) {
return `${foodObject.name}`;
}
console.log(getName(food)); // "계란말이"
메서드와 함수를 구분하는 방법은 '.'을 통해 호출되었냐를 보는 것이다. 위의 예제 코드에서 메서드와 함수는 동일한 기능을 수행하지만 메서드는 객체의 속성으로 작동하는 것을 볼 수 있다.
메서드 내부
var food = {
getName: function() {
return this; // food를 가르킴
},
recipe: {
getRecipe: function() {
return this; // recipe를 가르킴
}
};
console.log(food.getName()); // { getName: f, recipe: { getRecipe: f } }
console.log(food.recipe.getRecipe()); // { getRecipe: f }
this는 호출 시에 결정되어 호출 주체에 대한 정보가 담기기에 메서드에서 호출 시 호출 주체는 함수명 앞의 객체이다. 점 표기법의 경우 마지막 점 앞의 객체가 this가 된다. getName 메서드는 호출 주체가 food이기에 this는 food를 가르키고 getRecipe 메서드는 호출 주체가 recipe 객체이기에 this가 recipe를 가르킴을 볼 수 있다.
함수 내부
함수를 함수로써 호출할때는 this가 지정되지 않는다. 실행 컨텍스트를 활성화할 당시에 this 가 지정되지 않은 경우 this는 전역 객체를 바라본다. 따라서 함수에서의 this 는 전역 객체를 가르킨다.
function f1() {
return this;
}
// 브라우저
f1() === window; // true
// Node.js
f1() === global; // true
이는 혼란을 야기하는데 왜냐하면 this라는 이름과 다르게 함수 내부에서의 this는 전역 객체를 참조하기 때문이다. ES6에서는 함수 내부에서 this가 전역객체를 바라보는 문제를 보완하고자, this를 바인딩하지 않는 화살표 함수가 추가되었다. 화살표 함수는 실행 컨텍스트를 생성할 때 ths 바인딩 과정이 없어, 상위 스코프의 this를 그대로 활용할 수 잇다.
콜백 함수 내부
콜백 함수는 다른 함수에 제어권을 넘기는 함수이다. 기본적으로 콜백 함수도 함수이기에 콜백 함수에서의 this는 전역 객체를 가리키지만, 제어권을 받은 함수에서 this가 가리키는 대상을 변경할 수 있습니다.
function callbackFunction() {
console.log(this); // 전역 객체 또는 지정된 대상을 출력
}
function receivingFunction(callback) {
callback();
}
receivingFunction(callbackFunction); // window or global
document.body.querySelector('button').addEventListener('click', callbackFunction); // button
callbackFunction을 직접 호출하면 this는 전역 객체를 가리키지만, addEventListener를 통해 콜백 함수로 사용할 때는 this가 이벤트를 받는 DOM 요소를 가리키는 것을 볼 수 있다.
생성자 함수 내부
생성자 함수는 객체를 생성하는 데 사용되는 함수이다. 객체지향 프로그래밍에서 생성자 함수는 클래스의 인스턴스를 생성하며, new 키워드와 함께 사용된다. 생성자 함수에서 호출될 때, this는 새롭게 생성된 인스턴스를 가리킨다. 이렇게 new와 함께 사용되면, this는 새로 생성된 객체에 바인딩되어 그 객체의 속성이나 메서드를 참조하거나 설정하는 데 사용된다.
function Food(name, price) {
this.name = name;
this.price = price;
}
let pasta = new Food("pasta", 10000);
console.log(pasta); // Food { name: 'pasta', price: 10000}
생성자 함수 내부에서의 this는 pasta 인스턴스를 가르킴을 볼 수 있다.
명시적으로 this를 바인딩
위의 상황들과 다르게 명시적으로 this를 바인딩하는 방법들도 있다.
Call 메서드
call 메서드는 함수를 즉시 실행하면서 첫 번째 인자로 지정된 객체를 this로 바인딩한다. call 메서드를 통해 함수의 this를 전역 객체가 아닌 다른 객체로 설정할 수 있다. call 메서드의 두 번째 인자부터는 호출할 함수의 매개변수로 사용된다.
function Food(price, country) {
console.log(this, price, country);
}
Food(5000, 'america'); // window 5000 america
Food.call({ name: 'sandwich' }, 5000, 'america'); // { name: 'sandwich'} 5000 america
Apply 메서드
apply 메서드는 call 메서드와 유사한 역할을 하며 함수를 호출하고 this를 설정하는 데 사용된다. 두 메서드의 차이는 인자를 전달하는 방식이다. call 메서드는 인자를 순서대로 넘기고, apply 메서드는 두 번째 인자로 배열을 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정한다.
function Food(price, country) {
console.log(this, price, country);
}
Food(5000, 'america'); // window 5000 america
Food.call({ name: 'sandwich' }, 5000, 'america'); // { name: 'sandwich'} 5000 america
Food.apply({ name: 'sandwich' }, [5000, 'america']); // { name: 'sandwich'} 5000 america
Bind 메서드
bind 메서드는 ES5에서 추가된 기능으로, call과 비슷하지만 콜백함수를 즉시 호출하지는 않고 전달받은 this 및 인수들로새로운 함수를 반환하는 메서드이다. 반환받은 함수를 호출할 때 인수를 전달하면 그 인수들은 기존 bind 메서드를 호출할 때 전달했던 인수들의 뒤에 이어서 등록된다. 즉 bind 메서드는 함수에 this를 미리 적용하는 것 과 부분 적용 함수를 구현하는 두 가지 목적을 지닌다. 새 함수의 this는 호출 방식과 상관없이 영구적으로bind()의 첫 번째 매개변수로 고정됩니다.
function Food(price, country) {
console.log(this, price, country);
}
const boundFood = Food.bind({ name: 'sandwich' });
boundFood(5000, 'america'); // { name: 'sandwich' } 5000 america
참고자료
Mozilla. (n.d.). this - JavaScript. MDN Web Docs. Retrieved [2024.01.09], 링크
정재남. (2019). 코어 자바스크립트. 위키북스.
'JS' 카테고리의 다른 글
[JS] Javascript의 프로토타입과 상속: 개념과 동작 이해 (0) | 2024.01.12 |
---|---|
[JS] Javascript에서의 Closures (1) | 2024.01.11 |
[JS] 반복문 대신 고차함수를 쓰는 이유 (0) | 2024.01.07 |
[JS] 클린코드를 위해 반복문과 조건문 줄이기(feat: 들여쓰기) (0) | 2024.01.07 |
API 응답과 빈 값: HTTP 상태 코드 200과 204 그리고 404 간의 선택 (0) | 2024.01.03 |