18.Constructure, Prototype, class (객체지향) - ES6

Updated:


Constructor

Constructor 용도

  • object 를 쉽게 복사 할 수 있을때 사용합니다.
// constructure 생성 보통 생성 자 이름에 대문자를 사용해서 일반 함수와 다르다는것을 구분하기 위해 처음 글자를 대문자를 넣어서 사용함
function Student(name, age) {
  this.name = name; // this 는 새로 생성되는 object 를 뜻함
  this.age = age;
  this.sayHi = function () {
    console.log(`안녕하세요 ${this.name} 입니다` )
  }
}
// this.age =15 이렇게 새로 생성되는 object 에 값을 부여 가능

const student1 = new Student('Park', 30) // constructure 를 복사해서 뽑아 내는것
const student2 = new Student('Kim', 20) // constructure 를 복사해서 뽑아 내는것

console.log(student1)
console.log(student2)
student1.sayHi();

image

Constructor 연습

🔷 쇼핑몰에서 쓸 상품데이터를 object 형태로 여러개 만들고 싶을경우 와 tax() 라는 함수가 실행 될때 10% 만큼 부가세가 출력 되게 만들고 싶을 경우의 consructure 생성

function Product(name, price) {
  this.name = name, // 주로 책에서 this 생성자를 instance 라고 합니다
  this.price = price,
  this.tax = function () {
    console.log(this.price * 0.1)
  }
}

const product1 = new Product('shirts', '50000')
const product2 = new Product('pants', '60000')

console.log(product1)
console.log(product2)

product1.tax()

image

  • 위의 constructure 를 활용해서 여러 object 를 만드는 것을 상속 (inheritance) 라고 말함

Prototype

  • JS 에 만 있는 상속을 구형 할 수 있는 또 하나의 문법인 prototype 입니다

  • protopype 은 유전자 라고 생각하면 됩니다.

  • constructor 를 만들면 prototype 이라는 공간이 자동으로 생깁니다.

  • prototype 에 값을 추가 하면 모든 자식들이 물려 받기가 가능합니다.


function Product(name, price) {
  this.name = name,
  this.price = price,
  this.tax = function () {
    console.log(this.price * 0.1)
  }
}

const product1 = new Product('shirts', '50000')
const product2 = new Product('pants', '60000')

Product.prototype.color = 'red'; // 부모인 Product 에 color 라는 key 값을 등록을 하게 되면 그 constructor 를 사용하는 자식 object 들은 그것을 다 상속을 받습니다.

console.log(product1.color) // 위에 constructro 에 prototype 으로 유전자를 color 라는것을 썼기 때문에 color 값이 출력 할 수 있음

image

Prototype 동작원리

  • 위의 code 에서 product1.color 을 실행하게 되면 JS 의 해석은 다음 순서와 같습니다.

    • product1이 직접 color 을 가지고 있는가?

    • 있으면 출력하는데, 만약 없다고 하면 product1의 부모 유전자가 color 를 가지고 있는가? 를 검사 함

    • 부모 유전자에 있는게 color 가 있는 게 확인이 되면 그것을 출력함

Prototype 내장 함수 동작

  • 위의 code 에서 product1.toString() 인 JS 내장 함수를 사용하게 되면 을 JS 의 해석은 다음 순서와 같습니다.

    • product1 이 toStrin() 을 가지고 있는가?

    • 없다면 부모 유전자에는 있는가?

    • 없다면 부모의 부모 유전자에 있는가?

product1.toString()


const arr = [1, 2, 3]; // 사용자 측면에서 arr 생성
const arr = new Array(1, 2, 3) // 실제 JS 가 내부적으로 array 가 만들어지는 방식임

const obj = { name : 'kim' };
const obj = new Object();

Array.prototype; // Array prototype 에 있는지 확인을 함

arr.sort(); // Array 에 prototype 이 포함되어 있는 것을 사용할 수 있음

🔑 그럼 prototype으로 상속시키는거랑 constructor로 상속시키는거랑 차이가 뭐죠?

  • 자식들이 값을 직접 소유하게 만들고 싶으면 constructor로 상속시키시면 되고

  • 부모만 가지고 있고 그걸 참조해서 쓰게 만들고 싶으면 prototype으로 상속시키면 됩니다?

  • 보통은 그래서 상속할 수 있는 함수 같은 것들은 prototype으로 많이 만들어놓습니다.

Prototype 특징

1.Prototype 은 함수에만 생성됩니다.

  • 일반 object, array 이런거 만들어도 거기엔 prototype이 없습니다.

  • 그럼 일반 object 같은걸 상속하고 싶으면 어떻게 할까요?

    • constructor 함수를 만들던가.

    • Object.create() 를 만들어서 쓰던가.

    • class를 만들어 쓰면 됩니다.

2.내 부모 유전자(부모의 prototype)을 검사하고 싶다면 proto

  • 부모로부터 생성된 자식 object들은 __proto__라는 속성이 있습니다.

  • 이걸 출력해보시면 부모의 prototype이 출력됩니다.

  • 그래서 __proto__는 부모의 prototype과 같은 의미입니다.

