4. 클래스와 객체의 혼합

주요 개념 및 내용

일반적인 클래스 지향 언어(자바와 같은)와 자바스크립트의 클래스 구조적 방식은 다름. 일반적으로 자바와 같은 언어에서 클래스는 설계도와 같고, new 연산자를 통해서 인스턴스를 만들어내는데, 이때 자동으로 메모리에 로딩되는 영역이 달라리게 된다. 따라서 new를 통해서 얻어낸 정보는 독립된 하나의 공간이라고 볼 수 있다.

근데 자바스크립트에서 클래스란 존재하지 않는다. 일단 클래스란 개념이 없는 언어이고, OOP적 사상을 function 기반으로 흉내를 내고 있는것에 불과하다. 그래서 자동으로 클래스 복사작업이 이루어지지 않는다. 즉, 자바스크립트 문법으로 직접적으로 복사 해주는 작업들이 필요해진다..

주의사항

  • 클래스란 존재하지 않는다. 다만 흉내를 낼 뿐이다. 따라서 복사작업을 직접 건드려야한다.

  • 자바스크립트에서 객체간의 서로 연결이라 볼 수 있다는 점 숙지.

  • 클래스의 부재된 자동복사 및 할당에 대한 방편으로 믹스인이란 개념이 등장

  • 다중상속 같은 흉내도 가능함 -> 이러면 동일명 메서드 참조에 대한 이슈.. 다이아몬드 문제 발생은 우려대상

다형성

다형성은 명시적의사 다형성과 암시적 다형성이 있다.

  • 명시적 다형성 및 암시적 다형성:

    명시적 다형성인 경우 명시적 바인딩(call 함수사용)을 통해서 함수호출을 하는 것이라 보면되고, 암시적 다형성인 경우 암시적 바인딩을 통해서 함수호출을 하는 경우라 보면 될 것 같다.

    명시적 다형성은 구분지어서 호출이 필요한 경우에 사용된다. 예를들으어 아래 코드에서 Vechicle, Car는 둘다 dirve라는 함수를 갖고 있다. 여기에서 명시적으로 호출하고 싶을 때!

      var Vehicle = {
        engines: 1,
        ignition: function () {
          console.log("엔진을 켠다");
        },
        drive: function () {
          this.ignition();  // this?
          console.log("방향을 맞추고 앞으로 간다!");
        }
      };
    
      var Car = mixin(Vehicle, {
        wheels: 4,
        drive: function () {
          Vehicle.drive.call(this); // 명시적 다형성 : this? -> targetObj 현재 객체 / 현재 drive
          Vehicle.drive();          // 상대적 다형성 : 실행은 같다 drive 에서 this의 참조가 달라진다. -> Vehicle
          console.log(this.wheels + "개의 바퀴로 굴러간다");
        }
      });
  • 중복가려짐

    5장 참조

  • 기생상속

    5장 참조

믹스인

믹스인이란 한 객체의 정보를 다른 객체로 정보를 복사해올 수 있는 클래스의 부재기능을 구현한 것이라 보면될 듯 여기에서는 상속, 객체정보 복사 등에 활용 중이고, 상속 또는 객체 정보 복사는 완벽한 OOP 언어들의 인스턴스화 같은 레퍼런스 분리를 구현하지는 못하고 있다.

  • 명시적 믹스인

      function mixin(sourceObj, targetObj) {
        for (var key in sourceObj) {
            if (!(key in targetObj)) {
                targetObj[key] = sourceObj[key];
            }
        }
        return targetObj;
      }
    
      var Vehicle = {
        engines: 1,
        ignition: function () {
          console.log("엔진을 켠다");
        },
        drive: function () {
          this.ignition();  // this?
          console.log("방향을 맞추고 앞으로 간다!");
        }
      };
    
      var Car = mixin(Vehicle, {
        wheels: 4,
        drive: function () {
          Vehicle.drive.call(this); // 명시적 다형성 : this? -> targetObj 현재 객체 / 현재 drive
          Vehicle.drive();          // 상대적 다형성 : 실행은 같다 drive 에서 this의 참조가 달라진다.
          console.log(this.wheels + "개의 바퀴로 굴러간다");
        }
      });
  • 암시적 믹스인 -> 설명이 좀헷갈림. 역자와 싸워야할거 같음

    권장히지 않음 Something.cool.call(this)에서 명시적호출을 하는데 여기서 Something의 작동을 Another를 혼재 했다는건데.. 저 위에서도 그렇게 해놓고 뭔소린지...

      // 암시적 믹스인
      var Something = {
        cool: function () {
          this.greeting = "Hello World, Something";
          this.count = this.count ? this.count + 1 : 1;
        }
      };
    
      Something.cool();
      console.log(Something.greeting);
      console.log(Something.count);
    
      var Another = {
        cool: function () {
        // 여기서 암시적 믹스인 -> mixin 유틸을 사용하지 않고 복사해서 그런가..?ㅡ_ㅡ??
        Something.cool.call(this);
        // Something.cool();
        // this.greeting = "Hello World, Another";
        // this.count = this.count ? this.count + 1 : 1;
        }
      };
      Another.cool();
      console.log(Another.greeting);
      console.log(Another.count);

