this
함수를 호출하는 객체를 의미합니다.
this가 존재하는 이유
❌ 함수 재사용 불가
let myDiner = {
name: "김치찌개",
menu: function() {
console.log("오늘 저녁은 " + myDiner.name);
}
}
myDiner.menu(); // "오늘 저녁은 김치찌개"
위와 같은 객체가 있을 때, menu 함수는
myDiner 변수 이름이 수정되거나,
menu 함수 자체를 다른 객체에서 사용하고 싶은 경우에 사용이 불편하게 됩니다.
이는 this를 통해 함수를 재사용할 수 있습니다.
✅ 함수 재사용 가능
function menuGlobal() {
console.log("오늘 저녁은 " + this.name);
}
let myDiner = {
name: "김치찌개",
menu: menuGlobal
}
myDiner.menu(); // "오늘 저녁은 김치찌개"
let yourDiner = {
name: "된장찌개",
menu: menuGlobal
}
yourDiner.menu(); // "오늘 저녁은 된장찌개"
this를 사용하면 함수를 다른 객체에서도 재사용할 수 있습니다.
이제 this 제어해봅시다.
일반적으로 this의 값은 자동으로 할당되지만,
상황에 따라 제어할 수 있어야 합니다.
call()
call 메서드는 this의 값을 바꿀 수도 있고,
함수를 실행할 때 사용할 인수도 전달할 수 있습니다.
코드 예시
function menuGlobal(item) {
console.log("오늘 저녁은 " + item + this.name);
}
let myDiner = {
name: "김치찌개"
}
let yourDiner = {
name: "된장찌개"
}
menuGlobal.call(myDiner, "묵은지"); // "오늘 저녁은 묵은지김치찌개"
menuGlobal.call(yourDiner, "삼겹살"); // "오늘 저녁은 삼겹살된장찌개"
- 첫 번째 인자: 함수에서 this가 바라볼 객체를 작성합니다.
- 두 번째 인자: 함수에 전달할 인자를 작성합니다.
apply()
apply 메서드는 함수를 실행할 때,
인자를 배열로 묶어 한 번에 전달합니다.
코드 예시
function menuGlobal(item1, item2) {
[item, item2].forEach(function(el){
console.log("오늘 저녁은 " + item + this.name);
}, this);
}
let myDiner = {
name: "김치찌개"
}
menuGlobal.apply(myDiner, ["묵은지", "삼겹살"]);
// "오늘 저녁은 묵은지김치찌개"
// "오늘 저녁은 삼겹살김치찌개"
사용 방법은 call() 메서드와 동일합니다.
- 첫 번째 인자: 함수에서 this가 바라볼 객체를 작성합니다.
- 두 번째 인자: 함수에 전달할 인자 값을 배열로 전달합니다.
💡 forEach의 두 번째 인자
forEach에서 this를 전달받으려면, 두 번째 인자값으로 this를 선언해주어야 합니다.
선언하지 않으면, 해당 함수(function(el))를 실행하는 객체를 바라보게 되는데
그 객체는 전역 객체인 window 입니다.
this라고 선언하게 되면, 상위 스코프의 this를 바라보게 됩니다.
call()과 apply()의 차이
call은 함수를 실행할 때 전달할 인수를 하나 하나 전달한다면,
apply는 전달할 인수를 배열로 묶어 한 번에 전달합니다. 그래서 인수를 두 개만 사용합니다.
인수를 배열로 보낸다는 점을 제외하고는 call과 apply는 동일한 기능을 수행합니다.
bind()
bind 메서드는 ES5에서 추가되었습니다.
this값을 어디서 사용하든, 호출 객체가 바뀌지 않게 고정시켜버립니다.
코드 예시
function menuGlobal(item) {
console.log("오늘 저녁은 " + item + this.name);
}
let myDiner = {
name: "김치찌개"
}
let yourDiner = {
name: "된장찌개"
}
let menuGlobalForMe = menuGlobal.bind(myDiner);
let menuGlobalForYou = menuGlobal.bind(yourDiner);
console.log(menuGlobalForMe("묵은지"));
// "오늘 저녁은 묵은지김치찌개"
// undefined
console.log(menuGlobalForYou("삼겹살"));
// "오늘 저녁은 삼겹살된장찌개"
// undefined
- 파라미터: 함수에서 this가 바라볼 객체를 작성합니다.
undefined는 menuGlobal 함수가 return값이 없어서 뜨는 것이므로, 신경쓰지 않아도 됩니다.
만약 아래와 같이 코드를 작성하게 되면,
myDiner.menuMine = menuGlobalForYou;
myDiner는 이런 객체를 갖게 됩니다.
let myDiner = {
name: "김치찌개",
menuMine: menuGlobalForYou
}
그래서 이 함수를 실행하게 되면 어떻게 될까요?
myDiner.menuMine("묵은지"); // "오늘 저녁은 묵은지된장찌개"
이처럼 bind는,
다른 객체에서 실행되는 함수도
그 함수만을 바라보는 객체를 완전히 고정시켜버릴 수 있습니다.
call, apply, bind 비교하기
메서드 | 역할 | 특징 |
call | this를 바꾸고 즉시 실행 | 인자를 하나씩 전달 |
apply | this를 바꾸고 즉시 실행 | 인자를 배열로 전달 |
bind | this를 바꾸고 새 함수 반환 | 나중에 실행 |
즉,
함수.call(누구를_this로_쓸지, 인자1, 인자2...)
함수.apply(누구를_this로_쓸지, [인자1, 인자2])
함수.bind(누구를_this로_쓸지, 인자1, 인자2...) → "새 함수"를 돌려줌
기본 예제
const user = {
name: "다은",
};
function sayHello(age, job) {
console.log(`안녕하세요, 저는 ${this.name}이고, 나이는 ${age}, 직업은 ${job}입니다.`);
}
✅ call
sayHello.call(user, 27, "개발자");
// 출력: 안녕하세요, 저는 다은이고, 나이는 27, 직업은 개발자입니다.
✅ apply
sayHello.apply(user, [27, "개발자"]);
// 출력: 안녕하세요, 저는 다은이고, 나이는 27, 직업은 개발자입니다.
✅ bind
const boundSayHello = sayHello.bind(user, 27, "개발자");
boundSayHello();
// 출력: 안녕하세요, 저는 다은이고, 나이는 27, 직업은 개발자입니다.
그래서 실무에서 언제 쓰나요?
1. 유사 배열을 "진짜 배열"처럼 쓰고 싶을 때 (call)
🌱 상황 설명
우리가 자바스크립트에서 arguments, NodeList, {0: ..., 1: ..., length: ...} 이런 애들은 배열처럼 생겼지만,
실제로는 배열이 아니라 유사 배열입니다.
따라서 map, slice와 같은 메서드를 사용하지 못합니다.
const likeArray = {
0: "a",
1: "b",
2: "c",
length: 3
};
// 유사 배열이 아니므로 아래와 같이 사용하지 못합니다.
likeArray.slice(); // ❌ 에러!
✅ 해결법: 배열의 메서드를 빌려쓰기
const realArray = Array.prototype.slice.call(likeArray); // call 사용
console.log(realArray); // ['a', 'b', 'c']
그래서 slice.call(likeArray)로 작성하여,
this를 likeArray로 바꿔서 slice를 강제로 쓰게 하는 것입니다.
📍 DOM NodeList, arguments 객체 등을 배열처럼 다루고 싶을 때 call 많이 씁니다.
2. 어떤 함수에서 this를 원하는 객체로 바꾸고 싶을 때 (call 또는 apply)
🌱 상황 설명
function sayName() {
console.log(this.name);
}
const user1 = { name: "다은" };
const user2 = { name: "한섭" };
여기서 sayName()을 호출할 때, this를 user1 혹은 user2로 바꾸고 싶다면?
✅ call 사용
sayName.call(user1); // 다은
sayName.call(user2); // 한섭
✅ apply 사용 (인자가 있으면 배열로)
function sayJob(job, age) {
console.log(`${this.name}의 직업은 ${job}, 나이는 ${age}`);
}
sayJob.apply(user1, ["개발자", 28]); // 다은의 직업은 개발자, 나이는 28
→ 재사용 가능한 유틸 함수를 만들고, this를 원하는 객체에 바인딩할 때 사용합니다.
3. 이벤트 핸들러에서 this가 엉킬 때 고정하기 (bind)
bind를 사용하여 this를 고정합니다.
🌱 상황 설명
<button id="btn">클릭</button>
function Counter() {
this.num = 0;
document.getElementById("btn").addEventListener("click", this.increment);
}
Counter.prototype.increment = function () {
this.num++;
console.log(this.num); // ❌ 여기서 this는 버튼 요소!
};
this.increment가 버튼에 붙었으므로, this는 Counter가 아니라 버튼이 됩니다.
❓왜 this가 버튼이 되어요?
이를 이해하려면, 먼저 자바스크립트의 일반 함수 호출 vs 메서드 호출 개념과
이벤트 리스너에서의 this 동작을 알아야 합니다.
element.addEventListener('click', someFunction);
이때, someFunction 안에서의 this는 기본적으로 이벤트를 받은 요소입니다.
즉, DOM 이벤트 핸들러에서 일반 함수로 전달된 콜백은 자동으로 "this = 이벤트 타겟"으로 설정되어요.
🧪 코드 예제로 이해하기
예시: 일반 함수 전달
const btn = document.getElementById("btn");
btn.addEventListener("click", function () {
console.log(this); // 👉 HTMLButtonElement (#btn)
});
이 경우 this는 btn을 가리킵니다.
왜냐하면 브라우저가 "이 이벤트 핸들러는 버튼에서 발생했네?"하고,
this를 자동으로 이벤트 타겟으로 바꾸기 때문이에요.
그래서 이걸 다시 Counter 예시를 다시 보면서 대입해 보면,
function Counter() {
this.num = 0;
document.getElementById("btn").addEventListener("click", this.increment);
}
Counter.prototype.increment = function () {
this.num++;
console.log(this.num);
};
여기서 this.increment는 그냥 함수일 뿐입니다.
addEventListener("click", this.increment)는 결국에는
element.addEventListener("click", function () {
// 내부에서 this = element가 됨
});
처럼 동작하기 때문에,
this.increment 에서 this는 Counter가 아니라 이벤트 타겟 요소인 버튼 요소가 되어버립니다.
✅ 해결 방법: bind로 this 고정
function Counter() {
this.num = 0;
document.getElementById("btn").addEventListener("click", this.increment.bind(this));
}
Counter.prototype.increment = function () {
this.num++;
console.log(this.num);
};
이렇게 하면 this.increment 함수 안에서의 this는 Counter 인스턴스로 고정이 됩니다.
✨ 추가 요약
상황 | this가 가리키는 것 |
obj.method() | obj |
element.addEventListener("click", 함수) | element (이벤트 대상) |
someFunction() 단독 호출 | window (strict mode에서는 undefined) |
함수.bind(어떤객체) | this가 해당 객체로 고정됨 |
📍 addEventListener에 전달하는 콜백은 this가 element로 바뀌기 때문에 bind로 묶어 this를 고정해야 접근이 가능합니다.
4. setTimeout에서 this이 엉켜서 고정할 때 (bind)
setTimeout 안에서 this가 엉킬 때에도 같습니다.
이때는 bind 또는 화살표 함수를 사용하여 문제를 해결할 수 있습니다.
🌱 상황 설명
function Timer() {
this.name = "타이머";
setTimeout(function () {
console.log(this.name); // ❌ undefined
}, 1000);
}
undefined 오류가 발생하는 이유는,
setTimeout 안의 function() 은 this가 window를 가리키기 때문입니다.
✅ 해결법 1: bind(this)
setTimeout(function () {
console.log(this.name);
}.bind(this), 1000); // '타이머'
✅ 해결법 2: 화살표 함수 (this 유지됨)
setTimeout(() => {
console.log(this.name);
}, 1000); // 👉 타이머
화살표 함수에서 this가 유지되는 이유는 다음 섹션에서 깊게 알아봅시다.
📌 상황 별 사용 메서드 쉽게 기억하는 법
상황 | 이거 쓰면 돼요 |
함수 안의 this를 특정 객체로 "지금 당장" 바꾸고 싶어요 | call / apply |
나중에 쓸 함수의 this를 "고정"하고 싶어요 | bind |
배열 아닌 걸 배열처럼 쓰고 싶어요 | Array.prototype.XXX.call() |
화살표 함수와 this
화살표 함수의 this는
일반적으로 this처럼 함수를 호출한 객체를 할당하지 않고,
바로 상위 스코프의 this를 할당합니다.
예시 코드
이전에 작성했던 forEach 코드에서 화살표 함수를 사용하게 되면,
상위 스코프를 this로 할당하게 되므로
두 번째 인자를 작성하지 않아도 됩니다.
function menuGlobal(item1, item2) {
[item, item2].forEach((el) => {
console.log("오늘 저녁은 " + item + this.name);
});
}
✨ Recap
- this는 함수를 호출하는 객체를 의미합니다.
- call과 apply는 this에 할당되는 객체를 지정할 수 있습니다.
- bind는 this에 할당되는 객체가 고정된 새로운 함수를 생성합니다.
- 화살표 함수에서 this는 상위 스코프의 객체를 할당받습니다.
'Front-End: Web > JavaScript' 카테고리의 다른 글
외부 함수가 사라져도 변수가 살아있는 이유: <클로저(Closure)> (0) | 2025.07.05 |
---|---|
JS 스코프(Scope) 제대로 이해하기: 변수는 어디까지 살아있을까? (0) | 2025.07.05 |
String인데 객체처럼? 자바스크립트의 마법: <원시 래퍼 타입> (0) | 2025.07.04 |
crypto로 비밀번호 암호화하기 (0) | 2023.07.25 |
[js] Map이란? (0) | 2023.04.22 |