관련지식
javascript, this, arrow function, bind, apply, call

Java 에서 this는 자기 자신을 뜻하지만 Javascript 에서의 this 는 조금 다릅니다.

Java에서의 this 예)

  1. public class Member {
  2. private String name = "홍길동";
  3. private int age = 37;
  4. /*
  5. 클래스의 멤버변수와 setAge 함수의 파라미터가 동일한 이름인 age를 사용하고 있기 때문에 this를 붙여 클래스의 멤버변수인 age라고 지정해주고 있다.
  6. */
  7. public void setAge(int age) {
  8. this.age = age;
  9. }
  10. }

자바스크립트에서도 this는 자신이 속해있는 객체를 가리키지만, 함수를 호출하는 쪽에서 this를 바꿀수 있다는 특징이 있습니다. call()apply()bind() 는 함수를 호출할때 this 를 바꿀수있도록 하는 함수입니다.

call() 함수

아래와 같은 함수는 이미 많이 만들어보셨을 겁니다.

  1. function test() {
  2. console.log(this);
  3. }
  4. test();

브라우저에서 실행을 해보면 this는 글로벌 객체인 window로 되어있습니다.

이번엔 call 함수를 이용하여 this 객체를 바꾸어 호출 해보겠습니다.

  1. function test() {
  2. console.log(this);
  3. }
  4. test.call("디스");

this 객체가 바뀌었음을 볼수 있습니다.

apply() 함수

apply() 함수도 call() 함수와 동일하게 this 객체를 변경하여 호출할수 있습니다. 그럼 call() 함수와 차이점은 무엇일까요?

  1. 함수.call(thisArg[, arg1[, arg2[, ...]]])
  2. 함수.apply(thisArg, [argsArray])

두 함수의 차이점은 함수의 파라미터를 배열로 넘기느냐 아니냐의 차이입니다. 아래 소스를 보겠습니다.

  1. function test2(...args) {
  2. console.log(this + ' list');
  3. for(var i = 0; i < args.length; i++) {
  4. console.log(args[i]);
  5. }
  6. }
  7. test2.call('집합', 'a', 'b', 'c');

이 소스는 call() 함수를 이용하여 this 객체를 변경하여 호출하고 있습니다. 그런데 test2() 함수는 가변 파라미터를 지원하는 함수로 파라미터의 갯수가 고정이 아닙니다.

그런데 만약 이 함수에 호출되는 파라미터가 변수로 바뀐다면 어떻게 될까요? 아래와 같이 문자열로 되어있는 값을 잘라내어 파라미터로 넘겨야 한다고 할 경우, 자릿수가 고정이 아니므로 call() 함수로는 대응할수가 없습니다.

  1. var str1 = 'a,b,c,d,e'.split(',');
  2. var str2 = '33,66,99'.split(',');

그러나 apply() 를 사용하면 간단하게 해결됩니다.

  1. test2.apply('apply호출', str1);
  2. test2.apply('apply호출', str2);

bind() 함수

bind() 는 call() 과 동일한 구조를 가지고 있습니다.

  1. 함수.bind(thisArg[, arg1[, arg2[, ...]]])

차이점은 call() 은 함수를 즉시 실행시키지만, bind() 는 함수에서 사용할 this와 인자값을 설정할 뿐 함수를 즉시 실행시키지 않습니다. 따라서 함수가 실행되는 시점과 this, 인자를 지정할 시점이 다른 경우에 사용할 수 있습니다.

아래 소스는 setTimeout() 함수를 이용한 예제입니다. 한번쯤은 사용해 보셨을 겁니다. 1초 후에 함수가 실행되지만 this 객체는 window 이기 때문에 false가 출력됩니다.

  1. setTimeout(function() {
  2. console.log(this == '변경'); //false 출력
  3. }, 1000);

위 소스를 아래처럼 변경할수 있습니다. 마찬가지로 1초후에 실행되지만 this 객체를 ‘변경’으로 변경하게 했기 때문에 true가 출력됩니다.

  1. function fn() {
  2. console.log(this == '변경'); //true 출력
  3. }
  4. setTimeout(fn.bind('변경'), 1000);

=> 화살표 함수(Arrow Function)

항상 익명함수로 사용하는 화살표 함수는 일반 함수와 다른 몇가지 특징이 있는데, 그중 한가지는 this를 바인딩하지 않는다는 것입니다. 따라서 위에서 살펴본 call()applay()bind() 로 this를 변경하려고 해도 화살표 함수에는 적용되지 않습니다. 그렇기 때문에 Vue.js 에서도 화살표 함수를 사용하지 말라고 가이드하고 있고, JQuery에서도 콜백함수에 화살표 함수를 사용하면 안됩니다.

아래 소스는 콜백함수를 일반 함수와 화살표 함수로 사용하고 있습니다. 결과를 비교해보시면 쉽게 이해 되실 겁니다.

  1. function test3(callback) {
  2. callback.call('콜백');
  3. }
  4. test3(function() {
  5. console.log('test3', this);
  6. });
  7. test3(() => {
  8. console.log('test3', this);
  9. });

참고
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Functions/%EC%95%A0%EB%A1%9C%EC%9A%B0_%ED%8E%91%EC%85%98

최종 샘플

  1. function test() {
  2. console.log(this);
  3. }
  4. test();
  5. function test2(...args) {
  6. console.log(this + ' list');
  7. for(var i = 0; i < args.length; i++) {
  8. console.log(args[i]);
  9. }
  10. }
  11. test2.call('집합', 'a', 'b', 'c');
  12. var str1 = 'a,b,c,d,e'.split(',');
  13. var str2 = '33,66,99'.split(',');
  14. test2.apply('apply호출', str1);
  15. test2.apply('apply호출', str2);
  16. setTimeout(function() {
  17. console.log(this == '변경');
  18. }, 1000);
  19. function fn() {
  20. console.log(this == '변경');
  21. }
  22. setTimeout(fn.bind('변경'), 1000);
  23. function test3(callback) {
  24. callback.call('콜백');
  25. }
  26. test3(function() {
  27. console.log('test3', this);
  28. });
  29. test3(() => {
  30. console.log('test3', this);
  31. });