apply, call, bind 함수 모두 this를 명시적으로 바인딩하는 함수이지만, 함수 호출 시점과 인자 전달 방식에서 차이점이 있다. ## 요약 | 메서드 | 동작 | 인자 전달 방식 | | ----- | ------------------------- | -------------- | | call | this를 바인딩하고 **함수를 즉시 호출** | 개별 전달 (콤마로 나열) | | apply | this를 바인딩하고 함수를 즉시 호출 | 배열로 전달 | | bind | this를 바인딩한 **새로운 함수를 반환** | 개별 전달 (콤마로 나열) | ## call ```javascript func.call(thisArg, arg1, arg2, ...argN) ``` this를 바인딩하고 **그 함수를 즉시 호출**한다. 함수의 인자로 전달할 값들은 this에 바인딩 할 객체 다음에 콤마로 나열해서 개별로 전달한다. ### 활용 - 다른 객체의 메서드를 재활용하는 경우 ## apply ```javascript func.apply(thisArg, [argsArray]) ``` call과 동일하게 this를 바인딩하고 **그 함수를 즉시 호출**한다. 함수의 인자로 전달할 값들은 this에 바인딩 할 객체 다음에 배열로 전달한다. ### 활용 - 배열을 개별 인수로 펼쳐야 하는 경우 (spread 연산자와 비슷..) ```javascript const numbers = [5, 2, 8, 1, 9]; // Math.max는 배열이 아닌 개별 인수를 받으므로 apply 사용 const max = Math.max.apply(null, numbers); ``` ## bind ```javascript const boundFunc = func.bind(thisArg, arg1, arg2, ...argN) ``` bind는 함수에 지정한 this를 바인딩한 **새로운 함수를 반환**한다. 함수의 인자로 전달할 값들은 this에 바인딩 할 객체 다음에 개별로 전달한다. --- bind 시점에 일부 인자를 고정할 수 있다. bind 시점에 전달한 인수와 호출 시점에 전달한 인수는 차례대로 결합되어 원본 함수에 전달된다. ```javascript function introduce(name, age, city, hobby, ...rest) { console.log(`이름: ${name}`); console.log(`나이: ${age}`); console.log(`도시: ${city}`); console.log(`취미: ${hobby}`); console.log(`추가 정보:`, rest); console.log(`this 컨텍스트:`, this.role); } const context = { role: '개발자' }; // bind 시점에 일부 인수 고정 const introduceCheolsoo = introduce.bind(context, '김철수', 25); // 호출 시점에 나머지 인수 전달 introduceCheolsoo('서울', '프로그래밍', '독서', '영화감상', '운동'); /* 출력: 이름: 김철수 (bind에서 고정) 나이: 25 (bind에서 고정) 도시: 서울 (호출 시 전달) 취미: 프로그래밍 (호출 시 전달) 추가 정보: ['독서', '영화감상', '운동'] (나머지 매개변수) this 컨텍스트: 개발자 */ ``` **bind한 함수를 다시 재바인딩하는 경우에 인수는 순서대로 결합되어 전달**된다. ```javascript function combine(a, b, c, d) { console.log(`a: ${a}, b: ${b}, c: ${c}, d: ${d}`); return this.name; } const obj = { name: '바인드객체' }; const boundOnce = combine.bind(obj, '첫번째'); const boundTwice = boundOnce.bind({ name: '무시됨' }, '두번째'); boundTwice('세번째', '네번째'); // 출력: a: 첫번째, b: 두번째, c: 세번째, d: 네번째 // 반환: "바인드객체" (this는 여전히 첫 번째 바인드 객체) ``` --- bind로 바인딩한 this는 **대부분의 경우 변경되지 않는다.** 이는 `bind`가 내부적으로 클로저를 사용하여 `this` 값을 고정하기 때문. ```javascript const obj1 = { name: '객체1' }; const obj2 = { name: '객체2' }; const obj3 = { name: '객체3' }; function getName() { return this.name; } const boundToObj1 = getName.bind(obj1); // 다시 bind를 시도해도 this는 여전히 obj1 const reboundToObj2 = boundToObj1.bind(obj2); console.log(reboundToObj2()); // "객체1" (obj2가 아님!) // call/apply로도 this를 변경할 수 없음 console.log(boundToObj1.call(obj3)); // "객체1" (obj3가 아님!) console.log(boundToObj1.apply(obj3)); // "객체1" (obj3가 아님!) ``` 하지만 **new 연산자를 사용하는 경우에는 덮어씌워진다**. (this 결정에 가장 높은 우선순위를 가지고 있기 때문이다.) ```javascript function Person(name) { this.name = name; } const BoundPerson = Person.bind({name: "Bob"}); const p = new BoundPerson("Alice"); console.log(p.name); // "Alice" ``` --- 함수를 감싸는 래퍼를 만든 것이므로 **실행 순서는 원본 함수와 동일한 흐름을 갖는다.** ```javascript function outerFunction() { console.log('1. 외부 함수 시작'); function innerFunction(message) { console.log('3. 내부 함수 실행:', message); console.log('4. this는:', this.name); } const obj = { name: '바인드객체' }; const boundInner = innerFunction.bind(obj, '바인드된 메시지'); console.log('2. bind 완료, 함수 호출 시작'); boundInner(); console.log('5. 외부 함수 종료'); } outerFunction(); ``` ### 활용 - 이벤트 핸들러 같은 콜백 함수에서 this를 명시적으로 지정할때