js, call과 apply api

call과 apply

  • 모든 함수는 call과 apply 메서드를 가지고 있다.
  • call과 apply는 함수에 인자를 넣는 것과 거의 동일하게 동작한다.
  • 다만 call은 인자 각각을 넣고 apply는 배열을 넣는다. fn(...arg)fn(arr) 와 유사하다.
  • 다만, call과 apply의 첫 번째 인자는, 해당 함수의 컨텍스트로 사용할 객체를 넣는다. 함수 내부에 this 를 사용할 경우 값을 삽입하며, 사용하지 않으면 null을 삽입한다.
<script>const log = console.log;</script>
<script>
    // 보통의 함수
    function add(x, y){
        return x + y;
    }
    log(add(3,4)); // 7
    log(add.call(null, 3,4)); // 7
    log(add.apply(null, [3,4])); // 7

    // 고차함수
    function fn_and_multi(fn){
        return function(x,y){
            return function(m){
                return fn(x,y) * m;
            }
        }
    }

    let m = fn_and_multi(add);
    let m2 = m.call(null, 2, 3); // 2 + 3 = 5
    let m3 = m.call(null, 2, 3)(4); // 5 * 4 = 20
    log(m3); // 20

</script>

2. call 과 this
- call을 사용하는 이유는 this를 명시하기 위해서이다.

<script>
    function sayHi(){
        return this.name;
    }

    let kim = {name:"kim"};
    let choi = {name:"choi"};

    log(sayHi.call(kim)); // kim
    log(sayHi.call(choi)); // choi
</script>

this의 분실

  • call과 apply를 사용하는 중요한 이유는 this가 무엇인지 정의되지 않는 상황을 대응하기 위해서이다.
  • 아래 예제를 보자.
  • user 객체는 createUserName(name)을 사용하여 회원의 아이디를 생성한다. createUserName 메서드는 user 객체의 registerDate() 메서드를 참조한다. 이때 this.registerDate() 형태를 사용한다. 이때 this는 “Object”로서 user를 가리킨다.
  • 데코레이터 패턴을 적용하기 위하여 createUserNameUpper(fn)을 정의할 때, 이때 fn는 더 이상 user의 메서드가 아니다. “Window”를 가리킨다.
  • 이때 this를 명시하기 위하여 apply 와 call을 사용한다.
3. this가 정의되지 않았을 때 명시한다.

<script>
    let user = {
        registerDate(){
            return new Date().getTime();
        },
        createUserName(name){
            log(this);
            return `${name}-${this.registerDate()}`
        }
    }

    // log(this) : object
    log(user.createUserName("kim")); // kim-{number}

    // decorator 적용
    function createUserNameUpper(fn){
        return function(name){
            const upper = name.toUpperCase();
            return fn(upper);
        }
    }

    user.createUserName = createUserNameUpper(user.createUserName);
    // log(this) : window
    // log(user.createUserName('kim')); // Uncaught TypeError: this.registerDate is not a function

    function createUserNameUpper(fn){
        return function(name){
            const upper = name.toUpperCase();
            return fn.call(this, upper); // this를 명시한다.
        }
    }

    user.createUserName = createUserNameUpper(user.createUserName);
    log(user.createUserName('kim')); // KIM-{number}
</script>

참고 : https://ko.javascript.info/call-apply-decorators