다음은 John Hann의 구현입니다. 이 코드는 영리한 방법을 사용하여 메서드 호출 결과를 캐시합니다.
코드 분석:
// memoize: 메모이제이션을 이용한 캐싱의 일반적인 방법
// func: 캐시할 메서드
// 컨텍스트: 메소드 실행 컨텍스트
// 참고: 메소드는 외부에서 액세스 가능해야 하며 매개변수는 문자 직렬화 가능해야 합니다.
기능 메모(func, context) {
Function memoizeArg(argPos) { //매개변수는 원래 메서드의 매개변수 위치를 나타냅니다.
var 캐시 = {}; //이 캐시의 키는 매개변수이고, 값은 실행 결과
return function () { //함수 클로저 반환
If (argPos == 0) { //첫 번째 매개변수, 캐시된 키에 해당 매개변수가 없으면 원래 함수를 실행하고 실행 결과를 저장합니다
If (!(인수[argPos] in 캐시)) {
캐시[arguments[argPos]] = func.apply(컨텍스트, 인수)
~
캐시 반환[인수[argPos]]
Else {//는 첫 번째 매개변수가 아닙니다. 매개변수가 캐시 키에 없으면 Memoizearg 메소드를 재귀적으로 실행합니다. -1
If (!(인수[argPos] in 캐시)) {
캐시[arguments[argPos]] = memoizeArg(argPos - 1)
~
캐시 반환[인수[argPos]].apply(this, 인수)
}
}
var arity = func.arity || func.length; //func 매개변수의 길이, length 속성은 JavaScript에서 사용되고 arity 속성은 기타에 사용됩니다.
Return memoizeArg(arity - 1); //마지막 매개변수에서 재귀
}
사용:
코드 복사
코드는 다음과 같습니다.
var mem = memoize(func, this);
경고(mem.call(this,1,1,2));
경고(mem.call(this,2,1,2));
경고(mem.call(this,3,1,3))
경고(mem.call(this,2,2,4));
간단해 보이지만 언뜻 이해하기 쉽지 않을 수도 있습니다. 하지만 클로저의 사용법을 숙지하시면 이해하기 쉬울 것입니다. 위에서 mem.call을 여러 번 호출한 후 트리가 형성되고, 각 노드는 클로저이고, 각 클로저는 캐시를 가지며, 각 캐시의 키는 트리 분기입니다.
(참고: 위 그림의 "result"도 클로저이지만 argPos는 0입니다)
하지만 여러 가지 방법이 있습니다. 예를 들어 Limboy는 다음과 같이 말했습니다.
코드 복사
코드는 다음과 같습니다.
기능 메모(fn){
var 캐시 = {};
반환함수(){
var 키 = []
for( var i=0, l = 인수.길이; i < l; i )
key.push(인수[i])
If( !(캐시 키) )
캐시[키] = fn.apply(this, 인수)
캐시 반환[키]
};
}
구현 방법은 더 간단하지만 매개변수를 배열로 밀어넣은 뒤 그 배열을 키로 사용하고 키는 문자열 형식만 지원하므로 사용 시 주의가 필요합니다(예: 객체 "[object Object]" 문자열 이후에만 표시될 수 있으며 해당 기능은 위의 기능보다 약합니다.
이를 개선하는 것은 어렵지 않습니다. 매개변수에 대해 별도의 개체를 생성하면 원본 캐시 개체와 이 별도의 매개변수 개체가 ID와 연결됩니다.
기능 메모(fn){
var 캐시 = {}, args = {};
반환함수(){
for( var i=0, key = args.length; i < key; i ) {
If( 같음( args[i], 인수 ) )
캐시 반환[i]
}
args[키] = 인수
캐시[키] = fn.apply(this, 인수)
캐시[키] 반환
};
}
간결한 함수형 메서드로 작성할 수 있는 다른 메서드도 있습니다.