📚 What is TIL?

호출 메서드

apply() call() bind()는 자바스크립트에서 함수를 호출할 때 사용되는 메서드이다
이 메서드들은 함수의 this 값을 변경하거나 함수에 아규먼트를 전달하는데 사용된다

apply, call, bind 메서드는 JavaScript의 내장 함수인
Function 객체의 프로토타입 객체에 정의된 메서드이다
따라서 모든 함수 객체는 이들 메서드를 상속받아 사용할 수 있다


call apply 메서드


call과 apply 메서드의 첫 번째 아규먼트는
함수 내부에 사용할 this가 바인딩될 객체를 가리키는데
두 메서드의 차이를 간단하게 정리하자면,

🔎 call 메서드에서 두번째 이후로 전달되는 아규먼트들은
 함수를 호출할때 사용할 개별적인 아규먼트로 전달되고,

🔎 apply 메서드에서 두번째로 전달되는 아규먼트는
 함수를 호출할때 사용할 아규먼트들을 하나의 배열형태로 묶여서 전달된다


bind 메서드


bind 메서드는 함수로 부터 전달 받은 this와 아규먼트들을 바탕으로
새로운 함수를 생성하고 call과 apply와는 다르게 즉시호출하지 않는 메서드이다

🔎 여기서 말한 새로운 함수에는 원본 함수를 참조하는 this와 아규먼트가 들어가게 된다

🔎 그리고, 생성된 함수는 호출되기 전까지 실행되지 않고
 생성된 함수를 호출하면, 미리 바인딩한 this와 아규먼트들이 함께 적용되어 실행되게된다


const person = { name: 'DongSup' };

function greeting(message) {
  console.log(`${message} ${this.name}`);
}

// call
greeting.call(person, "What's up,"); // 출력 결과: What's up, DongSup

// apply
greeting.apply(person, ['Bonjour']); // 출력 결과: Bonjour Dongsup

// bind
const boundFunc = greeting.bind(person);
boundFunc(); // 출력 결과: undefined DongSup

name과 ‘DongSup’ 이라는 키와값으로 이루어진 객체를 person이 참조하고있고
message라는 파라미터를 가진 greeting 함수를 호출할 경우
콘솔로 message와 this.name이 출력되는 코드이다

✔️ call과 apply의 경우 즉시 함수를 호출해서 결과값이 반환된다

✔️ bind 메서드의 경우 person 객체를 greeting 함수 내부의 this 값으로 지정해서
 새로운 함수를 생성한 후에, 이것을 boundFunc 변수에 할당해서
 나중에 호출을 해야만 결과값이 반환되는 것을 볼 수 있다

boundFunc은 greeting 함수를 참조하면서 this를 person 객체로 지정하기때문에
this.name은 DongSup이 되지만
boundFunc 함수를 호출할 때 아규먼트를 전달하지 않았기 때문에
greeting 함수 내부에서 참조하는 message 값이 undefined가 되어 버린다
따라서 undefined DongSup 이라고 호출된다

💡 만약 여기서 Hi DongSup으로 출력되게끔 하려면 어떻게 해야할까?

1) 첫번째는 방법은 boundFunc 으로 호출할때 ‘Hi’ 라는 아규먼트를 전달하는 것이다

2) 두번째는 방법은 bind 메서드를 호출할 때 person 객체를 this로 지정하면서
 동시에 Hi 라는 아규먼트를 넘겨줘서 일부 아규먼트를 고정한 새로운 함수를 만드는 것(부분 적용 함수)이다


bind 메서드 활용


function sum(a, b, c) {
  return a + b + c;
}
const partialSum = sum.bind(null, 1, 2);
console.log(partialSum(3)); // 6
console.log(partialSum(4)); // 7

bind 메서드를 통해 this를 null로 지정하고
1, 2로 첫번째와 두번째 아규먼트를 고정한 partialSum 이라는 새로운 함수를 생성했다

콘솔로그로 함수를 호출해보면
호출하면서 전달한 아규먼트를 c의 값으로 받아
a, b, c 각각의 값을 모두 더한 결과가 반환되는 것을 볼 수 있다

🔎 이런식으로 함수의 일부 아규먼트를 미리 지정해놓고,
 필요에 따라 아규먼트를 나중에 전달하는 것은
 함수를 다양하게 재사용할 수 있고 가독성과 유지보수성이 좋아진다는 장점이 있다

⚠️ 하지만 이런식으로 새로운 함수가 계속해서 생성될 경우
 원본 함수와 새로 생성된 함수가 뒤섞여 디버깅이 이려울 수 있다

⚠️ 따라서, 이것을 해결하기 위해선 일반적인 함수와 새로 생성된 함수를 구별할 방법이 필요하다


name 프로퍼티


이를 위해 함수의 이름을 나타내는 name이라는 프로퍼티가 존재한다
bind 메서드를 활용해서 생성된 새로운 함수의 name 프로퍼티를 출력하면
bound func 과 같이 함수의 이름 앞에 bound라는 단어가 붙어서 출력된다

