[JavaScript ES6] Class

Sooooooah·2023년 4월 25일

javaScript ES6

목록 보기
5/8

클래스의 지원

기존에는 프로토타입 체인으로 객체지향 언어의 상속과 캡슐화(은닉화) 등의 OOP 문법을 구현 —> 낯설고 어렵다

class Person {
  // 생성자
  constructor(name, age) {
    this.name = name;
    this.age == age;
  }

  testHello() {
    console.log(`안녕하세요~ ${this.name}`);
  }
}

const p1 = new Person("홍길동", 20);
console.log(p1.name); // 홍길동
console.log(p1.age); // 20
p1.testHello(); // 안녕하세요~ 홍길동님

const p2 = new Person("이순신", 30);
console.log(p2.name); // 이순신
console.log(p2.age); // 30
p2.testHello(); // 안녕하세요~ 이순신님

console.log(p1 instanceof Person); // true

클래스와 프로토타입

클래스 타입체크 --> function

console.log(typeof Class); // function
// --> 함수도 객체

클래스 선언문의 내부동작

  1. Person 이름의 함수를 생성
  2. 이 함수의 본문은 class의 constructor를 그대로 가져온다. 만약 constructor가 없으면 빈 함수를 생성
  3. 메서드는 같은 이름의 Person.prototype(프로토타입 객체)에 추가
  4. 결국 ‘프로토타입 객체’ 쪽에 클래스 내에 정의한 메서드들을 추가해놓는 것이기 때문에, 해당 클래스의 인스턴스(객체)가 생성되면 추가된 메서드를 자유롭게 이용할 수 있는 것이다. —> 프로토타입에 대한 이해가 필요

호이스팅(Hoisting)

함수 레벨 스코프 vs 블록 레벨 스코프

var a = 111;
console.log(a);

{
  var a = 333; // 전역변수 --> 함수레벨 스코프, var 키워드는 중복이 허용(선언이 가능), 호이스팅 O. 함수가 아닌 변수 선언은 모두 전역
}
console.log(a); // 333
  • 호이스팅이란?

Hoist = (국기를, 닻을) 끌어올리다

자바스크립트는 기본적으로 모든 선언문(var, let, const, function, class)을 호이스팅

=== 스코프 안의 어디서든 변수 선언은 최상위에서 선언한 것과 동일

var testA;
console.log("testA 값은" + testA); // undefined

console.log("testAA 값은" + testAA); // undefined --> hoisting과 값 할당을 동시에
var testAA;

let testB;
console.log("testB 값은" + testB); // undefined

console.log("testBB 값은" + testBB); // Error --> hoisting은 되었지만 값은 변수 선언시에 할당됨
let testBB;

클래스 호이스팅

console.log(Person); // 초기화 오류
class Person {}

// var vs let, const == class 호이스팅 에러 비교
var str1 = "Hello, World";

const testFun = function () {
  console.log(str1); // undefined
  var str1 = "Hello Korea";
  console.log(str1); // Hello Korea

  console.log(str2);
  let str2 = "Helloo Koreaa"; // Error --> 초기화 Reference Error(참조 에러) 발생
};

testFun(); // undefined
  • 클래스 상속
class Parent {}
class Child extends Parent {}   // 정상 작동

class Child2 extends Parent2 {} // Error --> 초기화 Reference Error(참조 에러) 발생
class Parent2 {}
  • 표현식 정의

무명 표현식

const Person = class {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
};

const p1 = new Person("홍길동", 20);
console.log(p1.name);
console.log(p1.age);
console.log(Person.name); // Person --> 클래스명이 없으므로 암묵적으로 변수명이 name 속성값이 된다

유명 표현식

const Person = class namedPerson {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
};

const p1 = new Person("이순신", 30);
console.log(p1.name);
console.log(p1.age);
console.log(Person.name); // namedPerson --> 클래스명이 있으므로 name 속성값이 클래스명이 된다

const p2 = new namedPerson("강감찬", 40); // Error
  • 클래스 생성
// 생성자 함수
function Person(name, age) {
  this.name = name;
  this.age = age;
}

Person.prototype.say = function () {
  console.log("안녕하세요");
};

const p1 = new Person("홍길동", 20);
p1.say(); // 안녕하세요

