
Class
ES6에서 도입되었지만 기존 프로토타입 기반 객체지향 모델을 폐지한 것은 아니다. 사실 클래스도 함수이고, 클래스와 생성자 함수 모두 프로토타입 기반 인스턴스를 생성한다. 이는 새로운 객체 생성 메커니즘으로 보는 것이 더 알맞다.
객체: 속성으로 여러 개 값을 단위 하나로 구성한 복합적인 자료구조
객체의 집합으로 프로그램을 표현하려는 프로그래밍 패러다임
실세계의 실체(사물이나 개념)를 인식하는 철학적 사고를 프로그래밍에 접목하려는 시도
실체를 구별할 수 있는 특징이나 성질을 나타내는 속성으로 실체를 인식하고 구별
const person = {
name: 'Leona',
age: 30,
}
console.log(person); // {name: 'Leona', age: 30}
레오나를 인식하고 구별할 수 있는 속성은 많으나 그 중에 이름과 나이만 추려 표현한 것을 추상화라고 한다.
또한 객체는 상태(프로퍼티)와 동작(메서드)을 논리적인 단위 하나로 묶은 복합적인 자료구조이다.
const circle = {
// 반지름
radius: 5,
// 원의 지름
getDiameter() {
return 2 * this.radius;
},
// 원의 둘레, 너비 등...
...
}
// Quiz
console.log(circle.radius); // ???
console.log(circle.getDiameter()); // ???
상속은 객체지향 프로그래밍의 핵심 개념. 어떤 객체의 프로퍼티나 메서드를 다른 객체가 상속받아 그대로 사용할 수 있는 것을 의미
자바스크립트는 프로토타입 기반으로 상속을 구현해서 불필요한 중복 제거 = 기존 코드 재사용
코드를 재사용하면 개발 비용을 현저히 줄일 수 있다.
// 생성자 함수
function Circle(radius) {
this.radius = radius;
this.getArea = function () {
return Math.PI * this.radius ** 2;
}
}
// Circle 인스턴스
const circle1 = new Circle(1);
const circle2 = new Circle(2);
// 모든 인스턴스가 중복으로 갖고 있는 메서드라 false
console.log(circle1.getArea === circle2.getArea); // false
// 각 Circle 인스턴스 값은 다를지라도 getArea 메서드는 동일한 내용이다.
console.log(circle1.getArea());
console.log(circle2.getArea());
프로토타입 상속
// 생성자 함수
function Circle(radius) {
this.radius = radius;
}
Circle.prototype.getArea = function () {
return Math.PI * this.radius ** 2;
}
// 부모 객체의 프로토타입 Circle.prototype으로부터 getArea를 상속받았기 때문에 true
console.log(circle1.getArea === circle2.getArea); // true
// 각 Circle 인스턴스가 getArea 메서드를 공유한다.
console.log(circle1.getArea());
console.log(circle2.getArea());
Circle 생성자 함수가 생성한 모드 인스턴스는 부모 역할을 하는 자신의 프로토타입의 모든 프로퍼티와 메서드를 상속받는다.(Circle.prototype.getArea)
모든 인스턴스가 공통적으로 사용할 프로퍼티나 메서드를 프로토타입에 미리 구현해 두면 인스턴스는 별도로 구현할 필요없이 상위 객체(프로토타입) 자산을 공유해서 사용할 수 있다.
_ _ proto _ _는 접근자 프로퍼티다
모든 객체는 _ _ proto _ _ 접근자 프로퍼티로 자신의 프로토타입([[Prototype]] 슬롯)에 간접적으로 접근할 수 있다.

접근자 프로퍼티는 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수([[Get]], [[Set]] 프로퍼티 어트리뷰트로 구성된 함수)로 구성된 프로퍼티

