자바스크립트는 프로토타입 기반의 객체지향 프로그래밍 언어이다.
프로그램을 전통적 명령형 프로그래밍의 절차지향적 관점에서 벗어나 여러 개의 독립적 단위인 객체의 집합으로 프로그램을 표현하려는 패러다임
생성자 함수는 동일한 속성 혹은 메서드를 객체마다 중복 생성한다. 중복 소유는 메모리를 불필요하게 낭비하기 때문에 동일한 속성과 메서드라면 공유해서 사용하는 것이 바람직하다.
이러한 불필요한 중복을 제거하기 위해 자바스크립트는 프로토타입을 기반으로 상속을 구현한다.
프로토타입에 추가한 속성 혹은 메서드는 모든 인스턴스가 공유한다. (모든 인스턴스가 속성 혹은 메서드를 상속받아 사용할 수 있다.)
즉, 자신의 상태를 나타내는 프로퍼티만 개별적으로 소유하고 내용이 동일한 프로퍼티는 상속을 통해 공유해 사용한다.
(줄여서 프로토타입이라고 한다.)
사용 목적 : 객체가 자신의 프로포타입에 접근 또는 교체하기 위해서 사용
만약 parent 객체를 child 객체의 프로토타입으로 지정하고, child 객체를 parent 객체의 프로토타입으로 지정하면 서로가 자신의 프로토타입이 되는 비정상 프로토타입 체인이 만들어진다. 이때 프로토타입 체인 종점이 존재하지 않기 때문에 프로퍼티를 검색할 때 무한루프에 빠지게 된다. 이런 이유로 아무런 체크없이 무조건적으로 프로토타입을 교체할 수 없도록 접근자 프로퍼티를 통해 프로토타입에 접근할 수 있도록 구현되어 있다.
사용 목적 : 생성자 함수가 자신이 생성할 객체의 프로토타입을 할당하기 위해 사용
function Person() {
this.name = name;
}
const me = new Person('movie');
// Person.prototype.constructor === Person
// me.constructor == Person
// (Person.prototype === me._ _ proto _ _)
생성자 함수와 프로토타입은 객체가 생성되기 이전에 이미 객체화되어 존재한다. 객체를 생성하면 생성된 객체의 [[Prototype]] 내부 슬롯에 할당된다.
여러 객체의 생성 방식이 있고, 각 방식마다 세부적인 객체 생성 방식은 차이가 있지만 추상 연산 (OrdinaryObjectCreate)에 의해 생성된다.
OrdinaryObjectCreate
- 자신이 생성할 객체의 프로토타입을 인수로 받는다. ([[Prototype]] 내부 슬롯에 할당한다.)
- 자신이 생성할 객체의 프로퍼티 목록을 옵션으로 전달할 수 있더.
- 즉, 프로토타입은 OrdinaryObjectCreate에 전달되는 인수에 의해 결정된다. 인수는 객체가 생성되는 시점에 객체 생성 방식에 의해 결정된다.
객체의 프로퍼티에 접근하려고 할 때 해당 객체에 접근하려는 프로퍼티가 없다면 [[Prototype]] 내부 슬롯의 참조를 따라 부모 역할을 하는 프로토타입의 프로퍼티를 순차적으로 검색한다. 이를 프로토타입 체인이라고 한다.
프로토타입 프로퍼티와 같은 이름의 프로퍼티를 객체에 추가하면 프로퍼티가 오버라이딩되어 프로토타입의 프로퍼티가 가져진다. 이를 프로퍼티 섀도잉이라고 한다.
오버라이딩 : 부모가 가진 메서드를 자식이 재정의하여 사용하는 방식
in
/Reflect.has
프로토타입 체인 상에 존재하는 모든 프로토타입에서 프로퍼티를 검색한다
Object.prototype.hasOwnProperty
인스턴스의 고유 프로퍼티일 경우에만 true를 반환한다.
프로토타입의 임의의 다른 객체로 변경할 수 있다.
객체 간의 상속 관계를 동적으로 변경할 수 있다.
프로토타입 교체를 통해 객체 간의 상속 관계를 동적으로 변경하는 것은 번거롭기 때문에 직접 교체하지 않는 것이 좋다.
생성자함수.prototype = 교체할 프로토타입
prototype 프로퍼티에 다른 객체를 바인딩하는 것은 미래에 생성할 인스턴스의 프로퍼티를 변경하는 것이고 / _ proto _ 접근자 프로퍼티를 통해 프로토타입을 교체하는 것은 이미 생성된 객체의 프로토타입을 교체하는 것이다.
Object.create
를 통한 직접 상속const parent = { name: '' }
const child = Object.create(parent)
// Object.getPrototyeOf(child) === parent
특징
- new 연산자 없이도 객체를 생성할 수 있다.
- 프로토타입을 지정하면서 객체를 생성할 수 있다.
- 객체 리터럴에 의해 생성된 객체도 상속받을 수 있다.
Object.prototype의 빌트인 메서드를 객체가 직접 호출하는 것은 권장되지 않는다. (by ESLint)
왜?
Object.create를 통해 프로토타입 체인의 종점에 위치하는 객체를 생성할 수 있기 때문
const obj = Object.create(null);
obj는 Object.prototype의 빌트인 메서드를 사용할 수 없다.에러 발생 위험도를 떨어뜨리기 위해 Object.prototype 빌트인 메서드는 간접적으로 호출하는 것이 좋다.
Object.create
를 통한 직접 상속은 두번째 인자로 프로퍼티를 정의해야 하는 부분이 번거롭다.const parent = { x: 10 };
const child = {
y: 20,
_ _ proto _ _: parent
}
Object.create
for..in
이 사용된다. in
이 프로토타입의 프로퍼티까지 열거하는 반면에 for..in
을 사용했을 때는 프로토타입의 프로퍼티는 열거되지 않는 경우가 있다.for..in
은 [[Enumerable]] 값이 true인 프로퍼티를 순회하며 열거한다.
누구는 뽀개고.. 누구는 빠개고.. 프로토타입이 남아나질 않겠네요.. 🤔