// class
class Person2 {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  say() {
    console.log("안녕");
  }
}

const p2 = new Person2("이순신", 30);
p2.say(); // 안녕

클래스는 기본적으로 엄격 모드(use strict) —> 자동적용

클래스 메서드는 열거 대상이 X —> 클래스와 같은 이름의 ‘프로토타입 객체’의 속성에 추가된 메서드들 열거 X

for (let i in p1) {
  console.log(i); // name, age, say
}

for (let i in p2) {
  console.log(i); // name, age
}
  • getOwnPropertyNames()

객체의 모든 특성을 보고자 할때 사용

정적 메서드 —> Object.getOwnPropertyNamse(객체명)

           —> Object.getPrototype() > 지정된 객체의 내부 Prototype 속성값을 반환
const p1 = {
  eat() {},
  run() {},
  rest() {},
};

class Person2 {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  eat() {}
  run() {}
  rest() {}
}
new p2 = new Person2('홍길동', 20)

// 열거 대상 X --> 메서드가 보이지않음
for(let i in p2){
  console.log('p2의 멤버들 = '+ i); // name, age
}

console.log(p2); // Person2{} --> 메서드는 보이지 않음
console.log(Object.getPropertyOf(p2));              // 지정된 객체의 내부 Prototype 속성값을 반환
console.log(Object.keys(p1));                       // ['eat', 'run', 'rest']
console.log(Object.keys(Object.getPropertyOf(p2))); // []
// -------------------------- //
console.log(Object.getOwnPropertyNames(p1)) // ['eat', 'run', 'rest']
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(p2))) // ['constructor', 'eat', 'run', 'rest']
class Animal {
  constructor(name) {
    this.name = name;
  }
  cry() {
    console.log(this.name + "가 웁니다");
  }
  move() {
    console.log(this.name + "가 이동합니다.");
  }
}
const ani1 = new Animal("호랑이");
console.log(ani1.name);
ani1.cry();
ani1.move();

// 속성과 메서드 추가
Animal.prototype.age = 4;
Animal.prototype.run = function () {
  console.log(this.name + "가 뛰어갑니다");
};

console.log(Animal.name); // Animal
console.log(Animal === Animal.prototype.constructor); // true

생성자 메서드

  • 생성자 메서드는 인스턴스(객체)를 생성할 때 제일 먼저 실행되는 메서드이다.
  • 주로 생성되는 객체의 초기화를 담당
  • 클래스내에 constructor라는 이름을 가진 생성자 메서드는 오직 하나여야만 한다.
  • constructor 생략이 가능 —> 클래스에 constructor(){}를 작성한 것과 동일
class Animal {}
const ani1 = new Animal();
ani1.name = "악어";
ani1.age = 4;

상속(Inheritance)

부모가 가진 자원을 그대로 상속받아 자식 클래스를 생성 및 확장해서 만들 수 있다.

—> extends 키워드 사용

// 부모 클래스
class Animal {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  eat() {
    console.log(this.name + "먹다");
  }
}

// 자식 클래스
class Tiger extends Animal {}
const t1 = new Tiger("호랑이", 2);

console.log(t1.name); // 호랑이
console.log(t1.age); // 2
  • super
class Animal {
  constructor(group) {
    this.group = group;
  }
  getGroup() {
    return this.group;
  }
  eat() {}
  sleep() {}
  bark() {
    return "짖다";
  }
}
class Mammal extends Animal {
  constructor(name, finger, toe, eyesight) {
    super(Mammal.name); // 상위 클래스 호출해줘야함 --> this보다 super가 당연히 먼저 호출되어야함 > 참조 오류
    this.name = name;
    this.finger = finger;
    this.toe = toe;
    this.eyesight = eyesight;
  }
  run() {
    return `${this.name} (${this.group}) 뛴다`;
  }
  bark() {
    return `${this.name} (${this.group}) 크게 짖다`;
  }
}

const tiger = new Mammal("호랑이", 10, 10, 1.5);
console.log(tiger.name); // super 호출 안하면 Error
console.log(tiger.getGroup()); // undefined --> super('') 인자값을 넣어주면 '' 안에 내용이 출력 > Mammal
console.log(tiger.eyesight); // 1.5
console.log(tiger.bark()); // override --> 기각하다, 무시하다/ ~보다 더 우선하다, 우선시하다