function Student(){
  this.name = 'Kim';
  this.age = 15;
}
var student1 = new Student();

console.log(student1.__proto__);
console.log(Student.prototype);

3.__proto__를 직접 등록하면 object 끼리 상속기능을 구현가능합니다.

const parent = { name : 'Kim' };
const child = {};

child.__proto__ = parent; // child 에 parnet 에 등록해서 사용함
console.log(child.name); // Kim 출력

4.콘솔창에 prototype 정보들이 항상 출력됩니다.

function Students(){
  this.name = 'Kim';
  this.age = 15;
}
Students.prototype.gender = 'male';

let student1 = new Students();


console.log(student1)

image

  • 이렇게 쭉 내 부모의 부모까지 탐색할 수도 있습니다.

  • 탐색해보시면 모든 object 자료형의 조상은 Object() 라는 Student 이며 (일명 Object.prototype)

모든 array 자료형의 조상도 Object()입니다. (중간에 Array()라는 부모도 있고요)

모든 함수 자료형의 조상도 Object() 입니다.

(그래서 자바스크립트는 모든게 다 Object라고 말하는 것입니다.)

Constructure, prototype 연습

Q1. object 를 여러개 만들기

  • student1,2,3 을 constructure 를 사용해서 출력하기

  • constructure 안에 sayHi() 라는 함수를 실행해서 메세지 출력

function Student(name, age) {
  this.name = name,
  this.age = age,
  this.sayHi = function () {
    console.log(`안녕 나는 ${this.name}이야`)
  }
}

const student1 = new Student('kim', 20)
const student2 = new Student('park', 21)
const student3 = new Student('Lee', 22)

console.log(student1)
console.log(student2)
console.log(student3)

student1.sayHi();

image

Q2. array 안에서 특정 숫자 제거하는 함수 만들기

  • 모든 array 에 사용되고, array 내에 3이라는 값을 제거하는 함수를 만들어 보자
let arr = [1, 2, 3]

Array.prototype.remove3 = function () { // remove3 이라는 array 내장함수 같이 prototype 형식으로 Array object 에 함수를 만들어서 넣기임
  for (let i = 0; i < this.length; i++){ // arr 길이 만큼 탐색 (0번 index 부터 니까 this.length 만큼만 반복)
    if (this[i] === 3) {
      this.splice(i, 1) // 만약 3을 발경하면 그자리 index 번호의 뒤에 1개를 splice 제거 해주는것임
    }
  }
}

arr.remove3()
console.log(arr) // 1, 2 가 출력 됨

Object.create() - ES5

  • prototype 을 대신해서 상속기능을 사용할 수 있는 ES5 버전의 object.create() 을 사용하는 방법 입니다.
// Object.create(prototype object);

let parent = {
  name: 'kim',
  age: 50
}

// 부모가 가진 name, age를 그대로 물려받은 object 를 만들고 싶으면?

// 1. constructor 함수를 만들거나. 
// 2. Object.create() 을 사용하거나

let child = Object.create(parent); // 이거는 그냥 prototype 을 부모로 해주세요 라는 의미만 됨

console.log(child)

console.log(child.name) // kim 으로 출력됨
// 왜냐하면 1. 자식에 name을 직접 갖고 있나 X => 없으면 자식의 부모 prototype에는 name 이 있나? O 있으니까 kim 이 출력되는 것임

// 자식을 바꾸고 싶으면 

child.age = 20

console.log(child.age); // 20 출력


// 자식의 자식도 만들 수 있음

let son = Object.create(child)

console.log(son.age) // 바로 위의 child 의 age를 출력 할 수 있음 

Class - ES6

// constructor 만드는 신문법

//  함수를 추가 하고 싶으면 1. constructor 에 추가를 하던가
class parent {
  constructor() {
    this.name = 'kim'
    this.sayHi = function () {
      console.log('hello')
    }
  }
  saybye() { // 함수 추가 하시면 2. 요기에다가 쓰던가. 단!! 여기에 쓰면 자식 object 에 추가가 안됨 부모.prototype 에 추가됨
    console.log('bye')
  }
}

let child = new parent()

console.log(child)


// Q. child.__proto__ 의 경우는 ?

// A. 
console.log(child.__proto__)
console.log(parent.prototype)

console.log(Object.getPrototypeOf(child))

image

extends / super


// 아래의 parent class 유사한 class 를 하나 더 만드는것 extends (class 상속)
class parent {
  constructor(name, name2) {
    this.firstname = 'kim';
    this.lastname = name;
    this.nickname = name2;
  }
  // super()의 또 다른 용도
  sayHi() {
    console.log('Hi');
  }
}

let parent1 = new parent('jacob', 'test') // 기존의 class 복사를 통해서 만든것
console.log(parent1)