var func = function (a, b, c, d) {
  console.log(this, a, b, c, d);
}
var bindFunc = func.bind({x: 1}, 4, 5)
console.log(func.name); // func
console.log(bindFunc.name); // bound func

🔎 덕분에, 일반함수를 더 쉽게 구별하고
🔎 원본함수의 이름만을 name 프로퍼티로 나타내는 call과 apply메서드 보다 코드를 수월하게 추적할 수 있다



함수 내부에서 this를 명시적으로 지정하는 방법

var obj = {
  print: function () {
    console.log(this);
  },
  delayPrint1: function () {
    setTimeout(this.print, 500);
  },
  delayPrint2: function () {
    setTimeout(this.print.bind(this), 1000);
  }
};

obj.delayPrint1();  // Window { ... }
obj.delayPrint2();  // obj { logThis: ƒ logThis(), ... }

obj 라는 객체안에 this를 출력하는 print 메서드가 존재한다
delayPrint1 메서드에서 setTimeout 함수 내부에서의 this는 전역 객체를 가리킨다

✔️ delayPrint2 메서드에서는 bind 메서드를 통해 this를 obj로 지정했기 때문에
 setTimeout() 함수가 실행될 때 새로운 함수가 호출되면서
 this가 obj를 참조하게 되기 때문에 콘솔에 obj 객체가 출력되는것을 볼 수 있다

⚠️ 여기서 주의할 점은 setTimeout() 함수의 콜백 함수는
원래의 print함수가 아닌, this가 obj를 참조하도록 바인딩한 새로운 함수라는 점이다

⚠️ 따라서 setTimeout 함수의 콜백 함수가 새로운 콜백 함수로 반환된다고 할 수 있습니다.



메서드의 내부함수에서 this를 명시적으로 지정하는 방법

self와 같은 변수명를 통해 this가 다른 객체를 가리키는 문제를 방지할 수 있는데
self 가 아닌 call, apply, bind 메서드를 통해서도 방지가 가능하다

// self
var obj = {
    outer: function() {
      var self = this;
      console.log(self);
      var innerFunc = function () {
          console.log(self);
      };
      innerFunc();
    }
};
obj.outer();

// call
var obj = {
    outer: function() {
        console.log(this);
          var innerFunc = function () {
            console.log(this);
        };
          innerFunc.call(this);
    }
};
obj.outer();

// bind
var obj = {
    outer: function() {
        console.log(this);
          var innerFunc = function () {
            console.log(this);
        }.bind(this);
          innerFunc();
    }
};
obj.outer();

outer 함수는 obj 객체의 메서드이고,
outer 함수 내부에서 innerFunc 함수를 호출하여
innerFunc 함수 내부에서 this 값을 출력하는 부분이 있는데

🔎 self 변수를 활용하는 경우
 outer 함수 내부에서의 this 값을 다른 변수에 저장한 이후,
 내부 함수인 innerFunc 에서 해당 변수를 참조해서 메서드 내부에서 원하는 객체를 참조할 수 있다

🔎 call 메서드를 활용하는 경우
 innerFunc 함수를 this와 함께 call 메서드로 호출하면, innerFunc 함수 내부의 this도
 outer 함수가 호출되는 시점에 있는 obj 객체를 참조할 수 있다

🔎 bind 메서드를 활용하는 경우
 innerFunc 함수를 bind 메서드로 호출하면,
 새로운 함수를 생성하고 call과 마찬가지로 obj 객체를 참조 할 수 있다

✔️ 따라서 세 코드 모두 innerFunc 함수 내부의 this가 obj 객체를 가리키기 때문에
 innerFunc() 와같이 일반함수로서 호출했음에도 불구하고
 obj.outer와 같이 obj 객체의 메서드로 호출했을때와 동일한 결과를 보여준다



함수를 호출하는 방식에 따라서 this가 참조하는 대상

💡 함수 호출 시의 this
 함수를 호출할 때, 기본적으로 this는 전역 객체를 참조한다
 하지만 메서드로서 호출할 경우, 메서드를 소유한 객체를 참조한다

💡 메서드 호출 시의 this
 메서드를 호출할 때, this는 메서드를 소유한 객체를 참조한다

💡 생성자 함수 호출 시의 this
 생성자 함수를 호출하여 새로운 객체를 생성할 때, this는 생성되는 객체를 참조한다

💡 apply/call/bind 메서드 호출 시의 this
 apply/call/bind 메서드를 사용하여 함수를 호출할 경우, this를 직접 지정할 수 있다
 apply/call 메서드는 함수를 즉시 호출하는 반면,
 bind 메서드는 함수를 호출하지 않고 this가 바인딩된 새로운 함수를 생성한다

💡 화살표 함수에서의 this
 화살표 함수 내부에서 this를 참조하면, 화살표 함수가 생성될 때의 this가 그대로 유지된다
 따라서 화살표 함수 내부에서 this를 참조할 때,
 this는 전역 객체가 아니라 화살표 함수를 포함한 외부 함수나 메서드의 this를 참조한다

💡 이벤트 핸들러에서의 this
 이벤트 핸들러에서 this는 이벤트가 발생한 DOM 요소를 참조한다

댓글남기기