자식 클래스에서 constructor를 정의해주면 super();를 꼭 작성해주어야 한다. —> constructor 정의 안하면 super 안써도 에러나지 않음

  • 부모 메서드 호출
class Mammal extends Animal{
~
move() {
    console.log(`${this.name} 이동하면서 ${super.bark()}`);
  }
~
}

tiger.move(); // 호랑이 이동하면서 짖다
  • 속성 출력
console.log(Object.getOwnPropertyNames(tiger));
// ['group', 'name', 'finger', 'toe', 'eyesight']

console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(tiger)));
// ['constructor', 'run', 'bark', 'move']

정적 메서드

  • 어떻게 정적 메서드를 구현하는지 - Static(고정된) Method

메서드 앞에 static 키워드를 붙여주면 따로 인스턴스(객체)를 생성하지 않아도 메서드 호출이 가능 <—> 반대로 인스턴스(객체)를 통해서 호출하는 것은 불가능

  • 정적 메서드가 어떤 경우에 호출될 수 있고 어떤 경우에 호출이 안되는지
class Animal {
  constructor(name) {
    this.name = name;
  }
  getName() {
    return this.name;
  }
  sleep() {
    console.log("잠자다");
  }
  static sleep2() {
    console.log("Zzz...");
  }
}

const tiger = new Animal("호랑이");

tiger.sleep();
console.log(tiger.getName());

tiger.sleep2(); // Error
Animal.sleep2(); // Zzz...
  • 인스턴스(객체)가 정적 메서드를 호출할 수 있는지 없는지

—> constructor 사용

tiger.constructor.sleep2(); // Zzz...
  • 상속 관계에서의 정적 메서드
class Add {
  static plus(x) {
    x = x || 100;
    return x + 1000;
  }
}
class ChildAdd extends Add {
  static plus(x) {
    return super.plus(x) + super.plus(x) + super.plus(x);
  }
}

console.log(Add.plus()); // 1100
console.log(Add.plus(500)); // 1500
console.log(ChildAdd.plus()); // 3300
console.log(ChildAdd.plus(300)); // 3900
console.log(ChildAdd.plus(30)); // 3090

const add1 = new Add();
console.log(add1.plus()); // Error
console.log(add1.constructor.plus()); // 1100
console.log(add1.constructor.plus(30)); // 1030

const cass1 = new ChildAdd();
console.log(cass1.plus()); // Error
console.log(cass1.constructor.plus(30)); // 3090
  • Object.create 상속

클래스 문법을 지원하기 전, object.create로 상속을 구현

  1. Object.create(부모객체.prototype);
  • 첫 번째 인자(부모객체)로 들어온 해당 객체(부모)의 '프로토타입 객체'를 복제

  • 이렇게 복제된 것을 --> '자식객체.prototype'에 할당

  1. 그러나 여전히 복제된 ‘프로토타입 객체’는 부모 객체를 가리키고 있기 때문에 이것을 자식 객체를 가리키도록 바꿔줘야 한다.
  • ’자식객체.prototype.constructor = 자식객체’를 할당해서 연결 고리를 맞춰준다.
  1. new 키워드를 통해서 인스턴스(객체)를 생성시 자식 객체의 this가 부모 객체까지 전달되도록 해줘야 한다. (super)
  • 부모객체.call(=apply)(this, 인자값)

  • class의 super 역할

—> class에서는 extends와 super를 통해서 상속을 구현, class 이전에는 Object.create(), prototype, call 등을 이용해서 구현하고 있다.

// 부모 클래스
function ParentClass(name, age) {
  this.name = name;
  this.age = age;
}

ParentClass.prototype.sayHello = function () {
  console.log(`Hello ${this.name}`);
};

// 자식 클래스
function ChildClass(name, age, power) {
  ParentClass.call(this, name, age); // class의 super 역할
  this.power = power;
}

ChileClass.prototype = Object.create(ParentClass.prototype);
ChildClass.prototype.constructor = ChildClass;
ChildClass.prototype.move = function () {
  console.log(`${this.name} is moving...`);
};

const c1 = new ChildClass("batman", 20, 900);
console.log(c1);
console.log(c1.name);   // batman
console.log(c1.age);    // 20
console.log(c1.power);  // 900
c1.sayHello();          // Hello batman
c1.move();              // batman is moving...

