자바스크립트 객체 생성 패턴

2 minute read

네임스페이스 패턴

네임스페이스는 전역 객체를 하나 만들고 모든 기능을 이 객체에 추가한다. 주의해야 할 점은 추가하려는 프로퍼티가 이미 존재할 수도 있기 때문에, 추가하려는 프로퍼티가 존재하는지 여부를 검사하는 해야한다.

네임스페이스를 적용하지 않은 코드

function Parent() {};
function Child() {};

var some_var = 1;

var module1 = {};
module1.data = {a: 1, b: 2};
var module2 = {};

네임스페이스를 적용한 코드


// 기존의 MYAPP 이 존재하는지 검사
var MYAPP = MYAPP || {};

// 새롭게 생성되는 프로퍼티가 이미 존재하는 프로퍼티를 덮어 쓰지 않도록 하는 namespace 메소드
MYAPP.namespace = function (ns_string) {
    var parts = ns_string.split('.'),
        parent = MYAPP,
        i;
    
    // 처음에 중복되는 전역 객체명은 제거한다.
    if (parts[0] === "MYAPP") {
        parts = parts.slice(1);
    }

    for (i = 0; i < parts.length; i += 1) {
        // 프로퍼티가 존재하지 않으면 생성한다.
        if (typeof parent[parts[i]] === "undefined") {
            parent[parts[i]] = {};
        }

        parent = parent[parts[i]];
    }
    return parent;
}

MYAPP.namespace('Parent');
MYAPP.Parent = function () {};

MYAPP.namespace('Child');
MYAPP.Child = function () {};

MYAPP.namespace('some_var');
MYAPP.some_var = 1;

MYAPP.namespace('modules.module1.data');
MYAPP.modules.module1.data = {a: 1, b: 2};

MYAPP.namespace('modules.module2');
MYAPP.module2 = {};

의존 관계 선언

의존 관계 선언 패턴은 함수나 모듈 내 최상단에 의존관계가 있는 모듈을 선언하는 것이다.

장점

  • 함수의 첫 머리에 의존 관계가 선언되기 때문에 의존 관게를 찾아내고 이해하기 쉽다.
  • 의존 관계가 명시적으로 선언되어 있기 때문에 코드를 사용하는 사람이 페이지 내에 반드시 포함 시켜야하는 스크립트 파일이 무엇인지 알 수 있다.
  • dom과 같이 지역변수를 사용하는 것이 YAHOO.util.dom처럼 전역 변수의 중첩프로퍼티를 사용하는 것 보다 빠르다.
var myFunction = function() {
    // 의존관계가 있는 모듈을 지역변수로 선언한다.
    var event = YAHOO.util.Event, 
        dom = YAHOO.util.dom;

    // event와 dom을 사용한다.
}

비공개 프로퍼티와 메서드

생성자 함수로 비공개 프로퍼티를 만드는 방법

자바스크립트에서는 비공개 멤버에 대한 별도의 문법이 없지만 클로저를 사용해서 구현할 수 있다. 생성자 함수 안에서 클로저를 만들면, 클로저 유효범위 안의 변수는 생성자 함수 외부에 노출되지 않지만 객체의 공개 메서드 안에서는 쓸 수 있다.

function Gadget() {
    // 비공개 멤버
    var name = 'iPod';
    //공개된 함수
    this.getName = function () {
        return name;
    };
}

var toy = new Gadget();
console.log(toy.name); // undefined
console.log(toy.getName()); // iPod

클로저에서 객체를 반환하는 경우에는 새로운 객체를 만들어 반환해야한다.

function Gadget() {
    // 비공개 멤버
    var specs = {
        screen_width: 320,
        screen_height: 480,
        color: "white"
    };

    // 공개함수
    this.getSpecs = function () {
        var clonedSpecs = {};
        clonedSpecs.screen_width = specs.screen_width;
        clonedSpecs.screen_height = specs.screen_height;
        clonedSpecs.color = specs.color;
        return clonedSpecs;
    };
}

리터럴 객체로 비공개 프로퍼티 만드는 방법

위 방식은 비공개 프로퍼티를 가지는 객체를 생성자 함수로 만드는 방식이었다. 다음은 리터럴 객체로 비공개 멤버를 구현한 코드이다.

var myobj;
(function () {
    // 비공개 멤버
    var name = "my, oh my";

    myobj = {
        getName: function () {
            return name;
        }
    };
}());

myobj.getName(); // my, oh my
var myobj = (function () {
    // 비공개 멤버
    var name = "my, oh my";

    return {
        getName: function () {
            return name;
        }
    };
}());

myobj.getName(); // my, oh my

생성자의 프로토타입을 사용하여 비공개 프로퍼티 만드는 방법

생성자를 사용하여 비공개 멤버를 만들 경우, 생성자를 호출할 때마다 비공개 멤버가 재 생성된다. 생성자의 프로토타입에 프로퍼티와 메서드를 추가하면 메모리를 절약하고 비공개 멤버가 재 생성되지 않도록 할 수있다.

function Gadget() {
    // 비공개 메서드
    var name = 'iPod';

    // 공개함수
    this.getName = function () {
        return name;
    }

    this.setName = function (arg) {
        name = arg;
    }
}

// 생성자의 프로토타입에 비공개 멤버와 공개 함수를 추가한 경우
Gadget.prototype = (function () {
    // 비공개 멤버
    var browser = "Mobile Webkit";

    return {
        // 공개 함수
        getBrowser: function () {
            return browser;
        },
        setBrowser: function (arg) {
            browser = arg;
        }
    };
}());

var gadget1 = new Gadget();
var gadget2 = new Gadget();

gadget1.setName('gadget1'); 
gadget2.setName('gadget2');
console.log(gadget1.getName()); // gadget1
console.log(gadget2.getName()); // gadget2

gadget1.setBrowser('Edge');
gadget2.setBrowser('Chrome');
console.log(gadget1.getBrowser()); // Chrome
console.log(gadget2.getBrowser()); // Chrome