[javascript] 자바 스크립트에서 고전적 상속과 프로토 타입 상속

나는 너무 많은 링크를 봤는데 고전적 상속과 프로토 타입 상속의 차이점에 대해 좋은 아이디어를 얻을 수 없습니까?

나는 이것들로부터 몇 가지를 배웠지 만 여전히 개념에 대해 혼란 스럽습니다.

고전적 상속

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

//superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

//subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);

클래식 상속은 내부에서 프로토 타입 상속을 사용합니까?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

위의 링크 에서 클래식 상속에서 런타임에 새 메서드를 추가 할 수 없음을 배웠습니다 . 이 올바른지? 하지만 위의 코드를 확인하면 prototype을 통해 런타임에 “move”메소드와 모든 메소드를 추가 할 수 있습니다 . 그렇다면 이것은 프로토 타입 기반의 고전적 상속입니까? 그렇다면 실제 클래식 상속과 프로토 타입 상속은 무엇입니까? 나는 그것에 대해 혼란 스럽습니다.

프로토 타입 상속.

function Circle(radius) {
    this.radius = radius;
}
Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};
Circle.prototype.circumference: function () {
    return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);

이것이 고전적 상속과 비슷합니까? 프로토 타입 상속이 무엇인지 완전히 혼란 스럽습니까? 고전적 상속이란 무엇입니까? 고전적 상속이 왜 나쁜가요?

간단한 방법으로 더 잘 이해할 수 있도록 간단한 예를 들어 주시겠습니까?

감사,

시바



답변

귀하의 질문에서 보여준 코드 샘플은 모두 프로토 타입 상속을 사용합니다. 사실 JavaScript로 작성하는 모든 객체 지향 코드는 프로토 타입 상속의 패러다임입니다. 자바 스크립트에는 단순히 고전적인 상속이 없습니다. 이것은 상황을 약간 정리할 것입니다.

                                   Inheritance
                                        |
                         +-----------------------------+
                         |                             |
                         v                             v
                    Prototypal                     Classical
                         |
         +------------------------------+
         |                              |
         v                              v
Prototypal Pattern             Constructor Pattern

보시다시피 프로토 타입 상속과 고전적 상속은 상속의 두 가지 다른 패러다임입니다. Self, Lua 및 JavaScript와 같은 일부 언어는 프로토 타입 상속을 지원합니다. 그러나 C ++, Java 및 C #과 같은 대부분의 언어는 고전적 상속을 지원합니다.


객체 지향 프로그래밍의 빠른 개요

프로토 타입 상속과 고전적 상속은 모두 객체 지향 프로그래밍 패러다임입니다 (즉, 객체를 다룹니다). 객체는 단순히 실제 개체의 속성을 캡슐화하는 추상화입니다 (예 : 프로그램에서 실제 단어를 나타냄). 이것을 추상화라고합니다.

추출: 컴퓨터 프로그램에서 실제 사물의 표현.

이론적으로 추상화는 “특정 예에서 공통 특징을 추출하여 형성된 일반적인 개념”으로 정의됩니다. 그러나이 설명을 위해 앞서 언급 한 정의를 대신 사용할 것입니다.

이제 일부 개체에는 많은 공통점이 있습니다. 예를 들어 진흙 자전거와 할리 데이비슨은 공통점이 많습니다.

진흙 자전거 :

진흙 자전거.

할리 데이비슨 :

할리 데이비슨

진흙 자전거와 Harley Davidson은 모두 자전거입니다. 따라서 자전거는 진흙 자전거와 할리 데이비슨의 일반화입니다.

                   Bike
                     |
    +---------------------------------+
    |                                 |
    v                                 v
Mud Bike                       Harley Davidson

위의 예에서 자전거, 진흙 자전거 및 Harley Davidson은 모두 추상화입니다. 그러나 자전거는 진흙 자전거와 Harley Davidson의보다 일반적인 추상화입니다 (즉, 진흙 자전거와 Harley Davidson은 모두 특정 유형의 자전거입니다).

일반화 : 보다 구체적인 추상화의 추상화입니다.

객체 지향 프로그래밍에서 우리는 객체 (실제 엔티티의 추상화)를 만들고 클래스 나 프로토 타입을 사용하여 이러한 객체의 일반화를 만듭니다. 일반화는 상속을 통해 생성됩니다. 자전거는 진흙 자전거의 일반화입니다. 따라서 진흙 자전거는 자전거에서 물려받습니다.