테스트 코드

(function () {

  // class

  // Vehicle
  // class Vehicle {
  //   constructor(engines) {
  //     this.engines = engines;
  //   }
  //
  //   ignition() {
  //     console.log(this.engines, "엔진을 켠다");
  //   }
  //
  //   drive() {
  //     this.ignition();
  //     console.log("방향을 맞추고 앞으로 간다");
  //   }
  // }
  //
  // // Car
  // class Car extends Vehicle {
  //   constructor(engines, wheels) {
  //     super(engines);
  //     this.wheels = wheels;
  //   }
  //
  //   drive() {
  //     super.drive();
  //     console.log(this.wheels, "개의 바퀴로 굴러간다");
  //   }
  // }
  //
  // var car = new Car(2, 4);
  // car.drive();


  // 1번째 mixin
  function mixin(sourceObj, targetObj) {
    for (var key in sourceObj) {
      if (!(key in targetObj)) {
        targetObj[key] = sourceObj[key];
      }
    }
    return targetObj;
  }

  var Vehicle = {
    engines: 1,
    ignition: function () {
      console.log("엔진을 켠다");
    },
    drive: function () {
      this.ignition();  // this?
      console.log("방향을 맞추고 앞으로 간다!");
    }
  };

  var Car = mixin(Vehicle, {
    wheels: 4,
    drive: function () {
      Vehicle.drive.call(this); // 명시적 다형성 : this? -> targetObj 현재 객체 / 현재 drive
      Vehicle.drive();          // 상대적 다형성 : 실행은 같다 drive 에서 this의 참조가 달라진다.
      console.log(this.wheels + "개의 바퀴로 굴러간다");
    }
  });

  // Car.drive();
  // Vehicle.drive();


  // 2번째 mixin
  function mixin2(sourceObj, targetObj) {
    for (var key in sourceObj) {
      targetObj[key] = sourceObj[key];
    }
    return targetObj;
  }

  var Car2 = mixin2(Vehicle, {});
  // Car2.drive();
  mixin2({
    wheels: 4,
    drive: function () {
      Vehicle.drive.call(this);
      Vehicle.drive();
      console.log(this.wheels + "개의 바퀴로 굴러간다");
    }
  }, Car2);
  // Car2.drive();

  // 전통적인 자바스크립트 Class
  function VehicleF() {
    this.engines = 1;
  }

  VehicleF.prototype.ignition = function () {
    console.log("엔진을 켠다");
  };
  VehicleF.prototype.drive = function () {
    this.ignition();
    console.log("방향을 맞추고 앞으로 간다!!");
  };

  function CarF() {
    var car = new VehicleF();

    // Car 필드
    car.wheels = 4;

    // 1. VehicleF::dirve 내부 레퍼런스 저장
    var vehicleDrive = car.drive;

    // 2. 오버라이딩
    car.drive = function () {
      vehicleDrive.call(this); // this -> VehicleF와 매핑?
      // vehicleDrive.call(car); // this -> VehicleF와 매핑?
      console.log(this.wheels + "개의 바퀴로 굴러간다");
    };
    // car.drive = () => {
    //   vehicleDrive.call(this); // car or this
    //   console.log(this.wheels + "개의 바퀴로 굴러간다");
    // };
    return car;
  }

  // var myCar = new CarF();
  // var myCar = CarF(); // car함수가 반환되므로... new 를 쓰지않아도 동일한 내용 가능, 불필요한 객체생성을 줄일 수 있다 / 클로저가 생김
  // myCar.drive();


  // 암시적 믹스인
  var Something = {
    cool: function () {
      this.greeting = "Hello World, Something";
      this.count = this.count ? this.count + 1 : 1;
    }
  };

  Something.cool();
  console.log(Something.greeting);
  console.log(Something.count);

  var Another = {
    cool: function () {
      // 여기서 암시적 믹스인
      // Something.cool.call(this);
      // Something.cool();
      this.greeting = "Hello World, Another";
      this.count = this.count ? this.count + 1 : 1;
    }
  };

  Another.cool();
  console.log(Another.greeting);
  console.log(Another.count);
  console.log(Something.count);

})();

Last updated