객체를 상속하기 위하여 Javascript에서 사용하는 방식
프로토타입(Prototype)
은 사전적의미로 원형을 뜻하며 실제 제품을 만들기 전 대략적인 형태, 또는 집합체안 비슷한 특징끼리 모아 형태를 만든 것이다.
자바스크립트에서는 Prototype
을 활용하여 다양한 객체들간 비슷한 특징들을 하나의 프로토타입으로 만들어 객체지향을 구현한다.
그래서 자바스크립트는 Prototype
기반의 객체지향 프로그래밍 언어라고 불린다.
자바스크립트에서 객체는 Object
라는 공통의 Prototype
을 포함하고 있다.
Object
를 통해 객체가 가지고 있는 공통적인 속성들을 Prototype
으로부터 상속받는다.
즉 모든 객체들은 단 하나의 Object Prototype
을 상속한다.
또한 배열도 Object Prototype
을 상속받은 Array Prototype
을 상속받는다. 이를 통해 객체간 프로토타입 체인을 연결한다.
외부에서는 Prototype
에 직접 접근이 불가능하다.
그래서 아래와 같은 방법으로 Prototype에 접근한다.
__proto__
Object.getPrototypeOf()
Object.setPrototypeOf()
생성자 함수에서의 prototype접근
const obj1 = {};
const obj2 = {};
console.log(obj1.__proto__ === obj2.__proto__); // true
주어진 객체 자신의 속성에 대한 속성 설명자
디스크립터(Descriptor)
는 스코프의 렉시컬환경처럼 오브젝트의 상태를 가지고 있다.
각각의 오브젝트는 프로퍼티는 프로퍼티 디스크립터
라고 하는 객체로 저장된다.
value
: 키에 해당하는 값
writable
: 값의 수정여부
enumerable
: 값의 열거여부 (iterator 여부)
configurable
: 값에 해당하는 속성들을 수정, 삭제 여부
모든 속성의 기본값은 true
이다.
const dog = { name: '와우', age: 2};
// 1. 모든 속성값 출력
const descriptors = Object.getOwnPropertyDescriptors(dog);
console.log(descriptors);
// { name: { value: '와우', writable: true, enumerable: true, configurable: true },
// age: { value: 2, writable: true, enumerable: true, configurable: true } }
// 2. 특정 속성값 출력
const desc = Object.getOwnPropertyDescriptor(dog, 'name');
console.log(desc);
// { value: '와우', writable: true, enumerable: true, configurable: true }
// 3. 특정 속성값 정의 및 수정
Object.defineProperty(dog, 'name', {
value: '멍멍',
writable: false,
enumerable: false,
configurable: false,
})
console.log(dog.name); // 멍멍
console.log(Object.keys(dog)); // [ 'age' ]
delete dog.name;
console.log(dog.name); // 멍멍
// 4. 모든 속성값 정의 및 수정
const student = {};
Object.defineProperties(student, {
firstName: {
value: '영희',
writable: true,
enumerable: true,
configurable: true,
},
lastName: {
value: '김',
writable: true,
enumerable: true,
configurable: true,
},
fullName: {
get() {
return `${lastName} ${firstName}`;
},
set(name) {
[this.lastName, this.firstName] = name.split(' ');
},
configurable: true,
},
})
console.log(student); // { firstName: '영희', lastName: '김' }
Object.keys
, values
, entries
: 객체의 키, 값, 혹은 둘다 가져오는 것const dog = { name: '와우', age: 2};
console.log(Object.keys(dog)); // [ 'name', 'age' ]
console.log(Object.values(dog)); // [ '와우', 2 ]
console.log(Object.entries(dog)); // [ [ 'name', '와우' ], [ 'age', 2 ] ]
in연산자
, Object.hasOwnProperty()
: 특정 오브젝트의 해당 키 존재 유무를 확인const dog = { name: '와우', age: 2};
console.log('name' in dog); // true
console.log(dog.hasOwnProperty('name')); // true
더 이상 변경될 수 없게 객체를 동결
Object.freeze()
: 객체를 동결하여 추가, 삭제, 쓰기, 속성 재정의를 금지 (단, 얕은 복사만 해당)const mirrer = { name: 'mirrer' };
const dog = { name: '와우', age: 2, owner: mirrer };
Object.freeze(dog);
dog.name = '멍멍';
console.log(dog.name); // 와우
dog.color = 'brown';
console.log(dog.color); // undefined
delete dog.name;
console.log(dog.name); // 와우
mirrer.name = 'mirrer2';
console.log(dog.owner.name); // mirrer2
Object.seal()
: 객체를 밀봉하여 추가, 삭제, 속성 재정의를 금지 (단 값의 수정은 가능)// const cat = Object.assign({}, dog);
const cat = { ...dog };
Object.seal(cat);
cat.name = '냐옹';
console.log(cat.name); // 냐옹
delete cat.age;
console.log(cat.age); // 2
Object.isFrozen
, isSealed
, isExtensible
: 객체의 동결, 밀봉 여부 확인const mirrer = { name: 'mirrer' };
const dog = { name: '와우', age: 2, owner: mirrer };
Object.freeze(dog);
const cat = { ...dog };
Object.seal(cat);
console.log(Object.isFrozen(dog)); // true
console.log(Object.isSealed(cat)); // true
Object.preventExtensions
: 객체의 확장, 추가 금지const tiger = { name: '어흥' };
Object.preventExtensions(tiger);
console.log(Object.isExtensible(tiger)); // false
tiger.name = '어흐응';
console.log(tiger.name); // 어흐응
delete tiger.name;
console.log(tiger.name); // undefined
tiger.age = 2;
console.log(tiger.age); // undefined
Object.create
,call
함수를 이용하여 프로토타입 상속을 구현
프로토타입을 베이스로한 객체지향 프로그래밍에서 상속은 다음과 같이 구현할 수 있다.
상속받을 객체의 Prototype
을 Object.create
함수로 부모와 연결
상속받을 객체의 생성자함수를 Object.call
함수로 부모의 생성자 함수와 연결
// 부모 Animal객체
function Animal(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.print = function () {
console.log(`${this.name} ${this.color}`);
}
// 자식 Dog 객체
function Dog(name, color, owner) {
// super(name, color);
Animal.call(this, name, color);
this.owner = owner;
}
// Dog.prototype = Object.create(Object.prototype);
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.play = () => {
console.log('같이 놀자옹!!');
}
// 자식 Tiger 객체
function Tiger(name, color) {
Animal.call(this, name, color);
}
Tiger.prototype = Object.create(Animal.prototype);
Tiger.prototype.hunt = () => {
console.log('사냥하자!!');
}
const dog1 = new Dog('멍멍', 'brown', 'mirrer');
dog1.play(); // 같이 놀자옹!!
dog1.print(); // 멍멍 brown
const tiger1 = new Tiger('어흥', 'yellow');
tiger1.print(); // 어흥 yellow
tiger1.hunt(); // 사냥하자!!
instanceof
연산자를 사용하여 상속관계를 확인할 수 있다.
instanceof
연산자는 상속관계 여부를 Boolean
으로 반환한다.
// 부모 Animal객체
function Animal(name, color) {
this.name = name;
this.color = color;
}
Animal.prototype.print = function () {
console.log(`${this.name} ${this.color}`);
}
// 자식 Dog 객체
function Dog(name, color, owner) {
Animal.call(this, name, color);
this.owner = owner;
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.play = () => {
console.log('같이 놀자옹!!');
}
// 자식 Tiger 객체
function Tiger(name, color) {
Animal.call(this, name, color);
}
Tiger.prototype = Object.create(Animal.prototype);
Tiger.prototype.hunt = () => {
console.log('사냥하자!!');
}
const dog1 = new Dog('멍멍', 'brown', 'mirrer');
dog1.play();
dog1.print();
const tiger1 = new Tiger('어흥', 'yellow');
tiger1.print();
tiger1.hunt();
console.log(dog1 instanceof Animal); // true
console.log(dog1 instanceof Dog); // true
console.log(dog1 instanceof Tiger); // false
오브젝트는 단 하나의 prototype
만을 가리킬 수 있다.
하지만 여러개의 함수들을 상속받아 사용하고 싶다면 Object.assign
를 사용하여 객체의 프로토타입에 사용할 함수들을 복사할 수 있다.
const play = {
play: function () {
console.log(`${this.name} 놀아요!`);
}
}
const sleep = {
sleep: function () {
console.log(`${this.name} 자요ZzZ`);
}
}
function Dog(name) {
this.name = name;
}
Object.assign(Dog.prototype, play, sleep);
const dog = new Dog('멍멍');
console.log(dog); // Dog { name: '멍멍' }
dog.play(); // 멍멍 놀아요!
dog.sleep(); // 멍멍 자요ZzZ
Object.assign
은 프로토타입뿐만 아니라 클래스에서도 사용가능
const play = {
play: function () {
console.log(`${this.name} 놀아요!`);
}
}
const sleep = {
sleep: function () {
console.log(`${this.name} 자요ZzZ`);
}
}
class Animal {}
class Tiger extends Animal {
constructor(name) {
super();
this.name = name;
}
}
Object.assign(Tiger.prototype, play, sleep);
const tiger = new Tiger('어흥!');
console.log(tiger); // Tiger { name: '어흥!' }
tiger.play(); // 어흥! 놀아요!
tiger.sleep(); // 어흥! 자요ZzZ
Object prototypes - Web 개발 학습하기 | MDN
모던 자바스크립트 Deep Dive
모던 JavaScript 튜토리얼