고전적인 객체 지향 프로그래밍

고전적인 객체 지향 프로그래밍에는 클래스와 객체라는 두 가지 유형의 추상화가 있습니다. 앞에서 언급했듯이 객체는 실제 개체의 추상화입니다. 반면에 클래스는 객체 또는 다른 클래스의 추상화입니다 (예 : 일반화). 예를 들어 다음을 고려하십시오.

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | Man            | Class of object johnDoe.              |
| 3                    | Human          | Superclass of class Man.              |
+----------------------+----------------+---------------------------------------+

고전적인 객체 지향 프로그래밍 언어에서 볼 수 있듯이 객체는 추상화 일뿐 (즉 모든 객체의 추상화 수준이 1)이고 클래스는 일반 화일뿐입니다 (즉, 모든 클래스의 추상화 수준이 1보다 큼).

클래식 객체 지향 프로그래밍 언어의 객체는 클래스를 인스턴스화해야만 만들 수 있습니다.

class Human {
    // ...
}

class Man extends Human {
    // ...
}

Man johnDoe = new Man();

고전적인 객체 지향 프로그래밍 언어의 요약에서 객체는 실제 엔티티의 추상화이고 클래스는 일반화 (즉, 객체 또는 다른 클래스의 추상화)입니다.

따라서 추상화 수준이 증가함에 따라 개체가 더 일반적이되고 추상화 수준이 감소함에 따라 개체가 더 구체적이됩니다. 이러한 의미에서 추상화 수준은보다 구체적인 개체에서보다 일반적인 개체에 이르는 범위와 유사합니다.


프로토 타입 객체 지향 프로그래밍

프로토 타입 객체 지향 프로그래밍 언어는 전통적인 객체 지향 프로그래밍 언어보다 훨씬 간단합니다. 프로토 타입 객체 지향 프로그래밍에서는 한 가지 유형의 추상화 (즉, 객체) 만 있기 때문입니다. 예를 들어 다음을 고려하십시오.

+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity |                Comments               |
+----------------------+----------------+---------------------------------------+
| 0                    | John Doe       | Real World Entity.                    |
| 1                    | johnDoe        | Variable holding object.              |
| 2                    | man            | Prototype of object johnDoe.          |
| 3                    | human          | Prototype of object man.              |
+----------------------+----------------+---------------------------------------+

프로토 타입 객체 지향 프로그래밍 언어에서 볼 수 있듯이 객체는 실제 엔티티 (이 경우 단순히 객체라고 함) 또는 다른 객체 (이 경우 추상화하는 객체의 프로토 타입이라고 함)의 추상화입니다. 따라서 프로토 타입은 일반화입니다.

프로토 타입 객체 지향 프로그래밍 언어의 객체는 전일로 (즉, 무에서) 또는 다른 객체 (새로 생성 된 객체의 프로토 타입이 됨)에서 생성 될 수 있습니다.

var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);

저의 겸손한 의견으로는 프로토 타입 객체 지향 프로그래밍 언어는 다음과 같은 이유로 고전적인 객체 지향 프로그래밍 언어보다 더 강력합니다.

  1. 추상화 유형은 한 가지뿐입니다.
  2. 일반화는 단순히 객체입니다.

지금 쯤이면 고전적 상속과 프로토 타입 상속의 차이를 깨달았을 것입니다. 클래식 상속은 다른 클래스에서 상속 된 클래스로 제한됩니다. 그러나 프로토 타입 상속에는 다른 프로토 타입에서 상속 된 프로토 타입뿐만 아니라 프로토 타입에서 상속 된 객체도 포함됩니다.


프로토 타입 클래스 동형

프로토 타입과 클래스가 매우 유사하다는 것을 알고 계실 것입니다. 사실입니다. 그들은. 사실 그들은 매우 유사해서 실제로 프로토 타입을 사용하여 클래스를 모델링 할 수 있습니다.

function CLASS(base, body) {
    if (arguments.length < 2) body = base, base = Object.prototype;
    var prototype = Object.create(base, {new: {value: create}});
    return body.call(prototype, base), prototype;

    function create() {
        var self = Object.create(prototype);
        return prototype.hasOwnProperty("constructor") &&
            prototype.constructor.apply(self, arguments), self;
    }
}

위의 CLASS함수를 사용하여 클래스처럼 보이는 프로토 타입을 만들 수 있습니다.

