파란하늘의 지식창고
반응형
prototype 속성은 객체에 새로운 속성이나 메소드를 추가할 때 사용한다.
그냥 추가하면 되는데 왜 prototype이란 개념이 있을까?

'함수의 호출 - 3. 생성자 호출 패턴'에서 사용한 호출 구문을 보자.
var A = function(string) {
	this.aValue = string;
	this.getValue = function() {
		return this.aValue;
	}
}
var a = new A("a의 값");
document.write(a.getValue());
예제1.

생성자 A는 aValue라는 속성과, getValue라는 메소드를 가지고 있다.
위에서 선언한 A 생성자를 prototype을 이용하면 다음과 같다.
var A = function(string) {
	this.aValue = string;
}
A.prototype.getValue = function() {
	return this.aValue;
}
var a = new A("a의 값");
document.write(a.getValue());
예제2.

객체를 생성하여 호출하여 사용하는데 다른 점이 없다.
동일하게 객체를 만들 수 있고, 또한 메소드를 호출하여 사용할 수 있다.

하지만 실제 동작하는 데이는 큰 차이가 있다.
함수를 new (전치)연산자를 사용하여 호출하면 호출한 함수의 prototype 속성의 값에 연결되는 (숨겨진) 링크를 가진 객체가 생성되며 새로운 객체는 this에 바인딩 된다.

예제1의 경우 new 연산자와 함께 함수를 호출하는 순간 객체가 생성이 되고, 그 객체의 값으로 aValue라는 property와 getValue라는 메소드가 담겨지게 된다.
만약 해당 구문을 이용하여 여러 개의 객체를 생성하면 객체마다 해당 값을 가지게 되며 각 객체마다 aValue와 getValue 메소드에 대한 메모리가 할당이된다.
100개의 객체가 생성되면 각각 100개의 aValue와 getValue가 들어있는 객체가 만들어지는 것이다.
함수를 오버라이트 해서 쓰는 것은 좋은 사용은 아니지만 다만 예를 들어보기 위해 다음과 같이 호출하였다.
var A = function() {
	this.getValue = function() {
		return "처음 값";
	}
}

var a = new A();
document.writeln(a.getValue());	//결과 : "처음 값"
var A = function() {
	this.getValue = function() {
		return "재정의한 값";
	}
}
document.writeln(a.getValue());	//결과 : "처음 값"
A라는 생성자가 정의 된 이후 변수 a에 new 연산자를 통해 생성된 객체가 만들어졌고, getValue 메소드를 호출하자 담겨져 있던 "처음 값"이 호출되었다.
다시 재정의를 하고 getValue를 호출하더라도 이미 a라는 객체에는 처음 정의된 A 생성자의 값이 저장되어 있기 때문에 별다른 영향을 미치지 못한다.
재정의 된 값을 가져오기 위해서는 다시 한번 new A()를 통해 객체를 생성해서 담아야 한다.

예제2의 경우 aValue property는 각각의 객체마다 메모리가 생성이 되지만 getValue 메소드는 경우가 다르다.
숨겨진 prototype 속성의 값에 연결되는 링크가 생성자에 있다는 것만 알고 있을 뿐 실제로 getValue 메소드가 해당 객체에 담겨있지는 않다.
var B = function() {}
B.prototype.getValue = function() {
	return "처음 값";
}
var b = new B();
document.writeln(b.getValue());	//결과 : "처음 값"
B.prototype.getValue = function() {
	return "재정의한 값";
}
document.writeln(b.getValue());	//결과 : "재정의한 값"
B라는 생성자에 연결된 getValue 메소드의 값을 재정의 하고 호출하자 해당 변경사항이 기존에 생성되어 있던 b라는 객체에 바로 적용이 된다.
즉 prototype으로 선언된 속성과 메소드는 그 내용를 해당 함수에 prototype으로 연결되어 있는 상태일 뿐이며 생성자의 속성이나 메소드는 아니다.

이러한 prototype의 특징으로 인해 prototype으로 정의된 속성과 메소드와 실제 해당 생성자의 속성과 메소드를 구분하기 위해 JavaScript는 hasOwnProperty라는 메소드를 제공한다.
var A = function() {
	this.getValue = function() {
		return "처음 값";
	}
}
var a = new A();
document.writeln(a.hasOwnProperty("getValue"));	//결과 : true

var B = function() {}
B.prototype.getValue = function() {
	return "처음 값";
}
document.writeln(b.hasOwnProperty("getValue"));	//결과 : false
객체 리터럴로 생성되는 객체는 Object.prototype에 연결되며, 함수 객체는 Function.prototype에 연결된다.
또한 Function은 Object.property에 연결이 된다.

prototype은 해당 객체에 연결만 하기 때문에 객체를 apply나 call 메소드를 통해 상속받은 객체에서는 상위 객체의 prototype으로 연결된 속성이나 메소드를 사용할 수 없다.
var A = function(name) {
	this.name = name;
	this.getName = function() {
		return "이 객체의 이름은 " + this.name + " 이다.";
	}
}

var ExtendA = function(name) {
	A.apply(this, new Array(name));
}
var ex_a = new ExtendA("A의 확장 객체");
document.writeln(ex_a.getName());	//상속받은 getName의 사용이 가능

var B = function(name) {
	this.name = name;
}
B.prototype.getName = function() {
	return "이 객체의 이름은 " + this.name + " 이다.";
}

var ExtendB = function(name) {
	B.apply(this, new Array(name));
}
var ex_b = new ExtendB("B의 확장 객체");
document.writeln(ex_b.getName());	//ex_b.getName is not a function 오류 발생
어찌보면 prototype은 JAVA의 STATIC 속성과 메소드와 비슷하기도 하다.
해당 생성자가 만든 모든 객체가 prototype의 내용을 공유하니까.
반면 상속이 되지 않는다는 점은 차이가 있다.


  1. 해당 객체의 모든 인스턴스에서 사용 가능
  2. 이미 만들어진 객체에 대해서도 일괄 적용
  3. 다수의 객체를 만드는 작업에서 연결만으로 속성과 메소드 구현이 되므로 속도와 메모리 절약에 장점
위에 열거한 장점을 가진 덕분에 prototype은 쓸모가 많다.

하나의 예를 들면 새로 함수를 사용하지 않더라도 기본 타입 객체에 대해 해당 메소드의 적용이 가능하다.
String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g, '');
}
document.write("앞뒤공백자르기시작");
document.write("  공백글   ".trim());
document.write("앞뒤공백자르기끝");
문자열의 끝에 .trim() 만 붙이더라도 앞뒤공백을 자르는 메소드가 단 몇줄만에 만들어진다.
반응형
profile

파란하늘의 지식창고

@Bluesky_

내용이 유익했다면 광고 배너를 클릭 해주세요