- 자바스크립트는 프로토타입 기반의 객체지향 언어이다.
- 자바스크립트를 이루고 있는 거의 모든것은 객체이다.
1. 객체지향 프로그래밍
명령어 또는 함수의 목록으로 보는 전통적인 명령형 프로그래밍의 절차지향적 관점에서 벗어나 여러 개의 독립적 단위, 즉 객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임
- 객체는 특징이나 성질을 나타내는 속성을 가지고 있고, 이를 통해 실체를 인식하거나 구별할 수 있다.
- 다양한 속성 중에서 프로그램에 필요한 속성만 간추려 내어 표현하려는 것을 추상화 라고 한다.
- 객체는 상태 데이터와 동작을 하나의 논리적인 단위로 묶은 복합적인 자료구조이다.
2. 상속과 프로토타입
상속은 객체지향 프로그래밍의 핵심 개념으로, 어떤 객체의 프로퍼티 또는 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 말한다.
- 자바스크립트는 프로토타입을 기반으로 상속을 구현하여 불필요한 중복을 제거한다.
function Circle(radius){
this.radius = radius;
this.getArea = function{
return Math.PI * this.radius **2;
}
}
const circle1 = new Circle(1);
const circle2 = new Circle(2);
- 위 코드 중 getArea함수는 단 하나만 생성하여 모든 인스턴스가 공유하여 사용하는 것이 바람직 하지만 위 코드는 그렇지 않고 각 인스턴스가 동일한 메서드를 중복 소유하여 메모리를 낭비하고 있다.
- 상속을 이용하여 중복을 제거해준다.
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
};
const circle1 = new Circle(1);
const circle2 = new Circle(2);
- getArea 프로토타입은 Circle 생성자 함수의 prototype 프로퍼티에 바인딩 된다.
3. 프로토타입 객체
- [[Prototype]]에 저장되는 프로토타입은 객체 생성 방식에 의해 결정된다.
- 객체 리터럴에 의해 생성된 객체의 프토로타입은 Object.prototype 이다.
- 생성자 함수에 의해 생성된 객체의 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체다.
- 모든 객체는 하나의 프로토타입을 갖는다.
- 모든 프로토타입은 생성자 함수와 연결되어있다.
proto 접근자 프로퍼티
- 모든 객체는 proto 접근자 프로퍼티를 통해 자신의 프로토타입, 즉 [[Prototype]] 내부 슬롯에 간접적으로 접근할 수 있다.
-
proto 는 접근자 프로퍼티이다.
- proto 는 접근자함수 [[Get]],[[Set]]를 통해 [[Prototype]] 내부 슬롯의 값, 즉 프로토타입을 취득 하거나 할당한다.
-
proto 접근자 프로퍼티는 상속을 통해 사용된다.
- proto 접근자 프로퍼티는 객체가 직접 소유하는 프로퍼티가 아닌 Object.prototype의 프로퍼티이다.
- 모든 객체는 상속을 통해 Object.property. proto 를 사용하는 것
const person = { name: "Yim" };
console.log(person.hasOwnProperty("__proto__"));
-
proto 접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
-
proto 를 코드 내에서 직접 사용하는 것은 권장하지 않는다.
- 모든 객체가 proto 를 사용할 수 있지 않기 때문
- 따라서 프로토타입의 참조를 취득하고 싶은 경우에는 Object.getPrototypeOf(obj), 프로토타입을 교체하고 싶은 경우에는 Object.setPrototypeOf(obj, protoObj)를 사용할 것을 권장한다.
const obj = {};
const parent = { x: 1 };
console.log(Object.getPrototypeOf(obj));
console.log(obj.__proto__);
Object.setPrototypeOf(obj, parent);
console.log(Object.getPrototypeOf(obj));
함수 객체의 prototype 프로퍼티
- 함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
- 생성자 함수로서 호출할수 없는 함수(화살표함수, 축약표현 메서드)는 prototype 프로퍼티를 소유하지 않으며 프로토타입도 생성하지 않는다.
- 모든 객체가 가지고 있는 proto 접근자 프로퍼티와 함수 객체만이 가지고 있는 prototype 프로퍼티는 결국 동일한 프로토타입을 가리킨다.
프로토타입의 constructor 프로퍼티와 생성자 함수
- 모든 프로토타입은 costructor 프로퍼티를 갖는다.
- 프로토타입의 constructor 프로퍼티는 프로토타입 프로퍼티로 자신을 참조하고 있는 함수를 가리킨다.
- 이 연결은 생성자 함수가 생성될 때 이뤄진다.
function Person(name) {
this.name = name;
}
const me = new Person("Yim");
console.log(me.constructor === Person);
4. 리터럴 표기법에 의해 생성된 객체의 생성자 함수와 프로토타입
- 리터럴 표기법에 의해 생성된 객체도 물론 프로토타입이 존재한다.
- 하지만 리터럴 표기법에 의해 생성된 객체의 경우 프로토타입의 constructor 프로퍼티가 가리키는 생성자 함수가 반드시 객체를 생성한 생성자 함수라고 단정할 수는 없다.
const obj = {};
console.log(obj.constructor === Object);
- obj는 생성자 함수로 생성한 객체가 아니라 객체 리터럴에 의해 생성된 객체다. 하지만 Object 생성자와 연결되어있다.
Object 생성자 함수에 인수를 전달하지 않거나 undefined 또는 null 을 인수로 전달하면서 호출하면 내부적으로는 추상 연산 OrdinaryObjectCreate를 호출하여 Object.prototype을 프로토타입으로 갖는 빈 객체를 생성한다. (ECMAScript 19.1.1.1 -2 )
- Objecet 생성자 함수 호출과 객체 리터럴의 평가는 추상 연산 OrdinaryObjectCreate를 호출하여 빈 객체를 생성하는 점에서 동일하나 new.target의 확인이나 프로퍼티를 추가하는 처리 등 세부 내용은 다르다.
- 프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.
- 리터럴 표기법에 의해 생성된 객체는 생성자 함수에 의해 생성된 객체는 아니다.
- 하지만 큰틀에서 보면 본질적인 큰 차이는 없다.
5. 프로토타입의 생성 시점
- 프로토타입은 생성자 함수가 생성되는 시점에 더불어 생성된다. 프로토타입고 생성자 함수는 단독으로는 존재할 수 없고 언제나 쌍으로 존재하기 때문이다.
- 생성자 함수는 사용자가 직접 정의한 사용자 정의 생성자 함수와 자바스크립트가 제공하는 빌트인 생성자 함수로 구분할 수 있다.
사용자 정의 생성자 함수와 프로토타입 생성 시점
- 생성자 함수로서 호출할 수 있는 함수는 함수 정의가 평가되어 함수 객체를 생성하는 시점에 프로토타입도 더불어 생성된다.
console.log(Person.prototype);
function Person(name) {
this.name = name;
}
- 생성된 프로토타입은 Person 생성자 함수의 prototype 프로퍼티에 바인딩된다.
- 생성된 프로토타입은 오직 constructor 프로퍼티만을 갖는 객체이다.
- 모든 객체는 프로토타입을 가지므로 프로토타입도 프로토타입을 가진다. 이는 언제나 Object.prototype이다.
빌트인 생성자 함수와 프로토타입 생성 시점
- 빌트인 생성자 : Object, String, Number, Function,, Array, RegExp, Date, Promise 등
- 빌트인 생성자 함수도 일반함수와 마찬가지로 빌트인 생성자 함수가 생성되는 시점에 프로토타입이 생성된다.
- 모든 빌트인 함수는 전역객체가 생성되는 시점에 생성된다.
- 생성된 프로토타입은 빌트인 생성자 함수의 prototype 프로퍼티에 바인딩 된다.
- 전역 객체 : 코드가 실행되기 이전 단계에 자바스크립트 엔진에 의해 생성되는 특수한 객체, window(browser), global(server side) 객체가 된다.
- 이는 객체가 생성되기 이전에 생성자 함수와 프로토타입은 이미 객체화되어 존재함을 의미한다.
6. 객체 생성 방식과 프로토타입의 결정
- 객체를 생성하는 방법은 여러가지가 있고 각 방식마다 세부적인 객체 생성 방식의 차이는 있으나 추상 연산 OrdinaryObjectCreate에 의해 성성된다는 공통점이 있다.
- OrdinaryObjectCreate는 빈 객체를 생성한 후, 객체에 추가할 프로퍼티 목록이 인수로 전달된 경우 프로퍼티를 객체에 추가한다. 그 후 생성한 객체의 [[Prototype]] 내부 슬롯에 할당한 다음 생성한 객체를 반환한다.
객체 리터럴에 의해 생성된 객체의 프로토타입
- 엔진은 객체 리터럴을 평가하여 객체를 생성할 때 추상 연산 OrdinaryObjectCreate을 호출한다.
- 이때 추상연산 OrdinaryObjectCreate에 전달되는 프로토타입은 Object.prototype이다.
Object 생성자 함수에 의해 생성된 객체의 프로토타입
- Object 생성자 함수를 인수없이 호출하면 빈 객체가 생성된다.
- Object 생성자 함수를 호출하면 객체 리터럴과 마찬가지로 추상 연산 OrdinaryObjectCreate가 호출된다.
- 이때 추상연산 OrdinaryObjectCreate에 전달되는 프로토타입은 Object.prototype이다.
- 객체 리터럴과 Object 생성자 함수에 의한 객체 생성 방식의 차이는 프로퍼티를 추가하는 방식에 있다.
생성자 함수에 의해 생성된 객체의 프로토타입
- new 연산자와 함께 생성자 함수를 호출하여 인스턴스를 생성하면 다른 객체 생성 방식과 마찬가지로 추상 연산 OrdinaryObjectCreate가 호출된다.
- 이때 추상연산 OrdinaryObjectCreate에 전달되는 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩되어 있는 객체이다.
- 생성자 함수의 프로토타입에는 constructor 메서드만 존재한다.