세 메서드는 모두 함수 호출 시점의 this를 직접 지정하기 위해 사용합니다. call과 apply는 함수를 바로 실행한다는 점이 같고, 인자를 넘기는 방식만 다릅니다. call은 fn.call(thisArg, a, b)처럼 나열하고, apply는 fn.apply(thisArg, [a, b])처럼 배열로 넘깁니다.
bind는 fn.bind(thisArg)처럼 새 함수를 만들어 나중에 실행할 수 있게 합니다.
꼬리질문
조금 더 깊게 물어본다면
답변 뒤에 이어질 수 있는 질문들을 하나씩 열어볼 수 있어요.
bind로 만든 함수에 다시 bind를 호출하면 this가 바뀌나요?
이미 bind로 고정된 this는 다시 bind를 호출해도 바뀌지 않습니다. 새로 인자를 더 붙일 수는 있지만, 처음 고정된 this가 유지됩니다.
function sayName() { return this.name;}const bound = sayName.bind({ name: "A" });const rebound = bound.bind({ name: "B" });rebound(); // "A"
이 질문은 bind가 원본 함수를 수정하는지, 아니면 새 함수를 만드는지 확인하려는 질문에 가깝습니다. 답할 때 “첫 번째 bind 결과 함수의 this는 이미 고정된다”고 말하면 됩니다.
bind로 this를 고정했는데 new로 호출하면 어떻게 되나요?
이건 꽤 끈질긴 꼬리질문입니다. bound function을 new로 호출하면, bind로 넘긴 thisArg는 무시되고 새로 생성된 객체가 this가 됩니다. 다만 bind로 미리 넣어둔 인자는 유지됩니다.
function Person(name) { this.name = name;}const BoundPerson = Person.bind({ name: "ignored" }, "Joon");const person = new BoundPerson();console.log(person.name); // "Joon"
그래서 bind는 this를 항상 절대적으로 이긴다고 말하면 틀릴 수 있습니다. constructor 호출에서는 예외가 있습니다.
arrow function에도 call, apply, bind가 먹히나요?
화살표 함수는 자기만의 this를 만들지 않고 바깥 lexical this를 사용합니다. 그래서 call, apply, bind를 써도 일반 함수처럼 this가 바뀌지 않습니다.
여기서 한 번 더 물어보면 “그럼 bind가 아무 효과도 없나요?”라고 할 수 있습니다. this는 바꾸지 못하지만, bind로 인자를 미리 고정한 새 함수를 만들 수는 있습니다. 다만 화살표 함수의 this binding을 바꾸는 용도로는 의미가 없습니다.
apply는 이제 거의 안 써도 되나요?
새 코드에서는 spread syntax로 대체할 수 있는 경우가 많습니다. 다만 오래된 코드나 라이브러리 내부에서 apply가 보일 수 있고, “배열 형태 인자를 함수 호출 인자로 펼친다”는 의도를 읽기 위해 알아두는 편이 좋습니다.
Math.max.apply(null, numbers)는 Math.max(...numbers)로 대체할 수 있는 대표적인 예입니다. 단, 아주 큰 배열을 spread로 펼칠 때도 인자 개수 제한 문제가 생길 수 있습니다.
call/apply의 첫 번째 인자로 null이나 undefined를 넘기면 어떻게 되나요?
strict mode인지에 따라 달라집니다. strict mode가 아닌 일반 함수에서는 null이나 undefined가 전역 객체로 대체될 수 있습니다. strict mode에서는 그대로 null 또는 undefined가 this로 들어갑니다.
function showThis() { "use strict"; return this;}showThis.call(null); // null
이 질문은 this가 함수 선언 위치가 아니라 호출 방식과 strict mode 영향을 받는다는 점을 확인하려는 질문입니다.
세 메서드 모두 원본 함수를 바꾸나요?
아닙니다. call과 apply는 원본 함수를 특정 this로 호출할 뿐이고, bind는 새 함수를 반환합니다. 원본 함수 자체의 동작이나 내부 코드가 바뀌는 것은 아닙니다.
여기서 더 들어오면 “그럼 bind로 만든 함수의 length나 name은 원본과 같나요?”라고 물을 수 있습니다. bound function은 별도의 함수 객체이고, name은 보통 bound ... 형태가 되며, length도 미리 고정한 인자 수에 따라 달라질 수 있습니다.