728x90
최신 자바스크립트의 경우에서는 클래스를 이용한 객체지향이 가능합니다만 정확히는 자바나 다른 클래스기반의 언어와 다르게 프로토타입으로 class를 흉내낸 것입니다. 프로토타입에 대해서 공부해보는 것이 좋습니다.
const obj1 = {}
const obj2 = {}
obj1.__proto__ === obj2.__proto__ // true
모든 자바스크립트 객체들은 개별적인 오브젝트 프로토 타입을 상속하는 것이 아닌 동일한 오브젝트 타입을 상속합니다.
배열은 array라는 프로토타입을 상속하고 array도 객체이기 때문에 프로토 타입을 상속합니다.
프로퍼티 디스크립터
const dog = { name: '와우', emoji: '🐶' };
console.log(Object.keys(dog)); // ['name','emoji']
console.log(Object.values(dog)); // ['와우','🐶']
console.log(Object.entries(dog)); // [['name', '와우'],['emoji', '🐶']]
console.log('name' in dog); // true
console.log(dog.hasOwnProperty('name')); // ture
// 오브젝트의 각각의 프로퍼티는 프로퍼티 디스크립터라고 하는 객체로 저장됨
const descriptors = Object.getOwnPropertyDescriptors(dog);
console.log(descriptors); // 강아지의 모든 정보가 출력
const desc = Object.getOwnPropertyDescriptor(dog, 'name');
console.log(desc); // {value: '와우', writable: true, enumerable: true, configurable: true}
// 강아지라는 객체에 'name'키의 값들을 변경
Object.defineProperty(dog, 'name', {
value: '멍멍',
// 수정 불가, 열거 불가
writable: false,
enumerable: false,
configurable: false,
});
console.log(dog.name); // 멍멍
console.log(Object.keys(dog)); // 이름 출력 불가
delete dog.name;
console.log(dog.name);
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.freeze 추가 ❌, 삭제 ❌, 쓰기 ❌, 속성 재정의 ❌
// (단, 얕은 꽁꽁 얼림!)
const teogu = { name: 'teogu' };
const dog = { name: '와우', emoji: '🐶', owner: teogu };
Object.freeze(dog);
dog.name = '멍멍';
console.log(dog); // {name: '와우', emoji: '🐶', owner: {name: 'teogu'}}
dog.age = 4;
console.log(dog); // {name: '와우', emoji: '🐶', owner: {name: 'teogu'}}
delete dog.name;
console.log(dog); // {name: '와우', emoji: '🐶', owner: {name: 'teogu'}}
dog.name = '엘리얌';
console.log(dog); // {name: '와우', emoji: '🐶', owner: {name: 'teogu'}}
// 밀봉! Object.seal 값의 수정 ✅, 추가 ❌, 삭제 ❌, 속성 재정의 ❌
const cat = { ...dog };
// cat이라는 오브젝트에 dog라는 오브젝트를 복사
//Object.assign(cat, dog);
Object.seal(cat);
console.log(cat); // {name: '와우', emoji: '🐶', owner: {name: 'teogu'}}
cat.name = '냐옹';
console.log(cat); // {name: '냐옹', emoji: '🐶', owner: {name: 'teogu'}}
delete cat.emoji;
console.log(cat); // {name: '냐옹', emoji: '🐶', owner: {name: 'teogu'}}
console.log(Object.isFrozen(dog)); // true
console.log(Object.isSealed(cat)); // true
// 확장 금지 preventExtensions 추가 ❌
const tiger = { name: '어흥' };
Object.preventExtensions(tiger); // {name: '어흥'}
console.log(Object.isExtensible(tiger)); // false
tiger.name = '어흐응';
console.log(tiger); // {name: '어흐응'}
delete tiger.name;
console.log(tiger); // {}
tiger.age = 1;
console.log(tiger); // {}
프로토타입 레벨 함수
// const dog1 = { name: '뭉치', emoji: '🐶' };
// const dog2 = { name: '코코', emoji: '🐩' };
function Dog(name, emoji) {
this.name = name;
this.emoji = emoji;
// 생성자 함수안에서 정의하면
// 인스턴스 레벨의 함수
/* this.printName = () => {
console.log(`${this.name} ${this.emoji}`);
}; */
}
// 프로토타입 레벨의 함수
Dog.prototype.printName = function () {
console.log(`${this.name} ${this.emoji}`);
};
const dog1 = new Dog('뭉치', '🐶');
const dog2 = new Dog('코코', '🐩');
console.log(dog1, dog2);
dog1.printName();
dog2.printName();
// 오버라이딩
// 인스턴스 레벨에서(자식) 동일한 이름으로 함수를 재정의 하면 (오버라이딩 하면)
// 프로토타입 레벨의(부모) 함수의 프로퍼티는 가려진다 (섀도잉 됨)
dog1.printName = function () {
console.log('안녕!!');
};
dog1.printName();
// 정적 레벨
Dog.hello = () => {
console.log('Hello!');
};
Dog.hello();
Dog.MAX_AGE = 20;
프로토타입을 이용한 상속
// 프로토타입을 베이스로한 객체지향 프로그래밍
function Animal(name, emoji) {
this.name = name;
this.emoji = emoji;
}
// 프로토타입 레벨의 함수로 만들어 줍니다.
Animal.prototype.printName = function () {
console.log(`${this.name} ${this.emoji}`);
};
function Dog(name, emoji, owner) {
// super(name, emoji)
// Dog이 name과 emoji를 animal에 넘깁니다.
Animal.call(this, name, emoji);
this.owner = owner;
}
// Dog.prototype = Object.create(Object.prototype);
// Dog의 프로토 타입을 animal로 변경해줍니다.
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.play = () => {
console.log('같이 놀자옹!');
};
function Tiger(name, emoji) {
Animal.call(this, name, emoji);
}
Tiger.prototype = Object.create(Animal.prototype);
Tiger.prototype.hunt = () => {
console.log('사냥하자! ..🐇..');
};
const dog1 = new Dog('멍멍', '🐶', '엘리');
dog1.play();
dog1.printName();
const tiger1 = new Tiger('어흥', '🐯');
tiger1.printName();
tiger1.hunt();
dog는 animal의 프로토 타입을 상속을 하게 됩니다. 그렇기 때문에 animal의 prototpye에서 정의한 play와 printName을 사용할 수 있게 됩니다.
최신 자바스크립트에서는 프로토타입으로 객체지향 프로그래밍을 하지는 않고 클래스를 통해서 객체지향을 합니다.
상속도 확인 하는 법
console.log(dog1 instanceof Dog); // true
console.log(dog1 instanceof Animal); // true
console.log(dog1 instanceof Tiger); // false
console.log(tiger1 instanceof Dog); // false
console.log(tiger1 instanceof Animal); // true
console.log(tiger1 instanceof Tiger); // true
mixin
// 오브젝트는 단 하나의 prototype을 가리킬 수 있다 (부모는 단 하나!)
// 여러개의 함수들을 상속하고 싶다고 생각될때 사용하는 것이 mixin
// Mixin!
const play = {
play: function () {
console.log(`${this.name} 놀아요!`);
},
};
const sleep = {
sleep: function () {
console.log(`${this.name} 자요!`);
},
};
function Dog(name) {
this.name = name;
}
// dog의 프로토타입에 play, sleep의 객체를 할당
Object.assign(Dog.prototype, play, sleep);
const dog = new Dog('멍멍');
console.log(dog);
dog.play();
dog.sleep();
// 클래스에도 활용이 가능합니다.
class Animal {}
class Tiger extends Animal {
constructor(name) {
super();
this.name = name;
}
}
Object.assign(Tiger.prototype, play, sleep);
const tiger = new Tiger('어흥!');
tiger.play();
tiger.sleep();
이렇게 mixin을 사용하게되면 여러개의 객체를 다중 상속가능하게 됩니다.
728x90
'기술 면접 정리 > 자바스크립트' 카테고리의 다른 글
4장 콜백함수 정리 (0) | 2022.05.04 |
---|---|
구조분해 할당 (0) | 2022.04.21 |
이벤트 루프 (0) | 2022.04.15 |
자바스크립트 엔진 (0) | 2022.04.15 |
preventDefault(); 와 stopPropagation(); 차이 (0) | 2022.04.12 |