본문 바로가기

자바스크립트

객체지향 프로그래밍(2) : 프로토타입 상속

자바스크립트는 객체 프로토타입 체인으로 상속을 구현한다.

1. 클래스 기반 전통적인 상속 방식 흉내.

2. 클래스 개념 없이 객체의 프로토타입으로 상속 구현

function create_object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

create_object() 함수는 인자로 들어온 객체를 부모로 하는 자식 객체를 생성하여 반환한다.

새로운 빈 함수 객체 F를 만들고, F.prototype 프로퍼티에 인자로 들어온 객체를 참조한다.

함수 객체 F를 생성자로 하는 새로운 객체를 만들어 반환한다.

이렇게 반환된 객체는 부모 객체의 프로퍼티에 접근할 수 있고, 자신만의 프로퍼티를 만들 수도 있다.

이렇게 프로토타입의 특성을 활용하여 상속을 구현하는 것이 프로토타입 기반의 상속이다.


자바스크립트에서는 범용적으로 extend()라는 이름의 함수로 객체에 자신이 원하는 객체 혹은 함수를 추가시킨다.

jQuery 1.0의 extend 함수는 다음과 같이..

jQuery.extend = jQuery.fn.extend = function(obj, prop) {
    if( !prop ) {
        prop = obj;
        obj = this;
    }
    for(var i in obj) {
        obj[i] = prop[i];
    }
    return obj;
}
jQuery.extend = jQuery.fn.extend = ...

jQuery.fn은 jQuery.prorotype이다. 따라서 앞 코드가 의미하는 바는 jQuery 함수 객체와 jQuery 함수 객체의 인스턴스 모두 extend 함수가 있겠다는 말이다. 즉, jQuery.extend()로도,

var elem = new jQuery(..);

elem.extend();

로 호출가능.

    if( !prop ) {
        prop = obj;
        obj = this;
    }

위 코드는 extend 함수의 인자가 하나만 들어오는 겨웅에는 현재 객체(this)에 인자로 들어오는 객체의 프로퍼티를 복사함을 의미하고, 두 개가 들어오는 경우에는 첫 번째 객체에 두 번째 객체의 프로퍼티를 복사하겠다는 것을 뜻한다.

    for(var i in obj) {
        obj[i] = prop[i];
    }

루프를 돌면서 prop의 프로퍼티를 obj로 복사한다.

 

obj[i] = prop[i]; 는 얕은 복사를 의미한다. 즉, 문자 혹은 숫자 리터럴등이 아닌 객체(배열, 함수객체포함)인 경우 해당 객체를 복사하지 않고, 참조한다. 이는 두 번째 객체의 프로퍼티가 변경되면 첫 번째 객체의 프로퍼티도 같이 변경됨을 의미한다. 이것을 의도해서 작성한 경우가 아니라면, 작성자가 의도하지 않은 결과가 나오고 이를 디버깅하는 것은 그렇게 쉬운일이 아니다. 그러므로 보통 extend 함수를 구현하는 경우 대상이 객체일 때는 깊은 복사를 하는 것이 일반적이다.

 

깊은 복사를 하려면 복사하려는 대상이 객체인 경우 빈 객체를 만들어 extend 함수를 재귀적으로 다시 호출하는 방법을 쓴다. 또 함수객체인 경우 그대로 얕은 복사를 진행한다.

다음은 제이쿼리 1.7 extend함수는 일부다.

for ( ; i < length; i++ ) {
    // Only deal with non-null/undefined values
    if ( (options = arguments[ i ]) != null ) {
        // Extend the base object
        for ( name in options ) {
            src = target[ name ];
            copy = options[ name ];

            // Prevent never-ending loop
            if ( target === copy ) {
                continue;
            }

            // Recurse if we're merging plain objects or arrays
            if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
                if ( copyIsArray ) {
                    copyIsArray = false;
                    clone = src && jQuery.isArray(src) ? src : [];

                } else {
                    clone = src && jQuery.isPlainObject(src) ? src : {};
                }

                // Never move original objects, clone them
                target[ name ] = jQuery.extend( deep, clone, copy );

            // Don't bring in undefined values
            } else if ( copy !== undefined ) {
                target[ name ] = copy;
            }
        }
    }
}

// Return the modified object
return target;

point1

if ( (options = arguments[ i ]) != null )

인자로 넘어온 객체의 프로퍼티를 options로 참조시키고, 이 프로퍼티가 null이 아닌 경우 블록 안으로 진입

 

point2

src = target[ name ];
copy = options[ name ];

src는 반환될 복사본 target의 프로퍼티를 가리키고, copy는 복사할 원본의 프로퍼티를 가리킨다.

 

point3

if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {

deep 플래그: jQuery의 extend 함수는 사용자가 첫 번째 인자로 Boolean값을 넣어 깊은 복사를 할 것인지를 선택할 수 있게한다.

copy 프로퍼티가 객체이거나 배열인 경우 재귀 호출을 하려고 블록 안으로 진입한다.

 

point4

if ( copyIsArray ) {
    copyIsArray = false;
    clone = src && jQuery.isArray(src) ? src : [];

} else {
    clone = src && jQuery.isPlainObject(src) ? src : {};
}

copy가 배열인 경우 빈 배열을, 객체인 경우 빈 객체를 clone에 할당한다.

여기서 src가 같은 배열, 혹은 같은 객체이면 clone에 해당 배열 혹은 객체를 참조시킨다.

이는 복사본에 같은 이름의 프로퍼티가 있는 경우, 원본과 똑같은 배열이거나 객체라면 새롭게 할당시키지 않고, 복사본의 해당 프로퍼티에 추가될 수 있게 하기 위해서이다.

 

point5

target[ name ] = jQuery.extend( deep, clone, copy );

extend 함수를 다시 호출한다. clone이라는 빈 객체에 copy 객체를 복사함을 의미한다. copy 객체 안에 다시 객체 혹은 배열이 있는 경우 다시 재귀 호출이 이루어진다.

 

point6

else if ( copy !== undefined ) {
    target[ name ] = copy;
}

copy 프로퍼티가 객체 혹은 배열이 아닌 경우 undefined인지를 살피고 target 객체에 할당한다.

 

point7

return target;

만들어진 복사본을 반환한다.

 

jQuery의 extend 함수는 사용자가 얕은 복사를 할 것인지, 깊은 복사를 할 것인지 선택할수 있게 구현되어 있다.

(function($) {
    $.extend($.fn, {
        my_func: function() {}
    });
})(jQuery);

$.my_func();

'자바스크립트' 카테고리의 다른 글

객체지향 프로그래밍(4): 캡슐화  (0) 2022.04.17
객체지향 프로그래밍(3) : 클래스기반 상속  (0) 2022.04.17
객체지향 프로그래밍(1)  (0) 2022.04.17
ORM 활용  (0) 2022.04.10
데이터 인라인 포함하기  (0) 2022.04.10