본문으로 바로가기

Static member of JavaScript

category JavaScript/JS 객체지향 프로그래밍 2016. 10. 29. 16:15

스태틱 멤버

스태틱 프로퍼티와 메서드란 인스턴스에 따라 달라지지 않는 프로퍼티와 메서드 를 말합니다.

클래스 기반 언어에서는 별도의 문법을 통해 스택틱 멤버를 생성하여 클래스 자체의 멤버인 것처럼 사용할 수 있습니다.

예를 들어 MathUtils 클래스에 max() 라는 스태틱 메서드가 있다면MathUtils.max(2,5) 와 같은 식으로 호출할 수 있습니다.

이것은 공개 스태틱 멤버의 예로, 클래스의 인스턴스를 생성하지 않고도 사용할 수 있습니다.

비공개 스태틱 멤버는 클래스 사용자에게는 보이지 않지만 클래스의 인스턴스들은 모두 함께 사용할 수 있습니다.

그럼 자바스크립트에서 공개와 비공개 스태틱 멤버를 구현하는 방법을 살펴보도록 합니다.




공개 스태틱 멤버

자바스크립트에는 스태틱 멤버를 표기하는 별도의 문법이 존재하지 않습니다.

그러나 생성자에 프로퍼티를 추가함으로써 클래스 기반 언어와 동일한 문법을 사용할 수 있습니다.

생성자도 다른 함수와 마찬가지로 객체이고 그 자신의 프로퍼티를 가질 수 있기 때문에 이러한 구현이 가능합니다.

다음 예제는 Gadget 이라는 생성자에 스태틱 메서드인 isShiny() 와 일반적인 인스턴스 메서드인 setPrice() 를 정의한 것입니다.

isShiny() 는 특정 Gadget 객체를 필요로 하지 않기 때문에 스태틱 메서드라 할 수 있습니다.

모든 Gadget 이 빛나는지 알아내는 데는 특정한 하나의 Gadget 이 필요하지 않은 것과 같습니다.

반면 개별 Gadget 들의 가격은 다를 수 있기 때문에 setPrice() 메서드를 쓰려면 객체가 필요합니다.

javascript
// 생성자
var Gadget = function () { };

// 스태틱 메서드
Gadget.isShiny = function () {
	return 'you bet.'
};

// 프로토타입에 일반적인 함수를 추가
Gadget.prototype.setPrice = function (price) {
	this.price = price;
};

이제 이 메서드를 호출해 보도록 합니다.

스태틱 메서드인 isShiny() 는 생성자를 통해 직접 호출되지만, 일반적인 메서드는 인스턴스를 통해 호출됩니다.

javascript
// 스태틱 메서드를 호출하는 방법
console.log(Gadget.isShiny()); // you bet
	
// 인스턴스를 생성한 후 메서드를 호출하기
var iphone = new Gadget();
iphone.setPrice(500);

인스턴스 메서드를 스태틱 메서드와 같은 방법으로 호출하면 동작하지 않습니다.

스태틱 메서드 역시 인스턴스 iphone 객체를 이용해 호출하면 동작하지 않습니다.

javascript
console.log(typeof Gadget.setPrice); // undefined
console.log(typeof iphone.isShiny); // undefined

스태틱 메서드가 인스턴스를 통해 호출했을 때도 동작한다면 편리한 경우가 있을 수 있습니다.

이 경우에는 간단하게 프로토타입에 새로운 메서드를 추가하는 것만으로 쉽게 구현할 수 있습니다.

이 새로운 메서드는 원래의 스태틱 메서드를 가리키는 일종의 퍼사드(facade) 역할을 합니다.

javascript
Gadget.prototype.isShiny = Gadget.isShiny;
console.log(iphone.isShiny());

이런 경우에는 스태틱 메서드 안에서 this 를 사용할 때 주의를 기울여야 합니다.

Gadget.isShiny() 호출했을 때 내부의 this 는 Gadget 생성자를 가리키지만, iphone.isShiny() 를 호출했을 때는 this 는 생성자 함수로 생성된 객체인 iphone 을 가리키게 됩니다.


마지막으로 스태틱한 방법으로도, 스태틱하지 않은 방법으로도 호출될 수 있는 어떤 메서드를 호출 방식에 따라 살짝 다르게 동작하게 하는 예제를 살펴보도록 합니다.

메서드가 어떻게 호출되었는지 판별하기 위해서 instanceof 연산자를 활용해 봅니다.

javascript
// 생성자
var Gadget = function (price) {
	this.price = price;
};

// 스태틱 메서드
Gadget.isShiny = function () {
	
	// 다음은 항상 동작한다.
	var msg = 'you bet';
	
	if (this instanceof Gadget) {
		// 다음은 스태틱하지 않은 방식으로 호출되었을 때만 동작하도록 한다.(인스턴스 멤버를 가리킴)
		msg += ', it costs $' + this.price + '!!';
	}
	return msg;
	
};

