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


118

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

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

고전적 상속

// 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);

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

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

감사,

시바



5
하지 당신이 여기에 대해에있어 무엇을 - 코드의 첫 번째 블록 이다 프로토 타입 상속, 클래식 없습니다. 두 번째 코드 블록에는 상속이 전혀 없습니다!
Alnitak 2013 년


@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 이 링크는 하나가 고전적인 상속임을 알려줍니다. 그게 혼란스러운 이유입니다.
SivaRajini

클래식 상속을 피하고 싶은 이유에 대한 자세한 내용은 "클래식 상속은 더 이상 사용되지 않습니다 : 프로토 타입 OO에서 생각하는 방법"을 참조하십시오. vimeo.com/69255635
Eric Elliott

답변:


248

귀하의 질문에서 보여준 코드 샘플은 모두 프로토 타입 상속을 사용합니다. 사실 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));

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


결론

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

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

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


2
훌륭한 답변에 감사드립니다. 생성자 pattern.any 예제와 비교하여 프로토 타입 패턴이 더 나은 방법을 이해해야합니까?
SivaRajini 2013 년

1
내 블로그에 프로토 타입 대 생성자에 대한 비교 비판을 작성했습니다 : aaditmshah.github.io/why-prototypal-inheritance-matters/...
Aadit M 샤

그래서, 우리가 상속을 얻기 위해 자바 스크립트에서 함수를 사용할 때 우리는 고전적인 상속 모델을 사용하고 우리가 일반 객체를 사용할 때 프로토 타입 상속 (둘 다 내부적으로 프로토 타입 상속을 따름)을 사용한다고 말하는 것이 옳을까요?
Swanidhi

1
@Swanidhi 아니요. JavaScript를 사용하는 경우 프로토 타입 상속 모델을 사용하는 것입니다. 그러나 JavaScript에는 두 가지 유형의 프로토 타입 상속이 있습니다. 함수 사용 (예 : 생성자 패턴)과 객체 사용 (예 : 프로토 타입 패턴)입니다.
Aadit M Shah

5
@Swanidhi 아니요. 아직 프로토 타입입니다. JavaScript에는 "클래스"가 없으므로 생성자를 포함하여 클래식 JavaScript에서는 절대로 아무것도 없습니다. 여전히 프로토 타입 상속입니다. 사람들이 고전적 상속과 혼동하는 이상한 형태의 프로토 타입 상속입니다. 간단히, programming with classes = classical inheritance, programming with prototypes = prototypal inheritance, programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance. 그것을 명확히하는 희망.
Aadit M Shah 2015

8

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

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

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/ )


0

개는 동물입니다. 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"에서이를 자세히 탐구합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.