goongoguma's blog

Prototype

  1. 프로토타입

자바와 같은 객체지향 프로그래밍에서 객체를 생성하기 위해서는 먼저 클래스를 선언해야한다. 하지만 자바스크립트에서는 이러한 클래스 개념이 존재하지 않는다(ES6이전 기준). 대신 리터럴, 생성자 함수로 객체를 생성한다.

// 리터럴
{ name: 'kim', age: 20 }

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

var instance = new Person('kim', 20)

console.log(instance)  // { name: 'kim', age: 20 }

이렇게 생성된 객체들의 부모 객체를 프로토타입 객체라고하며, 자식 객체에서 필요한 프로퍼티나 메서드는 부모객체인 프로토타입 객체로부터 상속받아 사용한다. 자바스크립트의 모든 객체는 자신의 부모 객체(프로토타입 객체)를 가리키는 참조 형태의 프로퍼티인 암묵적 프로토타입 링크를 가지고 있으며 이런 링크는 __proto__프로퍼티에 저장된다. 즉, 자바스크립트에서 객체가 생성되는 규칙은: 자바스크립트의 모든 객체는 자신을 생성한 함수의 prototype 프로퍼티가 가리키는 프로토타입 객체를 자신의 부모 객체로 설정하는 __proto__링크로 연결하며 결국에 실제 부모 역할을 하는 건 해당 객체를 생성한 생성자 함수가 아니라 생성자 함수가 prototype 프로퍼티로 가리키는 프로토타입 객체이다.

객체 리터럴을 이용해서 생성한 객체는 생성자 함수가 없는것 같지만 자바스크립트의 객체 메서드나 프로퍼티를 사용할 수 있다. 왜냐하면 객체 리터럴은 Object()라는 내장 생성자 함수로 생성되었기 때문이다.

함수가 생성될때, 자신의 프로토타입 프로퍼티에 연결되는 프로토타입 객체는 생성자(constructor) 프로퍼티만을 가진 객체이며 일반 객체처럼 동적으로 프로퍼티를 추가/삭제 가능하다.

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

var instance = new Person('kim', 20);

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

instance.sayHello() // hello kim

프로토타입 객체는 함수가 생성될 때 같이 생성되는데 해당 함수와 연결되는 디폴트 프로토타입 객체를 다른 일반 객체로 변경하는 것이 가능하다. 만일 생성자 함수의 프로토타입 객체가 변경이 되면, 변경된 시점 이후에 생성된 객체들은 변경된 프로토타입 객체로 __proto__링크가 연결된다.

function Person(name) {
  this.name = name;
}
console.log(Person.prototype.constructor); // Person(name)

var instance1 = new Person('kim');
console.log(instance1.country); // undefined

Person.prototype = {
  country: 'korea'
};
console.log(Person.prototype.constructor); // Object()
// 기존 prototype의 constructor는 존재하지 않으나 { country: 'korea' } 객체 리터럴을 생성함
// 객체 리터럴은 Object.prototype을 __proto__링크로 연결함으로 Object.prototype 객체로 프로토타입 체이닝이 발생하며 Object.prototype역시 Object() 생성자 함수와 연결된 프로토타입 객체이기 때문에 Object() 생성자 함수를 constructor 프로퍼티로 연결하고 있기에 Object()가 출력된다

var instance2 = new Person('park');

console.log(instance1.country) // undefined
console.log(instance2.country) // korea
console.log(instance1.constructor) // Person(name)
console.log(instance2.constructor) // Object()