기술 면접 정리/자바스크립트

프로토타입

테오구 2022. 6. 1. 13:30
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