개발계발/Javascript

자바스크립트 prototype(프로토타입)

냥냥친구 2019. 7. 28. 14:46

 자바스크립트는 객체지향 언어입니다. 그러나 클래스가 없는 프로토타입 기반의 객체지향 언어입니다.

클래스가 없는 대신 자바스크립트 객체(array, object, 함수)의 특정 속성은 부모 클래스 역할을 하는 객체를 참조하는데, 그 객체가 프로토타입입니다. 프로토타입 객체를 사용해 상속의 개념인 부모 객체의 속성 접근이나 메서드 호출이 가능합니다.

프로토타입의 원리와 특징을 함수 객체위주로 살펴보도록 하겠습니다.

프로토타입

 객체가 생성되면 프로토타입 객체도 함께 생성됩니다. 함수 객체의 경우 함수의 prototype 속성이 함수와 함께 생성된 프로토타입 객체를 참조합니다. 반면에 생성자 함수로 정의된 객체는 __proto__ 속성을 갖습니다. __proto__ 속성도 프로토타입 객체를 참조합니다. 자세한 내용은 아래 예제로 살펴보겠습니다.

//Person 생성자 함수
function Person(name){
	this.name = name;
}

//foo 객체 생성
var foo = new Person('foo');

console.dir(foo);
console.dir(Person);

코드를 살펴보면, Person()는 함수 객체이므로 prototype 속성을 가지며 해당 속성이 Person() 함수 정의 시 함께 생성된 Person의 프로토타입 객체를 참조하고 있습니다. 다음으로 foo는 Person() 생성자 함수로부터 생성된 객체이며 __proto__ 속성을 가집니다. __proto__ 속성도 Person() 함수의 프로토타입 객체를 참조하고 있습니다. 객체의 속성을 확인할 수 있는 console.dir을 출력해 보면 아래와 같습니다.

console.dir(foo)

*__proto__프로퍼티가 파이어폭스에서는 <prototype>으로 표기되었습니다.

 foo 객체를 살펴보면, __proto__ 속성이 무엇을 가리키는지 명시되지 않았지만 Person의 프로토타입 객체를 참조하고 있습니다. 이 내용은 아래에서 다시 살펴보겠습니다. 그리고 프로토타입 객체는 constructor, __proto__속성을 갖고 있으며 constructor 속성의 경우 Person() 함수를 참조하고 있습니다.

console.dir(Person)

*__proto__프로퍼티가 파이어폭스에서는 <prototype>으로 표기되었습니다.

Person을 살펴보면 prototype 속성을 갖고 있고 prototype속성이 foo 객체의 __proto__와 마찬가지로 Person의 프로토타입 객체를 참조하고 있습니다.

 

명시되지는 않았지만 foo의 __proto__와 Person의 prototype 속성 모두 Person의 프로토타입 객체를 참조합니다. 이는 constructor 속성을 통해 알 수 있습니다. constructor 속성은 프로토타입 객체에 존재하며 내가 어느 함수로부터 파생되었는지 알 수 있게 해 줍니다. constructor 속성이 Person()이므로 Person의 prototype 객체임을 알 수 있습니다.

 정리하면 함수의 prototype은 프로토타입 객체를 참조하고 프로토타입 객체의 consturctor는 함수를 참조하는, 서로가 서로를 참조하고 있는 것입니다.

프로토타입 체이닝

프로토타입 체이닝이란 자바스크립트에서 특정 객체의 속성이나 메서드에 접근하려고 할 때, 해당 객체에 없는 속성, 메서드라면 프로토타입 객체의 속성을 차례대로 검색하는 것을 의미합니다.

아래 코드로 살펴보겠습니다.

var myObject = {
	name : 'foo',
    sayName : function(){
    	console.log('My Name is ' + this.name);
    }
};


myObject.sayName();
console.log(myObject.hasOwnProperty('name'));
console.log(myObject.hasOwnProperty('nickName'));
myObject.sayNickName();

//result
//My name is foo
//true
//false
//TypeError: myObject.sayNickName is not a function

myObject는 name속성과 sayName() 메서드를 가진 객체입니다. 그래서 sayName()은 리턴 값이 있지만 sayNickName() 메서드는 myObject에 없기 때문에 에러가 출력되었습니다. 그런데 hasOwnProperty는 myObject에 정의되어있지 않은 메서드임에도 불구하고 정상출력되고 있습니다. 해당 메서드는 호출한 객체에 프로퍼티나 메서드가 있는지 체크하는 자바스크립트 표준 API함수입니다.

 

이와 같은 메서드 호출이 가능한 이유를 설명하면 프로토타입 객체에 hasOwnProperty메서드가 있기 때문입니다. myObject처럼 객체 리터럴 방식으로 생성한 객체는  Object()라는 생성자 함수로 생성된 것입니다. Object() 함수도 함수 객체이므로 프로토타입 객체가 함께 만들어졌으며 Object.prototype이 프로토타입 객체를 참조하는 것입니다.

 

자바스크립트에서 Object.prototype 객체는 프로토타입 체이닝의 종점입니다. 호출한 메서드나 속성이 Object.prototype 객체에도 없다면 없는 메서드, 속성으로 취급하여 에러가 나는 것입니다. 객체는 결국엔 Object.prototype에서 프로토타입 체이닝이 끝이 납니다. 달리 말하면 모든 자바스크립트 객체는 Object.prototype 객체가 가진 속성과 메서드에 접근 가능하다는 것을 의미합니다.

 

추가적으로 프로토타입 객체 역시 객체이므로 메서드를 추가하거나 삭제하는 것이 가능합니다. 그리고 변경된 속성은 실시간으로 프로토타입 체이닝에 반영됩니다. 아래 예제를 살펴보겠습니다.

function Person(name){
	this.name = name;
};

var foo = new Person('foo');

foo.sayHello();   #sayHello 메서드가 없어서 error

Person.prototype.sayHello = function(){
	console.log('Hello');
}

foo.sayHello(); #Hello

Person 함수 객체에는 sayHello 메서드가 없어서 호출 시 에러가 나지만 prototype 객체에 sayHello 메서드를 추가함으로써 정상 리턴되는 것을 확인할 수 있습니다.