console.log(c1.___proto__);             // ChildClass의 prototype 객체
console.log(c1.___proto__.___proto__);  // ParentClass의 prototype 객체

접근 제한자

자바스크립트에는 전통적인 다른 OOP에서와 같은 접근자(private, public, protected)가 없고, 기본적으로 모두 public

—> 해당 클래스의 인스턴스(객체)를 통해 외부에서 항상 ‘참조’가 가능

—> 생성자 메서드내에서 속성명 앞에 ‘_’를 임의로 붙여 private라고 암묵적으로 표시 —> this._name

  1. Property
class Person {
  age = 20;
  power = 900;
  #finger = 10; // private --> 외부에서 직접 접근할 수 없다
  #toe = 10; // private
}

const p1 = new Person();
console.log(p1); // {age:20, power:900}
console.log(p1.age); // 20
console.log(p1.power); // 900

p1.age = 10;
console.log(p1.age); // 10

console.log(p1.finger); // undefined
console.log(p1.#finger); // Private Error
console.log(p1.#toe); // Private Error
  1. Method
class Animal {
  #age = 4;
  bark() {
    this.#age = 8; // 클래스 내부에서의 접근은 가능
    return `${this.#age}살 짜리 개가 짖고있다`;
  }
}

const ani1 = new Animal();
console.log(ani1.#age);     // Private Error
console.log(ani1.bark());   // 8살 짜리 개가 짖고있다

class Animal2 {
  #age = 4;
  bark() {
    this.#age = 8;
    return `${this.#age}살 짜리 개가 짖고있다`;
  }
  #privateMethod() {
    return `Hello, Private Method`;
  }
  getPrivateMethod() {
    return this.#privateMethod();     // get메서드를 통해 private를 호출
  }
}

const ani2 = new Animal2();
console.log(ani2.#privateMethod());   // Private Error
console.log(ani2.getPrivateMethod()); // Hello, Private Method
  1. Private static method
class Animal3 {
  static #privateStaticMethod() {
    return "Hello, Private Static Method";
  }

//   this로 접근
  static getPrivateStaticMethod() {
    return this.#privateStaticMethod();
  }
  
//   클래스명으로 접근
  static getPrivateStaticMethod_ClassName() {
    return Animal3.#privateStaticMethod();
  }
}
// static 메서드는 바로 호출 가능
console.log(Animal3.#privateStaticMethod());             // Private Error
console.log(Animal3.getPrivateStaticMethod());           // Hello, Private Static Method
console.log(Animal3.getPrivateStaticMethod_ClassName()); // Hello, Private Static Method

Getter, Setter

  1. getter
  • 클래스 속성에 접근하여 값을 가져오고자 할 때 사용
  • 메서드명 앞에 get 키워드를 붙여서 사용
  • [주의] 메서드 사용시 마치 속성처럼 사용 —> getter는 호출 X —> 메서드 끝에 괄호()를 생략
  • 기본적으로 값을 가져오는 용도 —> 반드시 어떤 값을 return
class Person {
  age = 100;
  constructor(id, name, email) {
    this._id = id;
    this._name = name;
    this._email = email;
  }
  get id() {
    return this._id;
  }
  get name() {
    return this._name;
  }
  get email() {
    return this._email;
  }
  info() {
    return `ID : ${this.id}, NAME : ${this.name}, EMAIL : ${this.email}`;
  }
}

const p1 = new Person("batman", "배트맨", "batman@gmail.com");
console.log(p1.age);    // 100
console.log(p1.name()); // Error
console.log(p1.name);   // 배트맨 --> getter 사용시 괄호 생략!

console.log(p1.info()); // ID : batman, NAME : 배트맨, EMAIL : batman@gmail.com

p1._name = "슈퍼맨";
console.log(p1.info()); // ID : batman, NAME : 슈퍼맨, EMAIL : batman@gmail.com
  1. setter
  • 클래스 속성에 값을 할당할 때 사용
  • 메서드명 앞에 set 키워드를 붙여서 사용 —> getter와 마찬가지로 메서드명을 속성처럼 사용
class Person {
  age = 100;
  constructor(id, name, email) {
    this._id = id;
    this._name = name;
    this._email = email;
  }
  get id() {
    return this._id;
  }
  get name() {
    return this._name;
  }
  get email() {
    return this._email;
  }
  set id(value) {
    this._id = value;
  }
  set name(value) {
    this._name = value;
  }
  set email(value) {
    this._email = value;
  }
  info() {
    return `ID : ${this.id}, NAME : ${this.name}, EMAIL : ${this.email}`;
  }
}

const a1 = new Person("antman", "앤드맨", "antman@gmail.com");
console.log(a1.id); // antman
a1.id = "aaaaaaaaaaaantman";
console.log(a1.id); // aaaaaaaaaaaantman

console.log(a1.age); // 100
a1.age = 200;
console.log(a1.age); // 200

console.log(a1.name); // antman
a1.name = "개미맨";
console.log(a1.name); // 개미맨

console.log(a1.email); // antman@gmail.com
a1.email = "test@test.com";
console.log(a1.email); // test@test.com
💡 getter는 값을 취할시 —> 해당 get 메서드 호출

setter는 값을 할당시 —> 해당 set 메서드 호출

다형성 (Polymorphism)

  • 다형성 : Polymorphism > 다양한 형태를 가지는 성질
  • 문법적 : 어떤 기능이나 동작을 구현시 설계적인 부분과 구현(동작/기능)되는 부분을 분리 —> 객체별로 다양하게 사용할 수 있게 하는 것
  • 부모(super) 클래스를 상속받은 자식(sub) 클래스의 인스턴스(객체) 별로 적절한 메서드를 사용하게 하는 것 —> ‘설계 부분’과 ‘구현 부분’은 1:N 관계가 성립

—> 부모가 잘 설계해놓은 것을 자식이 받아서 설계대로 구현한 후 각 인스턴스(객체)별로 다양하게 사용하는 것

다른 전통적인 언어들의 클래스와 ES6 클래스의 차이점

  • 이러한 다형성을 지원할 수 있는 문법적인 기반이 안되어 있음 —> 인터페이스, 추상클래스, 타입에 대한 개념
  • 따라서 ES6 클래스에서는 다형성을 전통적인 언어들에서와 같이 문법적인 측면에서 구현하기는 조금 어렵다 > 흉내
  • 설계 부분 : 인터페이스, 추상클래스 > 보통은 강제하고 구현하게 함. / ES6 지원 X, 강제하지도 X

for ...of 반복

  1. 다양한 자료구조에서 사용할 수 있다. —> 반복 가능한 객체(Array, String, Map, Set...)
  2. 반복문 내에서 배열이나 문자열에서 동작하는 특정 인터페이스가 지원되는 자료구조를 사용하기 때문
  3. 컬렉션 객체가 [Symbol.iterator] 속성을 가지고 있어야만 한다.

—> 사용자 정의 객체의 경우에도 특정 인터페이스 규격을 맞춰준다면 사용 가능

// Array
const fruits = ["apple", "banana", "pear", "watermelon", "orange"];

for (let value of fruits) {
  console.log(value);
  /*
    apple
    banana
    pear
    watermelon
    orange
  */
}

// String
const str = "KOREA";
for (let value of str) {
  console.log(value);
  /*
    K
    O
    R
    E
    A
  */
}

// Number > 반복 가능한 것이 아니므로 Err
const num = 12345;
for (let i of num) {
  console.log(value);
}

// Map
const map1 = new Map([
  ["seoul", 1],
  ["busan", 2],
  ["jeju", 3],
]);
for (let city of map1) {
  console.log(city);
  /*
    ["seoul", 1]
    ["busan", 2]
    ["jeju", 3]
  */
}

for (let [key, value] of map1) {
  // 구조분해 할당 Destructuring assignment
  console.log(key, value);
  /*
    seoul 1
    busan 2
    jeju 3
  */
}

toStirng() 메서드

  • 모든 객체에는 객체를 텍스트 형식으로 출력시키고 할 때 자동으로 호출되는 toString() 메서드가 존재 —> 객체를 나타내는 문자열을 반환 (Object에서 파생된 모든 객체에 상속)
  • 이 메서드가 사용자 정의 객체에서 재정의되면 해당 재정의된 toString() 메서드가 적용 > 그냥 사용하면 [object Object]
// Date
const dateObj = new Date(2030, 5, 25, 20, 40, 8);
strObj = dateObj.toString();
console.log(strObj);
console.log(typeof strObj); // string

// Array
const arrObj = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log(arrObj.toString()); // join처럼... --> 1, 2, 3, 4, 5, 6, 7, 8, 9
console.log(typeof arrObj.toString()); // string

// Object
const obj = new Object();
console.log(obj.toString()); // [object Object]

toString() 메서드 재정의의 목적 : 보다 객체의 정보를 잘 보여주기 위해서

function Person(name, age, hp, gender) {
  this.name = name;
  this.age = age;
  this.hp = hp;
  this.gender = gender;
}

const p1 = new Person("홍길동", 20, "010-1234-5678", "남성");

console.log(p1.toString()); // [object Object]

Person.prototype.toString = function () {
  let result = `객체정보 --> 이름:${this.name}, 나이:${this.age}, 핸드폰:${this.hp}, 성별:${this.gender}`;
  return result;
};

console.log(p1.toString()); // 객체정보 --> 이름:홍길동, 나이:20, 핸드폰:010-1234-5678, 성별:남성

String.prototype.toString 재정의

String.prototype.getLength = function () {
  return this.length;
};

String.prototype.toString = function () {
  return "나의 좌우명 -->" + this.valueOf();
};

let testObj = "일단 해보고 안되면 다시 또 해보자!";

console.log(testObj.getLength()); // 20
console.log(testObj); // 일단 해보고 안되면 다시 또 해보자!
console.log(testObj.toString()); // 나의 좌우명 --> 일단 해보고 안되면 다시 또 해보자!

let testObj = "일단 해보고 안되면 다시 또 해보자!";
alert(testObj); // 일단 해보고 안되면 다시 또 해보자!

let testObj = new String("일단 해보고 안되면 다시 또 해보자!");
alert(testObj); // 나의 좌우명 --> 일단 해보고 안되면 다시 또 해보자!

이터러블 객체와 이터레이터 객체

  1. 이터러블 규약
  • for ...of 반복 구문을 사용하여 객체(반복 가능한 객체)를 반복할 때 > 값이 열거되고 내부에는 @@iterator 메서드가 구현되어 있어야 한다.
  • Symbol.iterator() == @@iterator
  • 자바스크립트 내장 객체 (Array, String, Map, Set, arguments) 등이 이 이터러블 규약을 따름.

—> 이를 ‘반복 가능한 객체’, ‘이터러블 객체’ 등으로 부른다.

결국, 이터러블 객체라면 for ...of 구문으로 반복시키면서 값의 열거가 가능 > 이때, 각 객체별로 열거되는 방식에는 차이가 있다.

ex) 문자열(String) → 한 글자씩 따로 열거

배열(Array) → 요소 단위로 하나씩 열거

이러한 차이를 공통화하기 위해서 이터러블 객체는 내부적으로 @@iterator 메서드를 구현한다.

이때, @@iterator 메서드는 객체의 속성이나 체이닝 상의 객체 중 하나가 Symbol.iterator 키를 속성으로 가져야만 한다.

  1. 이터러블 객체의 생성

이터러블 객체(반복 가능한 객체)는 Symbol.iterator 키를 가진다.

const str1 = "KOREA";
const iterator = str1[Symbol.iterator]();
console.log(iterator);
// 이터레이터 객체는 '이터레이터 규약'에 따라 내부의 next() 메서드를 통해 하나씩 값을 순차적으로 열거
// 이때, 열거는 객체이며 속성으로 vlaue, done 2가지 속성을 가짐
// value(값), done(열거가 끝이면 true, value는 undefined, 아니면 false)

console.log(iterator.next()); // K, done: false
console.log(iterator.next()); // O, done: false
console.log(iterator.next()); // R, done: false
console.log(iterator.next()); // E, done: false
console.log(iterator.next()); // A, done: false
console.log(iterator.next()); // undefined, done: true

for (let value of iterator) {
  console.log(value);
  /*
    K
    O
    R
    E
    A
  */
}

// Array
const ar1 = ["seoul", "busan", "kwangju", "jeju", "dokdo"];
const iter = ar1[Symbol.iterator]();
console.log(iter); // Array Iterator

console.log(iter.next()); // value: seoul, done: false
console.log(iter.next()); // value: busan, done: false
console.log(iter.next()); // value: kwangju, done: false
console.log(iter.next()); // value: jeju, done: false
console.log(iter.next()); // value: dokdo, done: false
console.log(iter.next()); // value: undefined, done: true

// --------------------- //

for (let value of ar1) {
  console.log(value);
}
  1. 이터레이터 객체 구현
let testIterObj = {
  i: 1,
  [Symbol.iterator]: function () {
    return this;
  },
  next: function () {
    //   next() 메서드는 객체를 리턴한다.
    if (this.i < 5) return { value: this.I++, done: false };
    else return { value: undefined, done: true };
  },
};

// const str = "KOREA";
// const iter = str[Symbol.iterator]();
console.log(testIterObj.next()); // value: 1, done: false
console.log(testIterObj.next()); // value: 2, done: false
console.log(testIterObj.next()); // value: 3, done: false
console.log(testIterObj.next()); // value: 4, done: false
console.log(testIterObj.next()); // value: undefined, done: true

const iter = testIterObj[Symbol.iterator]();
for (let value of iter) {
  console.log(value); // 1,2,3,4
}

다형성의 예

다형성을 구현하기 위한 기본적인 OOP 문법

  1. 부모/자식간의 계층적인 구조 > 상속 관계
  2. 메서드 재정의 > 오버라이딩
  3. 재정의된 메서드 호출 > 프로토타입 체이닝상에서 제일 먼저 찾게되는 메서드 호출
// 생성
class Animal {
  constructor(name) {
    this._name = name;
  }
  bark() {
    return "짖다";
  }
}

class Dog extends Animal {
  constructor(name, age) {
    super(name);
    this._age = age;
  }
  bark() {
    return `${this._age}살 짜리 ${this._name}가 짖다. 멍멍`;
  }
}

class Cat extends Animal {
  constructor(name, age) {
    super(name);
    this._age = age;
  }
  bark() {
    return `${this._age}살 짜리 ${this._name}가 짖다. 야옹`;
  }
}
class Bull extends Animal {
  constructor(name, age) {
    super(name);
    this._age = age;
  }
  bark() {
    return `${this._age}살 짜리 ${this._name}가 짖다. 음메`;
  }
}

// 사용
const d1 = new Dog("개", 2);
console.log(d1.bark()); // 2살짜리 개가 짖다. 멍멍

const c1 = new Cat("고양이", 4);
console.log(c1.bark()); // 4살짜리 개가 짖다. 야옹

const b1 = new bull("소", 8);
console.log(b1.bark()); // 8살짜리 개가 짖다. 음메

// 배열
const animals = [new Dog("개", 2), new Cat("고양이", 4), new Bull("소", 8)];

// for ...of
for (let v of animals) {
  console.log(v);
}

function + prototype 기반으로

  1. 설계 클래스(인터페이스)

상속을 받은 자식 클래스에서 메서드가 미구현되었다면 에러를 던져준다 > 강제하는 효과

const Animal = function (name) {
  this._name = name;
};
Animal.prototype.move = function () {
  // 코드 구현
throw new Error("move 메서드가 구현되지 않았습니다");
};
  1. 구현 클래스

자식 클래스에서 move 메서드를 구현하지 않으면 부모 클래스에서 에러를 던져줌으로써 구현을 강제

// implement : 구현하다
const DogImpl = function (name, age) {
  Animal.call(this, name); // Class의 super 역할
  this._age = age;
};

DogImpl.prototype = Object.create(Animal.prototype);
DogImpl.prototype.constructor = DogImpl;

DogImpl.prototype.move = function (args) {
  // 오버라이딩
  console.log(`${args}(${this._name} ,${this._age}살) 이동중`);
  return "멍멍!";
};

// 객체 생성
const d1 = new DogImpl("바둑", 2);
console.log(d1.move("개"));
/*
    개(바둑, 2살) 이동중
    멍멍!
*/

다형성 체크

객체가 특정 클래스의 인스턴스인지 여부를 확인 > instanceof

console.log(d1 instanceof Object);  // true
console.log(d1 instanceof Animal);  // true
console.log(d1 instanceof DogImpl); // true
profile
즐거운 서비스를 추구합니다.

0개의 댓글