ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Javascript] 프로토타입, 개념, 상속, object.assign
    웹/JavaScript 2023. 1. 14. 11:25

    I. 프로토타입 prototype

    • 자바스크립트는 프로토타입 기반의 객체지향 프로그래밍을 지원하는 언어
    • 오늘날에는 클래스가 더 널리 사용되지만, 사용하기에 따라 보다 강력하고 효율적

     

    ⭐ 자바스크립트의 모든 객체는 Prototype을 가짐

    const obj = {};
    console.log(obj);
    • [[Prototype]] 펼쳐서 살펴볼 것

     

    console.log(
    obj.toString()
    );

     

    console.log(
    obj.valueOf()
    );
    • 빈 객체임에도 위의 메서드들이 사용 가능한 것은 프로토타입 때문




     

    II. Object - 모든 것의 조상

    • 아래 각 객체의 [[Prototype]]의 [[Prototype]]이 Object임 확인
    • 각 [[Prototype]]의 메서드들 확인
    console.log(
    new String('')
    );

     

    console.log(
    []
    );

     

    console.log(
    new Map()
    );
    • constructor 항목에서 각각의 생성자 확인 가능
      • 객체 리터럴({} 등)의 생성자는 Object()임 알 수 있음
    console.log(
    {}
    );



    💡 프로토타입 체인

    • 특정 객체에 호출된 프로퍼티가 없다면 프로토타입을 거슬러 올라감
    • 예: Array에는 valueOf가 없지만 그 프로토타입인 Object에는 있으므로 호출 가능




     

    II. 코드로 프로토타입에 접근하기

    1. __proto__ 접근자 사용 - Object.prototype의 프로퍼티

    • 👉 MDN 문서 보기
    • ⚠️ Deprecated - 사라질 기능, 사용 권장되지 않음. 위의 링크 확인
    console.log(
    {}.__proto__
    );

     

    console.log(
    new Number(2).__proto__
    );

     

    console.log(
    [1, 2, 3].__proto__
    );



    💡같은 종류는 프로토타입 공유 확인

    console.log(
    {}.__proto__ === { x: 1, y: 2 }.__proto__
    );

     

    console.log(
    [1, 2, 3].__proto__ === [4, 5].__proto__
    );

     

    console.log(
    new String('가나다').__proto__ === new String('ABC').__proto__
    );



    💡최상위, 공통조상은 Object임 확인

    console.log(
    {}.__proto__ === [].__proto__,
    {}.__proto__ === new Number(0).__proto__,
    [].__proto__ === new Number(0).__proto__
    );

     

    console.log(
    {}.__proto__ === [].__proto__.__proto__,
    {}.__proto__ === new Number(0).__proto__.__proto__,
    [].__proto__.__proto__ === new Number(0).__proto__.__proto__
    );

     

    // ⭐ 그 위로는 null
    console.log(
    {}.__proto__.__proto__
    );




    2. Object.getPrototypeOf

    • 수정할 때는 Object.setPrototypeOf 사용
    • ⭐ __proto__ 대신 이 기능을 사용할 것
    console.log(
    Object.getPrototypeOf({})
    );

     

    console.log(
    Object.getPrototypeOf([]) === [].__proto__
    );




    3. ⭐ 생성자 함수에서는 prototype으로 프로토타입 접근 가능

    • 즉 function으로 선언된 함수들에서
    function Person (name) {
    this.name = name;
    }

     

    // 인스턴스들에 공유될 프로토타입에 다음과 같이 접근
    console.log(Person.prototype);

     

    const hong = new Person('홍길동');
    console.log(hong);
    • [[Prototype]]이 두 단계로 있음 확인 (Person - Object)




    console.log(
    String.prototype
    );

     

    console.log(
    Number.prototype
    );

     

    console.log(
    Set.prototype
    );

     

    // 생성자 함수로 동작하지 않는 빌트인 객체
    console.log(
    Math.prototype
    );




     

    IV. 인스턴스 vs 프로토타입 프로퍼티

    🔗 5-2강 예제 다시보기

    function YalcoChicken (name, no) {
    this.name = name;
    this.no = no;
    this.introduce = function () {
    return `안녕하세요, ${this.no}${this.name}점입니다!`;
    }
    }
    const chain1 = new YalcoChicken('판교', 3);

     

    // 본사에서 새 업무를 추가
    YalcoChicken.prototype.introEng = function () {
    return `Welcome to Yalco Chicken at ${this.name}!`;
    };
    console.log(chain1.introEng());
    console.log(new YalcoChicken('강남', 17).introEng());



    ⭐ 인스턴스의 로그를 펼쳐 각 함수가 속한 레벨 확인

    console.log(chain1);
    • introduce : 만들어지는 인스턴스마다 각각 있음
    • introEng : 프로토타입에만 있음 - 메모리 절약



    💡 따라서 메모리 사용을 최소화하려면 아래와 같이 작성

    function YalcoChicken (name, no) {
    this.name = name;
    this.no = no;
    }
    // 공통된 요소들은 프로토타입 프로퍼티로
    YalcoChicken.prototype.titleEng = 'YalcoChicken';
    YalcoChicken.prototype.introduce = function () {
    return `안녕하세요, ${this.no}${this.name}점입니다!`;
    }
    YalcoChicken.prototype.introEng = function () {
    return `Welcome to ${this.titleEng} at ${this.name}!`;
    };

     

    const chain1 = new YalcoChicken('판교', 3);
    const chain2 = new YalcoChicken('강남', 17);
    const chain3 = new YalcoChicken('제주', 24);
    console.log(chain1.introduce());
    console.log(chain1.introEng());

     

    // 인스턴스 레벨과 프로토타입 프로퍼티들 비교
    console.log(chain1, chain2, chain3);

     

    💡 프로토타입 레벨의 함수를 인스턴스 레벨에서 덮어쓰기 가능

    const chain4 = new YalcoChicken('평양', 456);
    chain4.introduce = function () {
    return `어서오시라요, ${this.no}${this.name}점입네다!`;
    }
    console.log(chain4.introduce());




    ⭐ 클래스에도 적용 가능

    class Dog {
    constructor (name) {
    this.name = name;
    }
    }
    Dog.prototype.sound = '멍멍';
    Dog.prototype.bark = function () { // 메서드로 만드는 것과 같음
    console.log(this.sound)
    };
    const badugi = new Dog('바둑이');
    badugi.bark();

     

    console.log(badugi);
    • 프로토타입 확인

    👉 MDN 문서 보기

    function Bird (name, sound) {
    this.name = name;
    this.sound = sound;
    }
    Bird.prototype.fly = function () {
    console.log(`${this.name} ${this.sound} 비행중`);
    }
    function Eagle (name, sound, prey) {
    this.name = name;
    this.sound = sound;
    this.prey = prey;
    }
    Eagle.prototype.hunt = function () {
    console.log(`${this.name} ${this.prey} 사냥중`);
    }

     

    const bird = new Bird('새돌이', '파닥파닥');
    const eagle = new Eagle('독돌이', '푸드덕', '토끼');
    console.log(bird);
    console.log(eagle);

     

    bird.fly();
    eagle.hunt();
    • 프로토타입 이름 (Object로 표시) 에 크게 신경쓰지 말 것
    • Eagle이 Bird로부터 상속받도록 만들려면?



    I. 프로토타입으로 상속하기

    ⭐ Object.create 메서드

    function Bird (name, sound) {
    this.name = name;
    this.sound = sound;
    }
    Bird.prototype.fly = function () {
    console.log(`${this.name} ${this.sound} 비행중`);
    }
    function Eagle (name, sound, prey) {
    this.name = name;
    this.sound = sound;
    this.prey = prey;
    }
    // ⚠️ 순서 주의! 상속을 먼저 받음
    Eagle.prototype = Object.create(Bird.prototype);
    // Eagle.prototype = Bird.prototype; // 💡 비교해 볼 것
    // 상속 이후 자신의 프로토타입 작성
    Eagle.prototype.hunt = function () {
    console.log(`${this.name} ${this.prey} 사냥중`);
    }

     

    const bird = new Bird('새돌이', '파닥파닥');
    const eagle = new Eagle('독돌이', '푸드덕', '토끼');
    // 상속 구조 확인
    console.log(bird);
    console.log(eagle);

     

    console.log(
    eagle instanceof Bird,
    bird instanceof Eagle
    );

     

    bird.fly();
    eagle.fly();
    eagle.hunt();
    • 상속을 먼저 하고 자체 프로토타입 프로퍼티 입력
    • Object.create... 대신 Bird.prototype 대입 결과 비교 - eagle과 bird 모두 확인




     

    II. 부모의 생성자 활용하기

    • 생성자에서 중복되는 부분 위임
    • class에서는 constructor에서 super 사용
    function Bird (name, sound) {
    this.name = name;
    this.sound = sound;
    }
    Bird.prototype.fly = function () {
    console.log(`${this.name} ${this.sound} 비행중`);
    }
    function Eagle (name, sound, prey) {
    // 💡 call 호출방식 사용
    Bird.call(this, name, sound);
    this.prey = prey
    }
    Eagle.prototype = Object.create(Bird.prototype);
    Eagle.prototype.hunt = function () {
    console.log(`${this.name} ${this.prey} 사냥중`);
    }

     

    const eagle = new Eagle('독돌이', '푸드덕', '토끼');
    console.log(eagle);

     

    eagle.fly();
    eagle.hunt();




     

    III. 클래스로 구현

    ⭐ 클래스 역시 프로토타입을 기반으로 구현됨

    클래스와 프로토타입

    • 클래스의 메서드는 프로토타입으로 들어가게 됨
    • extends - 프로토타입 상속도를 만듦
    function AAA () {
    this.field = 1;
    this.run = function () { return 1; };
    }
    class BBB {
    field = 1;
    run = function () { return 1; }
    }
    class CCC {
    field = 1;
    run () { return 1; }
    }
    console.log(new AAA()); // 인스턴스에 속함
    console.log(new BBB()); // 인스턴스에 속함
    console.log(new CCC()); // 프로토타입에 속함
    • run 함수 또는 메서드가 속한 곳 비교
    • 필드는 양쪽 모두 인스턴스에 속함



    // ♻️ 새로고침 후 실행
    class Bird {
    constructor (name, sound) {
    this.name = name;
    this.sound = sound;
    }
    fly () {
    console.log(`${this.name} ${this.sound} 비행중`);
    }
    }
    class Eagle extends Bird {
    constructor (name, sound, prey) {
    super(name, sound);
    this.prey = prey;
    }
    hunt () {
    console.log(`${this.name} ${this.prey} 사냥중`);
    }
    }

     

    const bird = new Bird('새돌이', '파닥파닥');
    const eagle = new Eagle('독돌이', '푸드덕', '토끼');
    console.log(bird);
    console.log(eagle);

     

    bird.fly();
    eagle.fly();
    eagle.hunt();




     💡 인스턴스의 클래스/생성자함수 이름 출력

    • 무엇의 인스턴스인지 프로그램상에서 이름으로 파악할 때 유용
    console.log(
    Object.getPrototypeOf(bird).constructor.name,
    Object.getPrototypeOf(eagle).constructor.name,
    Object.getPrototypeOf(
    Object.getPrototypeOf(eagle)
    ).constructor.name,
    );




     

    IV. Mixin - Object.assign으로 조립하기

    • 상속 - 한 부모로부터만 물려받음
    • 믹스인 - 여럿을 조합하여 가져올 수 있음
    const runner = {
    run : function () {
    console.log(`${this.name} 질주중`);
    }
    }
    const swimmer = {
    swim: function () {
    console.log(`${this.name} 수영중`);
    }
    }
    const flyer = {
    fly: function () {
    console.log(`${this.name} 비행중`);
    }
    }
    const hunter = {
    hunt: function () {
    console.log(`${this.name} 사냥중`);
    }
    }

     

    class Owl {
    constructor (name) {
    this.name = name;
    }
    }
    class FlyingFish {
    constructor (name) {
    this.name = name;
    }
    }
    class PolarBear {
    constructor (name) {
    this.name = name;
    }
    }

     

    Object.assign(Owl.prototype, flyer, hunter);
    Object.assign(FlyingFish.prototype, flyer, swimmer);
    Object.assign(PolarBear.prototype, runner, swimmer, hunter);

     

    const owl = new Owl('붱돌이');
    const f_fish = new FlyingFish('날치기');
    const p_bear = new PolarBear('극곰이');
    console.log(owl);
    console.log(f_fish);
    console.log(p_bear);

     

    owl.fly();
    owl.hunt();
    f_fish.swim();
    f_fish.fly();
    p_bear.run();
    p_bear.swim();
    p_bear.hunt();
     
Designed by Tistory.