Programing/Design Patterns

Javascript 다른 객체의 메서드 호출하기 메서드 빌려쓰기 패턴

리커니 2022. 3. 14. 13:34
반응형

Javascript 다른 객체의 메서드 호출하기 메서드 빌려쓰기 패턴

 

어떤 객체에 특정 메소드만 호출해서 쓰고싶은 경우가 있습니다.

물론 상속을 받거나 특정 코드를 ctrl + c, ctrl + v 해서 쓸 수는 있지만

이는 중복코드를 발생시키고 유지보수 측면에서도 좋지 않습니다.

 

그래서 해당 객체의 메서드에 접근하여 호출하는 방식을 사용하게됩니다.

이를 위해서는 객체의 this를 바인딩 해주는 call, apply, bind 메서드를 활용하게 되는데요.

call, apply, bind 메서드의 사용법, 차이점은 아래의 Link를 참고해주세요.

 

Link: https://aljjabaegi.tistory.com/524

 

javascript call, apply, bind 차이점! 알짜만 빼먹기!

javascript call, apply, bind 함수 알짜만 빼먹기 call, apply, bind 는 함수의 this를 명시적으로 바인딩 할때 사용합니다. 유용하면서 자주 사용되는 함수이고, 형태는 조금씩 다르니 익숙해 지도록 합

aljjabaegi.tistory.com

 

그럼 간단한 예제를 보겠습니다.

유사배열인 htmlCollection을 배열로 변경하는 예제 입니다.

div 태그 내에 3개의 section tag가 있습니다.

 

<div id="test">
    <section>1</section>
    <section>2</section>
    <section>3</section>
</div>

 

div 자식 요소들인 section 들을 유사배열로 접근해보겠습니다.

 

const sections = document.getElementById("test").children;
console.log(sections);

 

 

콘솔의 결과는 위와 같습니다.

유사 배열은 객체이기 때문에 배열의 foreach, map, filter, reduce와 같은 메소드들을 사용할 수 없습니다.

그래서 유사배열을 배열로 변경하고 싶을 때 배열의 slice 메소드를 활용합니다.

 

console.log(Array.prototype.slice.call(sections));
console.log([].slice.call(sections); /*위와 같은 결과*/
console.log(Array.from(sections));  /*위와 같은 결과*/

 

Array에 prototype에 있는 slice 메소드의 this 를 call을 활용해 sections로 변경해 호출하였습니다.

(물론 Array.from 메소드를 사용해도 되지만 빌려쓰기 예시를 위해..)

 

 

배열의 slice 메소드를 빌려써서 유사배열을 배열로 변경하였습니다.

prototype이 HTMLCollection에서 Array로 변경된 것을 보실 수 있죠.

이처럼 메소드를 빌려쓰기 위해서는 메소드의 this를 call, apply 메소드를 활용하여 바인딩해 사용합니다.

 

한가지 예를 더 들어보겠습니다.

아래와 같이 Calculator 클래스가 있습니다.

 

Class의 메소드 빌려쓰기

class Calculator {
    constructor(){
        this.values = [];
    }
    sum(){
        let sum = 0;
        if(this.values.length > 0){
            for(let num of this.values){
                sum += num;
            }
        }else{
            console.erorr("합을 구할 배열의 값이 존재하지 않습니다.");
        }
        return sum;
    }
    setValues(values){
        if(this.getObjType(values) === "Array"){
             this.values = values;
        }else{
            console.error("setValues 메서드 파라미터는 배열만 가능합니다.");
        }
    }
    getObjType(values){
        return Object.prototype.toString.call(values).slice(8, -1);
    }
}

 

이 클래스의 sum 메소드를 다른곳에서 사용하고 싶다면 아래와 같이 활용하시면 됩니다.

 

const test = {
    values : [1,2,3,4,5]
};
console.log(calc.sum.call(test)); /*15*/

 

또는 Test라는 클래스를 내에서 Calculator의 sum 이라는 메소드를 쓰고 싶다면

Calculator 클래스를 상속 받아서 사용할 수도 있습니다.

 

class Test extends Calculator{
    constructor(){
        super();
    }
}
const t = new Test();
console.log(t.sum([1,2,3,4,5])); /* Array*/

 

하지만 이렇게 구현을 하면 setValues, getObjType 과 같이 불필요한 메소드까지 상속을 받게 되죠.

그래서 단순히 sum 메소드만 쓰고 싶다면 아래와 같이 this를 바인딩 하여 사용하면 됩니다.

빌려쓰기 패턴을 사용할 때는 빌려쓰는 메소드의 this가 가리키는 것이 무엇인지 항상 주의하고 사용해야 합니다.

 

const calc = new Calculator();
	
class Test {
    constructor(){
    }
    getSum(values){
        this.values = values;
        return calc.sum.call(this);
    }
}
		
const t = new Test();
console.log(t.getSum([1,2,3,4,5])); /*15*/

 

단순히 Test 클래스에 sum 메소드를 추가하여 사용할 수도 있습니다.

 

const t = new Test();
t.sum = calc.sum;
t.values = [1,2,3,4,5];
console.log(t.sum()); /*15*/

 

이렇게 구현했을 때 문제점은 무엇일까요? new Test(); 를 할 때마다 sum 메소드를 추가해 주어야 합니다. 

 

const t = new Test();
t.sum = calc.sum;
t.values = [1,2,3,4,5];
console.log(t.sum()); /*15*/
	
const t2 = new Test();
console.log(t2.sum()); /*t2.sum is not a function*/

 

빌려쓰기 패턴의 사용법과 주의할 점에 대해서 알아보았습니다.

이처럼 메소드 빌려쓰기 패턴을 사용하면

코드 재사용율을 높힐 수 있고, 중복코드 또한 줄일 수 있으니 절절하게 활용하도록 합시다!

 

 

반응형