코딩/Javascript

[JS-중급편-OOP] 9.빌트인 객체 확장

드리프트 2020. 12. 27. 22:03
728x170

 

 

우리가 지금까지 알고 있듯이 JavaScript는 빌트인 객체가  잘 제공되는 언어입니다.

 

이러한 빌트인 객체는 텍스트, 숫자, 데이터, 날짜 등으로 작업할 때 많은 핵심 기능을 제공해 줍니다.

 

자바 스크립트에 익숙해지면 점점 더 빌트인 객체에 대해 더 완벽하게 이해하고 싶어할 겁니다.

 

배열을 셔플(shuffle)할 수 있는 다음 코드를 예로 들어 알아 보겠습니다.

 

function shuffle(input) {
    for (let i = input.length - 1; i >= 0; i--) {

        let randomIndex = Math.floor(Math.random() * (i + 1));
        let itemAtIndex = input[randomIndex];

        input[randomIndex] = input[i];
        input[i] = itemAtIndex;
    }
    return input;
}

 

셔플하고 싶은 배열을 shuffle 함수의 인수로 전달하면 됩니다.

 

let shuffleArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
shuffle(shuffleArray);

// and the result is...
console.log(shuffleArray);

// [ 8, 6, 5, 4, 3, 9, 2, 1, 7, 10 ]

 

이 코드의 실행 결과는 우리가 전달한 배열의 내용이 재정렬된다는 겁니다.

 

생각해 보면 이 기능은 매우 유용합니다. 그래서 이렇게 생각할 수 있습니다.

 

위 shuffle 기능이 너무 유용해서 Array 객체의 빌트인 기능인 push, pop, slice 및 기타 기능처럼 쉽게 액세스 할 수 있지 않을까 생각할 수 있습니다.

 

만약 shuffle 함수가 Array 객체의 빌트인 기능이라면 다음과 같이 shuffle 함수를 호출할 수 있을 겁니다.

 

let shuffleArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
shuffleArray.shuffle();

 

위 방식은 빌트인 객체인 Array에 우리가 정의한 기능을 확장한다는 뜻입니다.

 

다음 섹션에서는 빌트인 기능을 확장하는 정확한 방법, 그리고 작동되는 이유 및 이렇게 확장하는 것이 논란의 여지가 있는지 없는지에 대해 알아 보겠습니다.

 

 

Prototype 다시 호출하기

새로운 기능으로 빌트인 객체를 확장한다는 말은 처음에 들으면 좀 어렵게 들릴 지 모르겠으나, 확장하는 방법만 정확하게 이해한다면 크게 어려운 개념이 아닙니다.

 

이해를 돕기 위해 Array 객체와 관련된 샘플 코드와 다이어그램의 조합을 살펴 보겠습니다.

 

let tempArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

 

tempArray 객체의 전체 계층 구조를 다이어그램으로 나타내면 다음과 같습니다.

 

 

왼쪽에 있는 tempArray는 Array.prototype의 인스턴스입니다.

 

Array.prototype은 당연히 Object.prototype의 인스턴스입니다.

 

우리가 shuffle 기능을 Array 객체에 추가한다고 한다면 결국은 Array.prototype에 삽입하는 방법을 알아야 한다는 겁니다.

 

 

여기서 생각해 볼 문제는 JavaScript의 기이함이 빛을 발하는 부분이 있다는 겁니다.

 

우리는 모든 배열 기능을 구성하는 코드에 액세스 할 수 없습니다.

 

배열을 구성하는 함수 또는 객체를 찾을 수 없고 우리가 삽입할려고 하는 shuffle 함수를 삽입 할 수 없습니다.

 

Array와 같은 내장 객체는 인간이 갈 수 없는 브라우저의 깊숙한 곳에 있습니다.

 

그래서 우리는 다른 접근 방식을 취해야합니다.

 

다른 접근 방식은 그냥 자연스럽게 몰래 들어가서 우리의 기능을 Array 객체의 프로토타입 속성에 추가하는 것입니다.

 

다음과 같이 보일 것입니다.

Array.prototype.shuffle = function () {
    let input = this;

    for (let i = input.length - 1; i >= 0; i--) {

        let randomIndex = Math.floor(Math.random() * (i + 1));
        let itemAtIndex = input[randomIndex];

        input[randomIndex] = input[i];
        input[i] = itemAtIndex;
    }
    return input;
}

 

shuffle 함수가 Array.prototype에 선언되어 있습니다!

 

처음 본 shuffle 함수를 Array.prototype에 추가하기 위해 코드를 약간 변경했습니다.

 

shuffle 함수는 더 이상 shuffle이 필요한 배열을 참조하기 위한 인수를 사용하지 않습니다.

 

대신 이 함수는 이제 Array의 일부이기 때문에 함수 본문 내의 this 키워드를 이용해서 shuffle이 필요한 배열을 가리키게 합니다.

 

Array.prototype.shuffle = function () {
    let input = this;
...

 

이 코드를 실행하면 shuffle 함수는 Array 객체가 Array.prototype을 통해 노출하는 다른 모든 내장 메서드와 함께 액세스 가능하다는 겁니다.

 

이제 shuffle 기능에 액세스하려면 처음에 원했던 접근 방식을 사용하여 액세스 할 수 있습니다.

 

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
numbers.shuffle();

 

무엇보다도 우리가 새로 생성하는 모든 배열은 프로토타입 상속이 작동하는 방식 덕분에 기본적으로 위와 같은 셔플 기능에 액세스 할 수 있습니다.

 

 

 

 

빌트인 기능을 확장하는 것은 논란의 여지가 있습니다.

 

프로토 타입 속성을 사용하여 메서드와 속성을 선언하여 빌트인 객체의 기능을 확장하는 것이 얼마나 쉬운지를 감안할 때 모든 사람이 이 모든 기능을 좋아한다고 생각하기 쉽습니다.

 

그러나 빌트인 객체를 확장하는 것은 약간 논란의 여지가 있습니다.

 

만약 우리가 Array 객체의 shuffle 함수를 프로토 타입으로 추가했는데, 나중에 JavaScript 언어 자체적으로 발전을 해서 Array 객체에 shuffle 함수를 추가한다면 우리가 구현했던 shuffle과 Javascript가 발전되면서 구현한 shuffle 함수가 서로 충돌하게 문제가 발생됩니다.

 

물론, 우리의 코드가 더 빠를 수 있지만, Javascript가 발전되면서 추가한 코드가 더 빠를지 어떻게 알겠습니까?

 

또한 일부 기능은 확장하거나 재정의해서는 안됩니다.

 

예를 들어, 아래 코드는 Array 객체의 slice 함수가 작동하는 방식을 변경하는 것입니다.

 

Array.prototype.slice = function () {
    let input = this;
    input[0] = "This is an awesome example!";

    return input;
}

let tempArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
tempArray.slice();

// and the result is...
alert(tempArray);

 

이것은 끔찍한 예이지만 기존 기능을 손상시키는 것이 얼마나 쉬운 지 보여줍니다.

 

 

결론

결론은 본인의 판단하게 합리적으로 사용하면 된다는 겁니다.

 

만약 shuffle 이름을 cpro95Shuffle 이라고 이름 지으면 향후 Javascript가 이런 이름으로 새로운 기능을 추가한다는 확률은 0에 가깝기 때문에 충돌이 일어나지 않을 겁니다. ㅎㅎ

 

다음편에서는 클래스에 대해 알아보겠습니다.

그리드형