코드 재사용 및 최적화

From. 고성능 자바스크립트 그래픽스 - 라파엘레 체코/김태곤

고양이를 꾸미는 방법이 여러 가지 있듯, 타고난 유연함 덕분에 자바스크립트에서 상속할 수 있는 방법도 여러 가지 있다. 다음 코드는 프로토타입 상속을 사용해 Pet 객체를 만든 후 이 객체를 상속해 Cat 객체를 만들었다. 이런 방식의 상속 패턴은 여러 자바스크립트 자습서에 종종 볼 수 있으며, "전통적인" 자바스크립트 기법으로 보기도 한다.

						
							// Pet 객체를 정의한다. 이름과 다리의 개수를 전달한다.

							var Pet = function (name, legs) {
								this.name = name;	// 이름과 다리 개수를 저장한다.
								this.legs = legs;
							}

							// Pet의 이름과 다리 개수를 보여주는 메소드를 작성한다.
							Pet.prototype.getDetails = function () {
								return this.name + ' has ' + this.legs + ' legs';
							};

							// Pet을 상속해 Cat 객체를 정의한다.
							var Cat = function (name) {
								Pet.call(this, name, 4);	// 부모 객체의 생성자를 호출한다.
							};

							// Pet을 상속하는 코드
							Cat.prototype = new Pet();

							// Cat에 action 메소드를 추가한다.
							Cat.prototype.action = function () {
								return '새를 잡는다.';
							}

							// Cat의 인스턴스 생성 후 petCat에 저장한다.
							 var petCat = new Cat('Felix');

							var details = petCat.getDetails();
							console.log(details);	// Felix has 4 legs
							var action = petCat.action();
							console.log(action);	// 새를 잡는다. 
							petCat.name = 'Sylvester';
							console.log(petCat.name);	//Sylvester 
							petCat.legs = 7;
							console.log(petCat.legs);	//7
							details = petCat.getDetails();
							console.log(details);	//Sylvester has 7 legs 							
						

위의 코드는 잘 동작하지만 특별히 멋있지는 않다. C++이나 자바 같은 OPP 언어에 익숙하다면 이해하기 쉬운 new 연산자는 차치하고라도, prototype 키워드는 코드를 더 번잡하게 만드는 것 같고 비공개(private) 속성이나 메소드는 아예 없다.(petCat.legs 속성의 값을 어떻게 7로 바꾸었는지 살펴보자.) 이런 방식의 상속은 외부의 영향으로부터 객체를 전혀 지켜주지 못하므로, 여러 명의 프로그래머가 함께 작업하는 더 복잡한 프로젝트에서는 심각한 단점이 될 수도 있다.

prototype이나 new 연산자를 전혀 사용하지 않는 다른 방법으로는 함수형 상속을 사용해 객체의 인스턴스를 받아들인 후 확장하는 방법이 있다.

						
							// pet 객체 정의, 이름과 다리의 개수를 전달한다.
							var pet = function (name, legs) {

								// 객체 리터럴(that)을 만들되, 공개(public) 속성인 name과
								// getDetails() 함수를 포함시킨다. 다리의 개수는 비공개(private) 속성으로 남겨둔다.
								// 여기서 정의된 지역 변수나 pet 함수에 전달된 인수는 비공개 속성으로 남게 되지만, 
								// 아래에 정의된 함수에서는 계속 접근할 수 있다.
								var that = {
									name : name,
									getDetails : function () {
										return that.name + ' has ' + legs + ' legs';
									} 
								};
								return that;
							};

							// pet을 상속받아 cat 객체를 정의한다.
							var cat = function (name) {
								var that = pet(name, 4);
								that.action = function () {
									return '새를 잡는다.';
								};
								return that;
							};

							// petCat2에 cat의 인스턴스를 만든다.
							var petCat2 = cat('Felix');

							var details = petCat2.getDetails();
							console.log(details);	// Felix has 4 legs
							var action = petCat2.action();
							console.log(action);	// 새를 잡는다. 
							petCat2.name = 'Sylvester';	// 이름(name)을 바꿀 수 있다.
							console.log(petCat.name);	//Sylvester 
							petCat2.legs = 7;	// 하지만, 다리의 개수(legs)는 바꿀 수 없다.
							console.log(petCat2.legs);	//7
							details = petCat2.getDetails();
							console.log(details);	//Sylvester has 4 legs 						
						

여기에는 우스꽝스러운 prototype도 없고 모든 것이 훌륭하게 캡슐화되어 있다. 더 중요한 사실은 legs 변수가 비공개 속성이라는 점이다. cat 밖에서는 존재하지 않는 공개 legs 속성을 바꾸어봤자 아무 소용도 없다. 진짜 legs 값은 pet의 getDetails() 메소드가 만든 클로저에 안전하게 남아있다. 클로저는 함수 실행이 종류된 뒤에도 함수의 지역 변수를 보존한다.

사실 자바스크립트로 상속하는 방법에는 왕도가 따로 있는 것은 아니다. 개인적으로는 함수형 상속이 자바스크립트에 있어서는 훨씬 자연스로운 방법이라고 생각하지만, 여러분과 여러분이 만들 프로그램에서는 다른 방법을 사용할 수도 있다. 구글에서 "Javascript inheritance(자바스크립트 상속)"를 검색하면 다른 자료도 볼 수 있다.


프로토타입형 상속을 사용하는 것의 장점 중 하나는 메모리를 효율적으로 사용한다는 것이다. 어떤 객체의 prototype 속성과 메소드는 몇 번을 상속하든 상관없이 한 번만 저장된다.

함수형 상속에는 이런 장점이 없다. 새로운 인스턴스를 만들 때마다 매번 중복되는 속성과 메소드를 만들어야 한다. 따라서 덩치가 큰 객체의 인스턴스를 많이 만들 때는 메모리 소비를 염두에 두어야 한다. 해결책으로는 큰 속성과 메소드를 한 객체에 저장해두고 이를 생성자 함수에 인수로 전달하는 방법도 있다. 인스턴스를 매번 처음부터 새로 만드는 대신 한 객체의 리소스를 활용하도록 하는 것이다.