자바스크립트 모듈패턴이란? (2)

이정민 Lee Jeong Min·2021년 2월 23일
0

Javascript

목록 보기
7/10

private 멤버

function add(num1, num2) {
    var sum = num1 + num2;
    return sum;
}

num1, num2, sum은 함수 내부에서는 접근이 가능하지만 함수 외부에서는 접근이 불가능하다.
private 멤버에 대한 별도의 문법은 없지만 클로저를 사용해서 구현할 수 있다.

생성자 함수 안에서 클로저를 만들면, 클로저 유효범위 안의 변수는 생성자 함수 외부에 노출되지 않지만 객체의 공개 메서드 안에서는 쓸 수 있다. (스코프 체인을 통해 변수에 접근이 가능)

즉, private한 변수들을 함수의 지역 변수로 만들고, 객체의 메서드를 정의하면, 이 메서드 안에서는 private 변수에 접근할 수 있는 것이다.

function Gadget(){ 
  // private member
  var name = 'foo'; 
  // privieged member
  this.getName = function(){ 
    return name; 
  }; 
} 

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

private 멤버에 접근 가능하게끔 하는 public 멤버를 privieged 멤버라 볼 수 있다.

private 멤버의 허점

privieged 메서드에서 private 변수의 값을 바로 반환할 경우 이 변수가 객체나 배열이라면 값이 아닌 참조가 반환되기 때문에, 외부 코드에서 private 변수 값을 수정할 수 있다.

function Palette(){
    // private 멤버
    var colors = {
        white : "#ffffff",
        black : "#000000",
        red : "#ff0000"
    };

    // 공개 함수
    this.getColors = function(){
        return colors;
    };
}

var myPalette = new Palette(),
    specs = myPalette.getColors();

specs.blue = "#0000ff";

console.log(myPalette.getColors());
// white: "#ffffff", black: "#000000", red: "#ff0000", blue: "#0000ff"}

getColors()colors 객체에 대한 참조를 반환한다는 게 문제점이다.
이는 주어진 객체의 최상위 프로퍼티만을 복사하는 extend() 함수와 모든 중첩 프로퍼티를 재귀적으로 복사하는 extendDeep() 함수로 해결할 수 있다.

객체 리터럴과 private

생성자가 아닌 객체 리터럴로 private 멤버를 구현할 수도 있다.
객체 리터럴에서는 익명 즉시 실행함수를 추가하여 클로저를 만든다.

var myobj = (function(){
    // private 멤버
    var name = "foo";

    return {
        // privieged 메서드
        getName : function(){
            return name;
        }
    };
}());

myobj.getName();

이는 더글라스 크락포드가 제안한 모듈패턴 형식으로, 객체를 반환하는 익명 함수를 가진다.
즉, 객체 리터럴이 싱글톤에 대한 public 인터페이스를 정의하는 것이다.

프로토타입과 private

생성자를 사용하여 private 멤버를 만들 경우, 생성자를 호출하여 새로은 객체를 만들 때마다 private 멤버가 매번 재생성된다는 단점이 있다. (생성자 내부에서 this에 멤버를 추가하면 발생하는 문제)

이러한 중복을 없애고 메모리를 절약하려면 private 속성들을 생성자의 prototype 속성에 추가해야 한다. 이렇게 하면 동일한 생성자로 생성된 모든 인스턴스가 멤버들을 공유하게 된다.

이를 위해서는 두 가지 패턴, 생성자 함수 내부에 비공개 멤버를 만드는 패턴객체 리터럴로 비공개 멤버를 만드는 패턴을 함께 써야 한다.
(prototype 속성도 결국 객체이기 때문에, 객체 리터럴로 생성할 수 있다.)

function Foo() {
    // 비공개 멤버
    var name = 'foo';

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

Foo.prototype = (function () {
    // 비공개 멤버
    var nickname = 'foo foo';

    // 공개된 프로토타입 멤버
    return {
        getNickname : function () {
            return nickname;
        }
    }

})();

var foo = new Foo();
console.log(foo.getName()); // 객체 인스턴스의 privieged 메서드
console.log(foo.getNickname()); // 프로토타입의 privieged 메서드

정리

모듈 패턴은 늘어나는 코드를 구조화하고 정리하는데 도움이 된다.

다른 언어와는 달리 자바스크립트에는 패키지를 위한 별도의 문법이 없지만,
모듈 패턴을 사용하여 개별적인 코드를 느슨하게 결합시킬 수 있다.
따라서 각 기능들을 블랙박스처럼 다루면서도, 기능 추가/수정/삭제를 계속해서 자유롭게 할 수 있다.

자바스크립트의 모듈패턴은 네임스페이스, 즉시 실행함수, private/privieged 멤버, 의존관계 선언의 조합으로 볼 수 있다.

var MYAPP = MYAPP || {};

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('MYAPP.utilities.array');

// 모듈 정의
MYAPP.utilities.array = function () {

    // 의존 관계 선언
    var uobj = MYAPP.utilities.object,
        ulang = MYAPP.utilities.lang;

    // private 속성
    var array_string = '[object Array]',
        ops = Object.prototype.toString;

    // private 메서드
  
    // 필요하다면 일회성 초기화 코드 삽입

    // public
    return {
        inArray : function (needle, haystack) {
            for (var i = 0, max = haystack.length; i < max; i += 1) {
                if (haystack[i] === needle) {
                    return true;
                }
            }
        },
        isArray : function (a) {
            return ops.call(a) === array_string;
        }
    }
}();

Reference

profile
https://jeong-min.com/ <- 블로그 이전했습니다 :)

0개의 댓글