ABOUT ME

Today
Yesterday
Total
  • call/apply와 데코레이터, 포워딩
    프로그래밍/Js 2021. 7. 20. 11:56
    https://ko.javascript.info/call-apply-decorators 내용을 요약한 것이다

     

    코드 변경없이 캐싱 기능 추가하기

    function slow(x) {
      // CPU 집약적인 작업이 여기에 올 수 있습니다.
      alert(`slow(${x})을/를 호출함`);
      return x;
    }
    
    function cachingDecorator(func) {
      let cache = new Map();
    
      return function(x) {
        if (cache.has(x)) {    // cache에 해당 키가 있으면
          return cache.get(x); // 대응하는 값을 cache에서 읽어옵니다.
        }
    
        let result = func(x);  // 그렇지 않은 경우엔 func를 호출하고,
    
        cache.set(x, result);  // 그 결과를 캐싱(저장)합니다.
        return result;
      };
    }
    
    slow = cachingDecorator(slow);
    
    alert( slow(1) ); // slow(1)이 저장되었습니다.
    alert( "다시 호출: " + slow(1) ); // 동일한 결과
    
    alert( slow(2) ); // slow(2)가 저장되었습니다.
    alert( "다시 호출: " + slow(2) ); // 윗줄과 동일한 결과

    cachingDecorator와 같이 인수로 받은 함수의 행동을 변경시켜주는 함수를 데코레이터라고 한다.

    cachingDecorator 호출할 때 반환되는 것은 캐싱 래퍼이다. 함수에 cachingDecorator를 적용하기만 하면 캐싱이 가능한 함수를 원하는 만큼 구현할 수 있다.

     

    'func.call'를 사용해 컨텍스트 지정하기

    위에서 구현한 캐싱 데코레이터는 객체 메서드에 사용하기엔 적합하지 않다.

    객체 메서드 worker.slow()는 데코레이터 적용 후 제대로 동작하지 않는다.

    // worker.slow에 캐싱 기능을 추가해봅시다.
    let worker = {
      someMethod() {
        return 1;
      },
    
      slow(x) {
        // CPU 집약적인 작업이라 가정
        alert(`slow(${x})을/를 호출함`);
        return x * this.someMethod(); // (*)
      }
    };
    
    // 이전과 동일한 코드
    function cachingDecorator(func) {
      let cache = new Map();
      return function(x) {
        if (cache.has(x)) {
          return cache.get(x);
        }
        let result = func(x); // (**)
        cache.set(x, result);
        return result;
      };
    }
    
    alert( worker.slow(1) ); // 기존 메서드는 잘 동작합니다.
    
    worker.slow = cachingDecorator(worker.slow); // 캐싱 데코레이터 적용
    
    alert( worker.slow(2) ); // 에러 발생!, Error: Cannot read property 'someMethod' of undefined

    (*)로 표시한 줄에서 this.someMethod 접근에 실패했기 때문에, Error: Cannot read property 'someMethod' of undefined 한 에러가 발생한다.

    (**)로 표시한 줄에서 래퍼가 기존 함수 func(x)를 호출하면 this가 undefined 되기 때문이다.

    따라서, this를 명시적으로 고정해줘야 하는데, 특별히 내장함수 메서드 func.call(context, ...args)를 사용할 수 있다.

    // worker.slow에 캐싱 기능을 추가해봅시다.
    let worker = {
      someMethod() {
        return 1;
      },
    
      slow(x) {
        // CPU 집약적인 작업이라 가정
        alert(`slow(${x})을/를 호출함`);
        return x * this.someMethod(); // (*)
      }
    };
    
    // 이전과 동일한 코드
    function cachingDecorator(func) {
      let cache = new Map();
      return function(x) {
        if (cache.has(x)) {
          return cache.get(x);
        }
        let result = func.call(this,x); 
        cache.set(x, result);
        return result;
      };
    }
    
    alert( worker.slow(1) ); // 기존 메서드는 잘 동작합니다.
    
    worker.slow = cachingDecorator(worker.slow); // 캐싱 데코레이터 적용
    
    alert( worker.slow(2) ); //정상 작동

     

    'func.apply' 사용하기

    func.apply 문법은 다음과 같다

    func.apply(context, args)

    apply는 func의 this를 context로 고정해주고, 유사 배열 객체인 args를 인수로 사용할 수 있게 해준다.

    call과 apply의 문법적 차이는 call이 복수 인수를 따로따로 받는 대신, apply는 인수를 유사 배열 객체로 받는다는 점이다.

    • call은 인수로 iterable형태(ex) ...args(전개문법))를 받음
    • apply는 인수로 유사배열(ex args)를 받음

    대부분의 자바스크립트 엔진은 내부에서 apply를 최적화 하기 때문에 apply를 사용하는게 좀 더 빠르긴 하다.

    이렇게 context와 함께 인수 전체를 다른 함수에 전달하는 것을 콜 포워딩(call forwarding)이라고 한다.

     

     

    메서드 빌리기

    다음과 같은 함수가 있다.

    function hash() {
      return arguments.join(); //Error: arguments.join is not a function
    }

    함수를 실행하면 에러가 발생하는데, arguments는 진짜 배열이 아니고 이터러블 객체나 유사배열이기 때문이다.

    하지만 배열에서 메서드를 빌려오면 join을 사용할 수 있다.

    function hash() {
      alert( [].join.call(arguments) ); 
    }

     

     


    *참조

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

    '프로그래밍 > Js' 카테고리의 다른 글

    var, let, const  (0) 2022.03.23
    new Function 문법  (0) 2021.07.05
    객체로서의 함수와 기명 함수 표현식  (0) 2021.07.02
    JS-객체  (0) 2021.05.12
    JS - 추상관계비교  (0) 2021.02.24
Designed by Tistory.