-
[Javascript] 프로토타입, 개념, 상속, object.assign웹/JavaScript 2023. 1. 14. 11:25
I. 프로토타입 prototype
- 자바스크립트는 프로토타입 기반의 객체지향 프로그래밍을 지원하는 언어
- 오늘날에는 클래스가 더 널리 사용되지만, 사용하기에 따라 보다 강력하고 효율적
⭐ 자바스크립트의 모든 객체는 Prototype을 가짐
const obj = {};console.log(obj);- [[Prototype]] 펼쳐서 살펴볼 것
console.log(obj.toString());console.log(obj.valueOf());- 빈 객체임에도 위의 메서드들이 사용 가능한 것은 프로토타입 때문
II. Object - 모든 것의 조상
- 아래 각 객체의 [[Prototype]]의 [[Prototype]]이 Object임 확인
- 각 [[Prototype]]의 메서드들 확인
console.log(new String(''));console.log([]);console.log(new Map());- constructor 항목에서 각각의 생성자 확인 가능
- 객체 리터럴({} 등)의 생성자는 Object()임 알 수 있음
console.log({});💡 프로토타입 체인
- 특정 객체에 호출된 프로퍼티가 없다면 프로토타입을 거슬러 올라감
- 예: Array에는 valueOf가 없지만 그 프로토타입인 Object에는 있으므로 호출 가능
II. 코드로 프로토타입에 접근하기
1. __proto__ 접근자 사용 - Object.prototype의 프로퍼티
- 👉 MDN 문서 보기
- ⚠️ Deprecated - 사라질 기능, 사용 권장되지 않음. 위의 링크 확인
console.log({}.__proto__);console.log(new Number(2).__proto__);console.log([1, 2, 3].__proto__);💡같은 종류는 프로토타입 공유 확인
console.log({}.__proto__ === { x: 1, y: 2 }.__proto__);console.log([1, 2, 3].__proto__ === [4, 5].__proto__);console.log(new String('가나다').__proto__ === new String('ABC').__proto__);💡최상위, 공통조상은 Object임 확인
console.log({}.__proto__ === [].__proto__,{}.__proto__ === new Number(0).__proto__,[].__proto__ === new Number(0).__proto__);console.log({}.__proto__ === [].__proto__.__proto__,{}.__proto__ === new Number(0).__proto__.__proto__,[].__proto__.__proto__ === new Number(0).__proto__.__proto__);// ⭐ 그 위로는 nullconsole.log({}.__proto__.__proto__);2. Object.getPrototypeOf
- 수정할 때는 Object.setPrototypeOf 사용
- ⭐ __proto__ 대신 이 기능을 사용할 것
console.log(Object.getPrototypeOf({}));console.log(Object.getPrototypeOf([]) === [].__proto__);3. ⭐ 생성자 함수에서는 prototype으로 프로토타입 접근 가능
- 즉 function으로 선언된 함수들에서
function Person (name) {this.name = name;}// 인스턴스들에 공유될 프로토타입에 다음과 같이 접근console.log(Person.prototype);const hong = new Person('홍길동');console.log(hong);- [[Prototype]]이 두 단계로 있음 확인 (Person - Object)
console.log(String.prototype);console.log(Number.prototype);console.log(Set.prototype);// 생성자 함수로 동작하지 않는 빌트인 객체console.log(Math.prototype);
IV. 인스턴스 vs 프로토타입 프로퍼티
function YalcoChicken (name, no) {this.name = name;this.no = no;this.introduce = function () {return `안녕하세요, ${this.no}호 ${this.name}점입니다!`;}}const chain1 = new YalcoChicken('판교', 3);// 본사에서 새 업무를 추가YalcoChicken.prototype.introEng = function () {return `Welcome to Yalco Chicken at ${this.name}!`;};console.log(chain1.introEng());console.log(new YalcoChicken('강남', 17).introEng());⭐ 인스턴스의 로그를 펼쳐 각 함수가 속한 레벨 확인
console.log(chain1);- introduce : 만들어지는 인스턴스마다 각각 있음
- introEng : 프로토타입에만 있음 - 메모리 절약
💡 따라서 메모리 사용을 최소화하려면 아래와 같이 작성
function YalcoChicken (name, no) {this.name = name;this.no = no;}// 공통된 요소들은 프로토타입 프로퍼티로YalcoChicken.prototype.titleEng = 'YalcoChicken';YalcoChicken.prototype.introduce = function () {return `안녕하세요, ${this.no}호 ${this.name}점입니다!`;}YalcoChicken.prototype.introEng = function () {return `Welcome to ${this.titleEng} at ${this.name}!`;};const chain1 = new YalcoChicken('판교', 3);const chain2 = new YalcoChicken('강남', 17);const chain3 = new YalcoChicken('제주', 24);console.log(chain1.introduce());console.log(chain1.introEng());// 인스턴스 레벨과 프로토타입 프로퍼티들 비교console.log(chain1, chain2, chain3);💡 프로토타입 레벨의 함수를 인스턴스 레벨에서 덮어쓰기 가능
const chain4 = new YalcoChicken('평양', 456);chain4.introduce = function () {return `어서오시라요, ${this.no}호 ${this.name}점입네다!`;}console.log(chain4.introduce());⭐ 클래스에도 적용 가능
class Dog {constructor (name) {this.name = name;}}Dog.prototype.sound = '멍멍';Dog.prototype.bark = function () { // 메서드로 만드는 것과 같음console.log(this.sound)};const badugi = new Dog('바둑이');badugi.bark();console.log(badugi);- 프로토타입 확인
function Bird (name, sound) {this.name = name;this.sound = sound;}Bird.prototype.fly = function () {console.log(`${this.name} ${this.sound} 비행중`);}function Eagle (name, sound, prey) {this.name = name;this.sound = sound;this.prey = prey;}Eagle.prototype.hunt = function () {console.log(`${this.name} ${this.prey} 사냥중`);}const bird = new Bird('새돌이', '파닥파닥');const eagle = new Eagle('독돌이', '푸드덕', '토끼');console.log(bird);console.log(eagle);bird.fly();eagle.hunt();- 프로토타입 이름 (Object로 표시) 에 크게 신경쓰지 말 것
- Eagle이 Bird로부터 상속받도록 만들려면?
I. 프로토타입으로 상속하기
⭐ Object.create 메서드
- 주어진 것을 프로토타입으로 갖는 객체를 생성
- 👉 MDN 문서 보기
function Bird (name, sound) {this.name = name;this.sound = sound;}Bird.prototype.fly = function () {console.log(`${this.name} ${this.sound} 비행중`);}function Eagle (name, sound, prey) {this.name = name;this.sound = sound;this.prey = prey;}// ⚠️ 순서 주의! 상속을 먼저 받음Eagle.prototype = Object.create(Bird.prototype);// Eagle.prototype = Bird.prototype; // 💡 비교해 볼 것// 상속 이후 자신의 프로토타입 작성Eagle.prototype.hunt = function () {console.log(`${this.name} ${this.prey} 사냥중`);}const bird = new Bird('새돌이', '파닥파닥');const eagle = new Eagle('독돌이', '푸드덕', '토끼');// 상속 구조 확인console.log(bird);console.log(eagle);console.log(eagle instanceof Bird,bird instanceof Eagle);bird.fly();eagle.fly();eagle.hunt();- 상속을 먼저 하고 자체 프로토타입 프로퍼티 입력
- Object.create... 대신 Bird.prototype 대입 결과 비교 - eagle과 bird 모두 확인
II. 부모의 생성자 활용하기
- 생성자에서 중복되는 부분 위임
- class에서는 constructor에서 super 사용
function Bird (name, sound) {this.name = name;this.sound = sound;}Bird.prototype.fly = function () {console.log(`${this.name} ${this.sound} 비행중`);}function Eagle (name, sound, prey) {// 💡 call 호출방식 사용Bird.call(this, name, sound);this.prey = prey}Eagle.prototype = Object.create(Bird.prototype);Eagle.prototype.hunt = function () {console.log(`${this.name} ${this.prey} 사냥중`);}const eagle = new Eagle('독돌이', '푸드덕', '토끼');console.log(eagle);eagle.fly();eagle.hunt();
III. 클래스로 구현
⭐ 클래스 역시 프로토타입을 기반으로 구현됨
클래스와 프로토타입
- 클래스의 메서드는 프로토타입으로 들어가게 됨
- extends - 프로토타입 상속도를 만듦
function AAA () {this.field = 1;this.run = function () { return 1; };}class BBB {field = 1;run = function () { return 1; }}class CCC {field = 1;run () { return 1; }}console.log(new AAA()); // 인스턴스에 속함console.log(new BBB()); // 인스턴스에 속함console.log(new CCC()); // 프로토타입에 속함- run 함수 또는 메서드가 속한 곳 비교
- 필드는 양쪽 모두 인스턴스에 속함
// ♻️ 새로고침 후 실행class Bird {constructor (name, sound) {this.name = name;this.sound = sound;}fly () {console.log(`${this.name} ${this.sound} 비행중`);}}class Eagle extends Bird {constructor (name, sound, prey) {super(name, sound);this.prey = prey;}hunt () {console.log(`${this.name} ${this.prey} 사냥중`);}}const bird = new Bird('새돌이', '파닥파닥');const eagle = new Eagle('독돌이', '푸드덕', '토끼');console.log(bird);console.log(eagle);bird.fly();eagle.fly();eagle.hunt();💡 인스턴스의 클래스/생성자함수 이름 출력
- 무엇의 인스턴스인지 프로그램상에서 이름으로 파악할 때 유용
console.log(Object.getPrototypeOf(bird).constructor.name,Object.getPrototypeOf(eagle).constructor.name,Object.getPrototypeOf(Object.getPrototypeOf(eagle)).constructor.name,);
IV. Mixin - Object.assign으로 조립하기
- 상속 - 한 부모로부터만 물려받음
- 믹스인 - 여럿을 조합하여 가져올 수 있음
const runner = {run : function () {console.log(`${this.name} 질주중`);}}const swimmer = {swim: function () {console.log(`${this.name} 수영중`);}}const flyer = {fly: function () {console.log(`${this.name} 비행중`);}}const hunter = {hunt: function () {console.log(`${this.name} 사냥중`);}}class Owl {constructor (name) {this.name = name;}}class FlyingFish {constructor (name) {this.name = name;}}class PolarBear {constructor (name) {this.name = name;}}Object.assign(Owl.prototype, flyer, hunter);Object.assign(FlyingFish.prototype, flyer, swimmer);Object.assign(PolarBear.prototype, runner, swimmer, hunter);const owl = new Owl('붱돌이');const f_fish = new FlyingFish('날치기');const p_bear = new PolarBear('극곰이');console.log(owl);console.log(f_fish);console.log(p_bear);owl.fly();owl.hunt();f_fish.swim();f_fish.fly();p_bear.run();p_bear.swim();p_bear.hunt();'웹 > JavaScript' 카테고리의 다른 글
[Javascript] 프로미스, 콜백, callback, promise, all, allSettled, any, race (0) 2023.01.14 [Javascript] 비동기 프로그래밍, 타임아웃, setTimeout, asychronous (0) 2023.01.14 [Javascript] this의 동적 바인딩, 정적 바인딩, call, this, apply, bind (0) 2023.01.14 [Javascript] 스코프와 바인딩, 렉시컬, 클로저 (0) 2023.01.14 [Javascript] 옵셔널 체이닝, prop, ?., 옵셔널 체이닝 연산자 (0) 2023.01.13