const obj = {};
const parent = {x: 1};
obj.__proto__; // get
obj.__proto__ = parent; // set
console.log(obj.x); // 1
_ _ proto _ _접근자 프로퍼티는 상속을 통해 사용된다.
Object.prototype
모든 객체는 프로토타입 계층 구조인 프로토타입 체인에 묶여있다. 자바스크립트 엔진은 객체의 프로퍼티(+메서드)에 접근하려고 할 때 해당 객체에 프로퍼티가 없다면 _ _ proto _ _ 접근자 프로퍼티가 가리키는 참조를 따라 자신의 부모 프로토타입의 프로퍼티를 순차적으로 검색한다.
프로토타입 최상위 객체는 Object.prototype이며, 이 프로퍼티와 메서드는 모든 객체에 상속된다.
_ _ proto _ _접근자 프로퍼티를 통해 프로토타입에 접근하는 이유
서로가 자신의 프로토타입이 되는 비정상적인 프로토타입 체인을 방지하기 위함이다.
const parent = {};
const child = {};
child.__proto__ = parent;
parent.__proto__ = child; // TypeError: Cyclic __proto_ value
프로토타입 체인은 단방향 링크드 리스트로 구현되어야 한다.(프로퍼티 검색 방향이 한쪽 방향으로만 흘러야 한다)
단방향 링드크 리스트: 각 노드가 데이터와 포인터를 가지고 한 줄로 연결되어 있는 방식으로 데이터를 저장하는 자료 구조
_ _ proto _ _접근자를 코드 내에서 직접 사용하는 것은 권장하지 않는다.
모든 객체가 _ _ proto _ _ 접근자 프로퍼티를 사용할 수 있는 것은 아니다.
직접 상속을 통해 Object.prototype을 상속받지 않는 객체를 생성할 수도 있기 때문에 _ _ proto _ _ 접근자 프로퍼티를 사용할 수 없는 경우도 있다.
// 프로토타입 체인의 종점이기 때문에 Object.__proto__를 상속받을 수 없다.
const obj = Object.create(null);
const parent = {x: 1};
console.log(obj.__proto__); // undefined
// 프로토타입을 참조할 때
console.log(Object.getPrototypeOf(obj)); // null
// 프로토타입을 교체할 때
Object.setPrototypOf(obj, parent); // obj.__proto__ = parent;
함수 객체만이 소유하는 prototype 프로퍼티는 생성자 함수가 생성할 인스턴스의 프로토타입을 가리킨다.
// prototype 프로퍼티를 소유하는 함수 객체
(function () {}).hasOwnProperty('prototype'); // true
// 일반 객체는 소유하지 않는다.
({}).hasOwnProperty('prototype'); // false
| 구분 | 소유 | 값 | 사용 주체 | 사용 목적 |
|---|---|---|---|---|
| __proto__ 접근자 프로퍼티 |
모든 객체 | 프로토타입 참조 | 모든 객체 | 객체가 자신의 프로토타입에 접근 또는 교체하기 위함 |
| prototype 프로퍼티 |
생성자 | 프로토타입 참조 | 생성자 함수 | 생성자 함수가 생성할 객체(인스턴스)의 프로토타입을 할당하기 위함 |
모든 프로토타입은 constructor 프로퍼티를 갖는데, 이 프로퍼티는 prototype 프로퍼티로 자신을 참조하고 있는 생성자 함수를 가리킨다.
생성자 함수가 생성될 때(함수 객체가 생성될 때) 연결된다.
// 생성자 함수
function Person(name) {
this.name = name;
}
const me = new Person('Leona');
console.log(me.constructor === Person); // true
'me' 객체는 constructor 프로퍼티가 없어도 생성자인 Person이 constructor 프로퍼티를 가지고 있기 때문에 이를 상속받아 constructor 프로퍼티를 사용할 수 있는 것이다.
// 생성자 함수로 생성한 객체가 아닌 객체 리터럴
const obj = {};
// 그러나 obj의 생성자 함수는 Object 생성자 함수다.
console.log(obj.constructor === Object); // true결론
1.프로토타입과 생성자 함수는 단독으로 존재할 수 없고 언제나 쌍으로 존재한다.
2.리터럴 표기법으로 생성한 객체도 가상적인 생성자 함수를 갖는다.
3.생성자 함수로 생성한 객체나 리터럴 표기법으로 생성한 객체나 본질적인 차이는 없다.
함수 선언문은 런타임 이전에 자바스크립트 엔진이 먼저 실행한다.
함수 선언문으로 정의한 생성자 함수는 어떤 코드보다 먼저 평가되어 함수 객체가 된다. 이때 프로토타입도 함께 생성된다.
이렇게 생성된 프로토타입은 생성자 함수의 prototype 프로퍼티에 바인딩된다.
console.log(Person.prototype);
// 생성자 함수
function Person(name) {
this.name = name;
}

객체가 생성되기 전 이미 생성자 함수와 프로토타입이 객체화되어 존재하기 때문에 생성자 함수 또는 리터럴 표기법으로 객체를 생성하면 프로토타입은 생성된 객체의 [[Prototype]] 내부 슬롯에 할당된다. 이렇게 생성된 객체는 프로토타입을 상속받는다.



// Object.prototype의 메서드로, 프로토타입 체인을 따라 hasOwnProperty 메서드를 검색해서 사용한다.
me.hasOwnProperty('name') // trueObject.prototype.hasOwnProperty.call(me, 'name');// me 식별자는 전역에 선언되었으므로 전역 스코프에서 검색된다.
// me 식별자 검색 -> me 객체 프로토타입 체인에서 hasOwnProperty 메서드 검색
me.hasOwnProperty('name');스코프 체인과 프로토타입 체인은 서로 협력하며 식별자와 프로퍼티를 검색하는 데 사용된다.