// 프로토타입에 일반적인 메서드를 추가한다
Gadget.prototype.isShiny = function () {
	return Gadget.isShiny.call(this); // 이 this 는 생성자 함수로 생성된 인스턴스 객체를 가리키게 된다.
};

스태틱 메서드와 인스턴스를 통해 스태틱하지 않은 방법으로 호출해 보면 다음과 같은 결과가 나타납니다.

javascript
// 스태틱 메서드를 호출
console.log(Gadget.isShiny()); // you bet 이 기록된다.
	
// 인스턴스를 통해 스태틱하지 않은 방법으로 호출
var iphone = new Gadget(345.99);
console.log(iphone.isShiny()); // you bet, it costs $345.99!! 가 기록된다.



비공개 스태틱 멤버

지금까지는 공개 스태틱 멤버를 살펴보았습니다.

이번에는 비공개 스태틱 멤버를 구현하는 방법을 알아봅니다.


비공개 스태틱 멤버란 다음과 같은 의미를 가지고 있습니다.

  • 동일한 생성자 함수로 생성된 객체들이 공유하는 멤버입니다.
  • 생성자 외부에서는 접근할 수 없습니다.


Gadget 생성자 안에 counter 라는 비공개 스태틱 프로퍼티를 구현하는 예제를 살펴보도록 합니다.

비공개 프로퍼티는 먼저 클로저 함수를 만들고, 비공개 멤버를 이 함수로 감싼 후 이 함수를 즉시 실행한 결과로 새로운 함수를 반환하게 됩니다.

반환되는 함수는 Gadget 변수에 할당되어 새로운 생성자가 될 것입니다.

javascript
var Gadget = function () {
	
	// 스태틱 변수/프로퍼티
	var counter = 0;
	
	// 생성자의 새로운 구현 버전을 반환한다.
	return function () {
		console.log(counter += 1);
	};
	
}(); // 즉시 실행한다.

새로운 Gadget 생성자는 단순히 비공개 counter 값을 증가시켜 출력합니다.

몇 개의 인스턴스를 만들어 테스트해보면 실제로 모든 인스턴스가 동일한 counter 값을 공유하고 있음을 확인할 수 있습니다.

javascript
var g1 = new Gadget(); // 1 이 기록된다
var g2 = new Gadget(); // 2 이 기록된다
var g3 = new Gadget(); // 3 이 기록된다

객체당 1씩 counter 를 증가시키고 있기 때문에 이 스태틱 프로퍼티는 Gadget 생성자를 통해 생성된 개별 객체의 유일성을 식별하는 ID 가 될 수 있다.

유일한 식별자는 쓸모가 많으니 특권 메서드로 노출시켜도 좋지 않을까?

앞선 예제에 덧붙여 비공개 스태틱 프로퍼티에 접근할 수 있는 getLastId() 라는 특권 메서드를 추가해보도록 해봅니다.

javascript
// 생성자
var Gadget = function () {
	
	// 스태틱 변수/프로퍼티
	var counter = 0,
	    NewGadget;
	
	// 이 부분이 생성자를 새롭게 구현한 부분이다.
	NewGadget = function () {
		counter += 1;
	};
	
	// 특권 메서드
	NewGadget.prototype.getLastId = function () {
		return counter;
	};
	
	// Gadgt 생성자를 덮어쓴다.
	return NewGadget;
	
}(); // 즉시 실행한다.

새로운 버전을 아래에서 테스트 해봅니다.

javascript
var iphone = new Gadget();
console.log(iphone.getLastId()); // 1 이 기록
	
var ipod = new Gadget();
console.log(ipod.getLastId()); // 2 이 기록

var ipad = new Gadget();
console.log(ipad.getLastId()); // 3 이 기록

공개/비공개 스태틱 프로퍼티는 상당히 편리합니다.

특정 인스턴스에 한정되지 않는 메서드와 데이터를 담을 수 있고 인스턴스별로 매번 재생성되지도 않습니다.



Jaehee's WebClub



댓글을 달아 주세요

  1. 2018.11.10 00:56

    비밀댓글입니다

  2. Spectator 2018.11.10 01:01

    아래에 댓글 단 사람인데 실수로 비공개로 해버렸네요.. 비번도 까먹어서 다시 질문 드립니다.
    Gadget.prototype.isShiny = function () { return Gadget.isShiny.call(this); }// 이 this 는 생성자 함수로 생성된 인스턴스 객체를 가리키게 된다. };
    라고 하셨는데 왜 this가 생성자 함수로 생성된 인스턴스를 가리키는지 이해가 잘 안되네요...
    Gadget.prototype.isShiny = Gadget.isShiny; 로 하면 안되는 건가요 ?