call, apply, bind, 화살표 함수
· 약 5분
코어 자바스크립트 스터디 6주차 내용을 정리했습니다. 이번 주제는 call, apply, bind 메서드와 화살표 함수의 this 바인딩입니다.
📞 call 메서드
call 메서드는 메서드의 호출 주체인 함수를 즉시 실행하도록 하는 명령입니다.
Function.prototype.call(thisArg[, arg1[, arg2[, ...]]])
특징
- call 메서드의 첫 번째 인자를 this로 바인딩
- 이후의 인자들을 호출할 함수의 매개변수로 사용
- 함수를 그냥 실행하면 this는 전역객체를 참조하지만, call 메서드를 이용하면 임의의 객체를 this로 지정 가능
const obj = { name: "철수" };
function introduce(age, hobby) {
console.log(
`안녕하세요, 저는 ${this.name}이고 ${age}살이며 ${hobby}를 좋아합니다.`
);
}
introduce.call(obj, 25, "독서");
// "안녕하세요, 저는 철수이고 25살이며 독서를 좋아합니다."
🍎 apply 메서드
apply 메서드는 call 메서드와 기능적으로 완전히 동일합니다.
Function.prototype.apply(thisArg[, argsArray])
call과의 차이점
두 번째 인자를 배열로 받아 그 배열의 요소들을 호출할 함수의 매개변수로 지정합니다.
const obj = { name: "영희" };
function introduce(age, hobby) {
console.log(
`안녕하세요, 저는 ${this.name}이고 ${age}살이며 ${hobby}를 좋아합니다.`
);
}
// call vs apply
introduce.call(obj, 23, "영화감상"); // 인수를 개별적으로
introduce.apply(obj, [23, "영화감상"]); // 인수를 배열로
🔄 유사배열객체에 배열 메서드 적용
객체에는 배열 메서드를 직접 적용할 수 없습니다. 하지만 유사배열객체의 경우 call/apply 메서드를 이용해 배열 메서드를 차용할 수 있습니다.
유사배열객체의 조건
- 키가 0 또는 양의 정수인 프로퍼티가 존재
- length 프로퍼티의 값이 0 또는 양의 정수인 객체
const obj = {
0: "a",
1: "b",
2: "c",
length: 3,
};
// 배열 메서드를 객체에 적용
Array.prototype.push.call(obj, "d");
console.log(obj); // {0: 'a', 1: 'b', 2: 'c', 3: 'd', length: 4}
// 객체를 배열로 전환
const arr = Array.prototype.slice.call(obj);
console.log(arr); // ['a', 'b', 'c', 'd']
slice 메서드의 특별한 활용
slice 메서드는 매개변수를 아무것도 넘기지 않을 경우 원본 배열의 얕은 복사본을 반환합니다. 이를 이용해 유사배열객체를 배열로 전환할 수 있습니다.
대표적인 유사배열객체들
// 1. arguments 객체
function example() {
const argsArray = Array.prototype.slice.call(arguments);
console.log(argsArray);
}
// 2. NodeList
const nodeList = document.querySelectorAll("div");
const nodeArray = Array.prototype.slice.call(nodeList);
// 3. 문자열 (단, 읽기 전용이므로 변경 메서드는 에러)
const str = "hello";
const strArray = Array.prototype.slice.call(str);
console.log(strArray); // ['h', 'e', 'l', 'l', 'o']
ES6의 개선사항
call/apply를 이용한 형변환은 본래 메서드의 의도와 다소 동떨어진 활용법입니다. ES6에서는 더 명확한 방법을 제공합니다:
// ES6 Array.from 메서드
const obj = { 0: "a", 1: "b", 2: "c", length: 3 };
const arr = Array.from(obj);
console.log(arr); // ['a', 'b', 'c']
📊 apply 메서드의 실용적 활용
최대/최솟값 구하기
const numbers = [10, 20, 30, 40, 50];
// 기존 방식 (apply 사용)
const max = Math.max.apply(null, numbers);
const min = Math.min.apply(null, numbers);
console.log(max); // 50
console.log(min); // 10
// ES6 spread operator (더 간단!)
const maxES6 = Math.max(...numbers);
const minES6 = Math.min(...numbers);
🔗 bind 메서드
bind 메서드는 ES5에서 추가된 기능으로, call과 비슷하지만 즉시 호출하지 않고 새로운 함수를 반환합니다.
Function.prototype.bind(thisArg[, arg1[, arg2[, ...]]])
두 가지 목적
- 함수에 this를 미리 적용
- 부분 적용 함수 구현
const obj = { name: "민수" };
function introduce(age, hobby, location) {
console.log(
`안녕하세요, 저는 ${this.name}이고 ${age}살이며 ${hobby}를 좋아하고 ${location}에 살아요.`
);
}
// this만 바인딩
const boundIntroduce = introduce.bind(obj);
boundIntroduce(28, "게임", "서울");
// this + 일부 매개변수도 바인딩 (부분 적용 함수)
const partialBound = introduce.bind(obj, 28, "게임");
partialBound("서울");
// "안녕하세요, 저는 민수이고 28살이며 게임을 좋아하고 서울에 살아요."
bind의 특별한 성질
console.log(introduce.name); // 'introduce'
console.log(boundIntroduce.name); // 'bound introduce'
name 프로퍼티에 'bound' 접두어가 붙어 코드 추적이 더 수월합니다.
내부함수 this 문제 해결
const obj = {
outer: function () {
console.log(this); // obj
// 문제: 내부함수의 this는 전역객체
function innerFunc() {
console.log(this); // Window (전역객체)
}
// 해결: bind로 this 고정
const boundInner = innerFunc.bind(this);
boundInner(); // obj
},
};
콜백함수에서의 this 제어
const obj = {
logThis: function () {
console.log(this);
},
logThisLater1: function () {
setTimeout(this.logThis, 500); // Window
},
logThisLater2: function () {
setTimeout(this.logThis.bind(this), 1000); // obj
},
};
⚡ 화살표 함수의 특별한 this
ES6에 새롭게 도입된 화살표 함수는 실행 컨텍스트 생성 시 this를 바인딩하는 과정이 제외됩니다.
화살표 함수의 this 특징
- 함수 내부에 this가 아예 없음
- 접근하면 스코프체인상 가장 가까운 this에 접근
- call/apply/bind로 this 변경 불가
const obj = {
outer: function () {
console.log(this); // obj
// 일반 함수: this가 전역객체
const innerFunc1 = function () {
console.log(this); // Window
};
// 화살표 함수: 상위 스코프의 this 사용
const innerFunc2 = () => {
console.log(this); // obj
};
innerFunc1();
innerFunc2();
},
};
실용적 활용
const obj = {
name: "화살표",
friends: ["철수", "영희", "민수"],
// 기존 방식
greetAll1: function () {
this.friends.forEach(function (friend) {
console.log(`${this.name}이 ${friend}에게 인사합니다.`); // undefined
});
},
// 화살표 함수로 해결
greetAll2: function () {
this.friends.forEach((friend) => {
console.log(`${this.name}이 ${friend}에게 인사합니다.`); // 정상 동작
});
},
};
🎯 thisArg를 받는 메서드들
일부 메서드는 추가로 this로 지정할 객체(thisArg)를 인자로 받을 수 있습니다:
const obj = { multiplier: 3 };
const numbers = [1, 2, 3, 4, 5];
// thisArg 없이
numbers.forEach(function (num) {
console.log(num * this.multiplier); // NaN (this는 전역객체)
});
// thisArg 활용
numbers.forEach(function (num) {
console.log(num * this.multiplier); // 3, 6, 9, 12, 15
}, obj); // 두 번째 인자로 obj 전달
thisArg를 받는 배열 메서드들
forEach(callback, thisArg)
map(callback, thisArg)
filter(callback, thisArg)
some(callback, thisArg)
every(callback, thisArg)
find(callback, thisArg)
findIndex(callback, thisArg)
📝 this 바인딩 규칙 총정리
- 전역공간에서의 this → 전역객체 (브라우저: window, Node.js: global)
- 메서드로서 호출한 경우 → 메서드 호출 주체 (메서드명 앞의 객체)
- 함수로서 호출한 경우 → 전역객체 (메서드의 내부함수도 동일)
- 콜백 함수 내부에서의 this → 해당 콜백 함수의 제어권을 넘겨받은 함수가 정의한 바에 따름
- 생성자 함수에서의 this → 생성될 인스턴스
- call, apply 메서드 → this를 명시적으로 지정하면서 함수 호출
- bind 메서드 → this 및 함수에 넘길 인수를 일부 지정해서 새로운 함수를 만듦
- 화살표 함수 → 상위 스코프의 this를 그대로 활용 (바인딩 과정 없음)
🎯 정리
- call/apply는 this를 지정하며 즉시 함수를 실행합니다
- bind는 this를 지정한 새로운 함수를 반환합니다
- 유사배열객체에 call/apply로 배열 메서드를 적용할 수 있습니다
- 화살표 함수는 자신만의 this를 갖지 않아 상위 스코프의 this를 사용합니다
- 일부 배열 메서드는 thisArg 매개변수로 this를 직접 지정할 수 있습니다
이러한 방법들을 활용하면 JavaScript에서 this를 더욱 유연하고 정확하게 제어할 수 있습니다! 🚀
스터디: 코어 자바스크립트 6주차