var Human = CLASS(function () {
    var milliseconds = 1
      , seconds      = 1000 * milliseconds
      , minutes      = 60 * seconds
      , hours        = 60 * minutes
      , days         = 24 * hours
      , years        = 365.2425 * days;

    this.constructor = function (name, sex, dob) {
        this.name = name;
        this.sex = sex;
        this.dob = dob;
    };

    this.age = function () {
        return Math.floor((new Date - this.dob) / years);
    };
});

var Man = CLASS(Human, function (Human) {
    this.constructor = function (name, dob) {
        Human.constructor.call(this, name, "male", dob);
        if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
    };
});

var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));

그러나 그 반대는 사실이 아닙니다 (즉, 프로토 타입을 모델링하기 위해 클래스를 사용할 수 없습니다). 프로토 타입은 객체이지만 클래스는 객체가 아니기 때문입니다. 그것들은 완전히 다른 유형의 추상화입니다.


결론

요약하면 추상화는 “특정 예제에서 공통 기능을 추출하여 형성된 일반적인 개념” 이고 일반화는 “더 구체적인 추상화의 추상화”라는 것을 배웠습니다 . 우리는 또한 프로토 타입과 고전적 상속의 차이점과 둘 다 같은 동전의 두 얼굴 인 방법에 대해서도 배웠습니다.

이별 노트에서 프로토 타입 상속에는 프로토 타입 패턴과 생성자 패턴의 두 가지 패턴이 있음을 언급하고 싶습니다. 프로토 타입 패턴은 프로토 타입 상속의 표준 패턴 인 반면 생성자 패턴은 프로토 타입 상속이 클래식 상속처럼 보이도록 만드는 데 사용됩니다. 개인적으로 저는 프로토 타입 패턴을 선호합니다.

추신 : 나는 블로그 게시물 ” 왜 프로토 타입 상속이 중요한가 “를 작성하고 ” 클래식보다 프로토 타입 상속의 이점? ” 이라는 질문에 답한 사람 입니다. 내 대답은 받아 들여진 대답입니다.


답변

상속에 들어가기 전에 자바 스크립트로 인스턴스 (객체)를 생성하는 두 가지 기본 모델을 살펴 보겠습니다 .

클래식 모델 : 청사진 (클래스)에서 객체 생성

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();

프로토 타입 모델 : 개체는 다른 개체에서 직접 생성됩니다.

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);

두 경우 모두 프로토 타입 개체를 사용하여 개체를 연결하여 상속 *을 수행합니다.

(* 기본 클래스 메서드는 프로토 타입 개체를 통해 파생 클래스를 통해 액세스 할 수 있으며 파생 클래스에 명시 적으로 존재하지 않아도됩니다.)

다음은 더 잘 이해할 수있는 좋은 설명입니다 ( http://www.objectplayground.com/ )


답변

개는 동물입니다. Suzanna는 개입니다. 고전 상속에서 Animal하는 클래스 Dog의 서브 클래스입니다 Animal, 그리고 suzanna의 인스턴스입니다 Dog.

프로토 타입 상속에는 클래스가 없습니다. 당신은이 animal대상이다. A dog는 복제하고 확장하는 또 다른 개체 animal(프로토 타입 개체)입니다. suzanna복사하고 확장하는 세 번째 객체 dog입니다.

let animal = {hasChlorophyl: false};

let dog = Object.create(animal);
Object.assign(dog, {
  speak() {
    console.log("Woof!");
  }
});

let suzanna = Object.create(dog);
Object.assign(suzanna, {
  name: "Suzanna"
});

suzanna.speak();

당신이 작성하는 경우 Dog대신 dog당신이 만드는 특히, Dog“생성자”기능의 어떤 종류를, 다음 당신은 프로토 타입 상속을 수행하지 않는; 당신은 (의사) 고전적인 상속을하고 있습니다. Object.create()이것을 달성하기 위해 사용 하고 있다는 사실 이 프로토 타입 상속을하고 있음을 의미하지는 않습니다.

실제로 JavaScript는 프로토 타입 상속 만 지원합니다. 혼란스러운 new연산자와 .prototype속성은 프로토 타입 상속을 (의사) 고전 상속처럼 보이게하기 위해 존재합니다.

Douglas Crockford는 그의 저서 “JavaScript : The Good Parts”에서이를 자세히 탐구합니다.


답변