// 복사 / 상속 할 값이 많으면 힘들기 때문에 extends 로 class 를 복사해서 사용합니다
// parent 의 속성들을 그대로 물려 받아서 사용하려면
class father extends parent {
  constructor(name, name2) {
    // super()의 의미는 parent 의 constructor를 복사해서 같다 쓰는것과 같음
    super(name, name2); // super 를 사용해야 this 를 사용할 수 있음
    this.age = 50; // extends 해서 만든 class는 this 그냥은 못씁니다
  }
  sayBye() {
    console.log('Bye');
    super.sayHi(); // 여기서 super() 는 부모 class 의 prototype 을 의미함
  }
}

let father1 = new father('John', 'test2');
console.log(father1)
console.log(father1.sayHi())

getter / setter

  • get / set 없이 object 만들기
let person = {
  name: 'Park',
  age: 30,
  nextAge() { // 내년도의 알기 위한 function
    return this.age + 1;
  },
  setAge(age) {
    this.age = parseInt(age); // 정수로 변환 되기 때문에 str 이 들어와도 number 로 바꿔줌
  }
}

// age 라는 자료를 꺼내고 싶으면?
console.log(person.age) // 이렇게 직접 데이터를 꺼내서 사용시에는 수정할 때 실수가 유발 될 수 있음

// 다른 방법으로 요즘 유행하는 자료를 꺼내는 법은?

// 함수를 만들어서 object 데어터를 다루는 이유는 ?
//  1. object 자료가 복잡할 때 이득
//  2. object 자료 수정시..
// 즉, 데이터를 꺼내거나 / 수정하거나 그럴때 편리 & 실수 방지 & 관리 가능

console.log(person.nextAge())

person.setAge(20); // 20으로 변경
console.log(person.age)
  • ES5 부터 set /get 키워드가 실행되서 더 간단히 위에 코드를 만들 수 있음
let person2 = {
  name: 'Park',
  age: 30,
  get nextAge() { // 데이터를 꺼내 쓰는 함수에 사용 
    return this.age + 1;
  },
  set setAge(age) { // set 은 데이터 변경하는 함수에 사용
    this.age = parseInst(age); // 정수로 변환 되기 때문에 str 이 들어와도 number 로 바꿔줌
  }
}

// 이렇게 사용하게 되면, 괄호를 안해도 아래와 같이 간단하게 사용할 수 있음 
person.setAge = 20;

// get 을 사용했기 때문에
console.log(person2.nextAge) // 31 출력

getter, setter 주의점

- getter 는 return 이 있어야 합니다.

- setter 는 parameter 가 1개 있어야 합니다. 

class 에서 사용하는 get / set

class person {
  constructor() {
    this.name = 'Park';
    this.age = 20;
  }
  get nextAge() {
    return this.age + 1
  }
  set SetAge(age) {
    this.age = age;
  }
}

let person1 = new person();

console.log(person1)
console.log(person1.nextAge) // 괄호를 사용하지 않아도 get 을 사용했기 때문에 안써도 됨

person1.SetAge = 80; // set 함수를 통해서 80으로 값을 변경 한것임
console.log(person1)

연습문제 (class, extends, getter, setter)

Q1. class 를 사용하여 유사 object 를 만들기

class dog {
  constructor(type, color) {
    this.type = type;
    this.color = color;
  }
}

let dog1 = new dog('말티즈', 'white')
let dog2 = new dog('진돗개', 'brown')

console.log(dog1)
console.log(dog2)

image

Q2. 위의 코드에서 extends 를 사용해서 age 타입 추가 하기

class Cat {
  constructor(type, color) {
    this.type = type;
    this.color = color
  }
}


class CatAge extends Cat {
  constructor(type, color, age) {
    super(type, color);
    this.age = age;
  }
}

const cat1 = new CatAge('코숏', 'white', 5)
const cat2 = new CatAge('러시안블루', 'brown', 2)

console.log(cat1)

image

Q3. get / set 을 사용해서 게임 Unit 만들기

  • 조건

    • 모든 Unit의 인스턴스는 공격력, 체력 속성이 있으며 기본 공격력은 5, 기본 체력은 100으로 설정되어 있어야 합니다.

    • 모든 Unit의 인스턴스는 전투력을 측정해주는 battlePoint라는 getter가 있습니다. console.log( 인스턴스.battlePoint ) 이렇게 사용하면 현재 공격력과 체력을 더한 값을 콘솔창에 출력해주어야합니다.

    • 모든 Unit의 인스턴스는 heal이라는 setter가 있습니다. 인스턴스.heal = 50 이렇게 사용하면 체력 속성이 50 증가해야합니다.

class Unit {
  constructor() {
    this.str = 5;
    this.con = 100;
  }
  get bettlePoint() {
    return this.str + this.con;
  }
  set heal(a) {
    this.con = this.con + a;
  }
}

const unit1 = new Unit();
console.log(unit1.bettlePoint);
unit1.heal = 50;

🔶 🔷 📌 🔑

Reference

Leave a comment