JS 함수를 투명하게 래핑

래퍼 함수(고차 함수라고도 함)는 다른 함수의 동작을 강화하면서 이러한 문제를 분리하여 유지하는 좋은 방법입니다.

이 패턴은 JS/TS 세계에 널리 퍼져 있으며 저는 다소 경험이 많은 사용자라고 생각합니다. 그러나 언젠가 나는 내가 직접 작성한 래퍼 함수에서 한 가지 세부 사항이 누락되었음을 보여주는 코드 스니펫을 발견했습니다.

코드 조각:

function bindActionCreator<A extends AnyAction = AnyAction>(
  actionCreator: ActionCreator<A>,
  dispatch: Dispatch
) {
  return function (this: any, ...args: any[]) {
    return dispatch(actionCreator.apply(this, args))
  }
}


이 래퍼 함수는 Redux library에 있지만 라이브러리 컨텍스트에서 분석할 수 있습니다.

따라서 bindActionCreator는 2개의 함수를 인자로 받아 본문에 2개의 입력 함수를 합성한 함수를 반환합니다. 이것은 유틸리티 래퍼 함수의 좋은 예입니다. 그런데 내 눈길을 끈 곳이 하나 있다. 솔직히 비슷한 함수를 만들어야 한다면 apply(...) 메서드 호출 없이 actionCreator(args)만 작성할 것입니다. 그럼 코드 스니펫에서 적용 메소드를 통한 함수 호출이 사용된 이유를 알아보겠습니다.

간단한 함수를 생각해 봅시다.

function f(name, email){
   return {name, email, this:this}
}


주목해야 할 한 가지가 있습니다. 함수 f는 함수 호출의 이 값을 유지하는 객체를 반환합니다.

실제로 논리를 추가하지 않고 내부에 함수 f 호출을 포함하는 새 함수를 반환하는 간단한 래퍼 함수를 ​​작성해 보겠습니다.

function wrap(f) {
    return function (...args){
      return f(...args)
    };
}


이제 래퍼 함수를 ​​사용해 보겠습니다.

const wrappedF = wrap(f);
f(1,1) // {name: 1, email: 2, this: Window}
wrappedF(1,1) // {name: 1, email: 2, this: Window}


두 호출이 동등하거나 투명해 보입니다.

그러나 다음과 같이 함수를 호출해 보겠습니다.

f.apply({a:1}, [1,1]) // {name: 1, email: 2, this: {a:1}}
wrappedF.apply({a:1}, [1,1]) // {name: 1, email: 2, this: Window}


이제 호출 결과가 동일하지 않습니다. 반환된 객체는 이 키에 대해 다른 값을 가집니다.

래퍼 함수는 이 개체의 동적 특성으로 인해 왜곡을 만듭니다(1). 이 개체는 함수가 호출되는 방식에만 의존하며 범위 체인을 통해 액세스하거나 찾을 수 없습니다. 따라서 WrappedF 및 f 함수는 동일한 입력 인수를 갖지만 이 개체는 다릅니다.

초기 함수 랩은 함수 f 내에서 이 값(예: 적용 메서드를 통해 명시적으로 설정할 수 있음)을 전송할 수 없습니다. 그러나 초기 래퍼 기능을 현대화하는 매우 간단한 방법이 있습니다.

function wrap(f) {
    return function (...args){
      return f.apply(this, args)
    };
}
const wrappedF = wrap(f)
f.apply({a:1}, [1,1]) // {name: 1, email: 2, this: {a:1}}
wrappedF.apply({a:1}, [1,1]) // {name: 1, email: 2, this: {a:1}}


이제 함수 랩이 투명해졌습니다. 함수 f에서 이 값을 명시적으로 전달합니다. 인수와 this는 원래 버전과 래핑된 버전에 대해 동일합니다.

(1) - 이것의 동적 특성은 화살표가 아닌 함수에만 적용됩니다. 화살표 함수는 이 값을 소유하지 않지만 클로저(정적 특성)에서 가져옵니다. 함수 f가 화살표 함수로 정의된 경우 이러한 함수의 이 값은 생성되는 순간 정의되며 이미 함수 f가 호출되는 방식에 의존하지 않습니다.

추신
내 실습에서 나는 투명한 래핑 기능이 정말로 필요한 경우를 만난 적이 없지만 여전히 이것이 언제든지 도달하고 처벌할 수 있다는 것을 명심하는 것이 유용합니다=)
정적 범위에 축복을!

좋은 웹페이지 즐겨찾기