
자바스크립트는 기본적으로 프로토타입 기반 언어이다
모든 객체는 Object.getPrototypeOf(obj)를 통해 프로토타입 체인에 접근할 수 있다
프로토타입 체인을 활용하면 객체 간 속성과 메서드를 공유할 수 있다
✔️프로토타입 체인
프로토타입 체인에 따라 속성, 메서드를 검색한다
상위에서 물려받은 객체 정보를 담은 것
= 프로토타입 상속 관계를 보여준다
정의 : 지정된 프로토타입 객체 & 속성을 새 객체를 만든다
매개변수. :
proto : 새로 만든 객체의 프로토타입이어야 할 객체
propertiesObject(선택사항)
지정되고 undefined가 아니면, 자신의 속성
(즉, 자체에 정의되어 그 프로토타입 체인에서 열거가능하지 않은 속성)이 열거 가능한 객체는 해당 속성명으로 새로 만든 객체에 추가될 속성 설명자(descriptor)를 지정한다
이러한 속성은Object.defineProperties()의 두 번째 인수에 해당한다
특정 객체를 프로토타입으로 설정해서 새 객체를 생성할 수 있다
생성자 함수를 사용하지 않고 간결한 방식으로 상속을 구현할 수 있다
상속받은 객체를 커스텀하여 확장할 수도 있습니다.
✔️proto = [[prototype]]
함수를 포함한 모든 객체가 가지고 있는 인터널 슬롯
객체 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리킨다
함수 객체 경우 → Function.prototype을 가리킨다
Object.create(null)을 사용해 __proto__가 없는 '아주 단순한 객체’를 만들거나, 맵을 사용하는게 좋다
한편, Object.create를 사용하면 객체의 얕은 복사본(shallow-copy)을 만들 수 있다
✔️얕은 복사 vs 깊은 복사
얕은 복사
- 원시 타입 → 해당 값 복사
- 객체, 배열 등 참조 타입 → 참조하는 메모리 주소 복사
최상위 속성만 복사하고 중첩된 객체 & 배열은 그대로 참조하기에 원본 데이터에 영향 줄 수 있다
깊은 복사- 객체, 배열 등 모든 속성 값을 재귀적으로 탐색해서 복사한다
- 원본 객체 & 배열, 복사본 객체 & 배열은 서로 다른 메모리를 참조하게 된다
복사본을 수정해도 원본 데이터에 영향가지 않는다const person = { greet() { console.log("Hello!"); } }; const user = Object.create(person); user.greet(); // Hello!
__proto__다소 구식이기에 더는 사용하지 않는다
↓ 아래 3 방법을 권장한다
– [[Prototype]]이 proto를 참조하는 빈 객체를 만든다. 이때 프로퍼티 설명자를 추가로 넘길 수 있다
– obj의 [[Prototype]]을 반환한다(proto getter와 같다)
– obj의 [[Prototype]]이 proto가 되도록 설정한다(proto setter와 같다)
❓Object.create 상속은 언제 사용?
가볍고, 유연적인 방식이라
객체 간 상속은 필요하는데, 생성자 함수까진 사용하지 않을 때 사용할 수 있다
하지만, 메서드 추가와 확장할 때 불편하다
→ 그래서 생성자 함수 기반 상속이 나왔다
생성자 함수 기반 상속은 new 연산자와 함께 호출하고, 프로퍼티 & 메서드를 추가할 수 있다
생성자 함수 기반 상속
ES5에서는 생성자 함수를 사용하여 상속을 구현한다
부모 객체의 생성자를 호출하여 속성을 상속받고, prototype을 조작하여 메서드를 상속한다
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
console.log(`${this.name} makes a noise.`);
};
function Dog(name) {
Animal.call(this, name); // 부모 생성자 호출
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const myDog = new Dog('Buddy');
myDog.speak(); // Buddy makes a noise.
function Car(){
this.wheel = 18;
};
Car.prototype.cc = 2000;
//Using Object.create()
var audi = new Car();
var bmw = Object.create(Car.prototype);
console.log(bmw.wheel); //undefined
console.log(bmw.cc); //2000
//Using New Keyword
console.log(audi.wheel); //18
console.log(audi.cc); //2000
Car 생성자가 실행되지 않기 때문, Object.create(Car.prototype) 사용
Object.create(Car.prototype)가 Car 생성자를 실행하지 않기 때문이다
new Car()를 호출하면 Car 생성자 함수가 실행된다
audi.proto === Car.prototype이므로 cc 속성을 프로토타입에서 찾을 수 있다
결과적으로 audi.wheel === 18, audi.cc === 2000이 출력된다
var bmw = Object.create(Car.prototype);
Object.create(Car.prototype)는 새로운 객체를 만들고 그 객체의 프로토타입을 Car.prototype으로 설정할 뿐이다
하지만 Car 생성자 함수가 실행되지 않기 때문에 this.wheel = 18;이 실행되지 않음.
즉, bmw 객체에는 wheel 속성이 없고, 프로토타입 체인(bmw.proto)을 따라가도 Car.prototype에는 wheel 속성이 없으므로 undefined가 된다
반면 Car.prototype.cc = 2000;으로 정의된 cc 속성은 bmw.proto에 존재하므로 bmw.cc === 2000이 출력된다
bmw 객체도 wheel 속성을 가지게 하려면 Car 생성자를 실행해야 한다
var bmw = new Car();
console.log(bmw.wheel); // 18
var bmw = Object.create(Car.prototype);
Car.call(bmw); // 생성자 호출
console.log(bmw.wheel); // 18
ES6 Class 기반 상속
ES6에서는 class 문법이 도입되어 좀 더 직관적이게 사용할 수 있다
클래스도 결국 프로토타입 기반으로 동작
클래스 내부에서 constructor 메서드를 통해 객체 초기 상태 설정 가능
class Base {}
class Derived extends Base {}
class Base {
constructor() {}
}
class Derived extends Base {
constructor(...args) { super(..args); }
}
```
```jsx
class Base {
constructor(x, y) {
console.log(this); // Derived {}
console.log(new.target); //Derived
}
}
class Derived {}
✔️new.target
new 연산자와 함께 호출된 함수를 가리킨다
class Animal {
constructor(name) {
[this.name](http://this.name/) = name;
}
speak() {
console.log(`${this.name} makes a noise`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks.`);
}
}
const myDog = new Dog('Buddy', 'Golden Retriever');
myDog.speak(); // Buddy barks.
❓ES5의 생성자 함수 상속과 ES6 클래스 상속의 차이점
ES5 생성자 함수는 프로토타입 체인 활용, 생성자 기반으로 만드는 방식
call, Object.create, prototype 조작 등이 필요하다
→ 코드가 길어지고 가독성이 떨어진다
ES6 Class 상속은 직관적, super 사용(부모 클래스 기능 쉽게 확장 가능)
→ 하지만 이 클래스도 결국 내부적으론 프로토타입을 활용하는거라 기존 방식보단 편리하게 보이지만 근원적으로 같은 개념이다
❓프로토타입 상속, ES6 클래스 상속 중 어떤 방식이 더 좋은가?
프로토타입 상속은 자바스크립트 기본인 만큼
하지만 복잡한 코드, new 키워드 사용, this 바인딩이 어렵다는 단점이 있다
하지만 ES6 클래스는 결합성이 높기에 변경하기 어렵다
→ 계급 계층구조으로 만들어졌기에