그래서 나는 마침내 몇 년 동안 내 발 끌기를 멈추고 JavaScript를 "적절하게"배우기로 결정했습니다. 언어 디자인에서 가장 머리를 긁는 요소 중 하나는 상속 구현입니다. Ruby에서 경험을 쌓은 후 클로저와 동적 타이핑을 보게되어 정말 기뻤습니다. 그러나 내 인생에서 상속을 위해 다른 인스턴스를 사용하여 객체 인스턴스에서 어떤 이점이 있는지 알아낼 수 없습니다.
그래서 나는 마침내 몇 년 동안 내 발 끌기를 멈추고 JavaScript를 "적절하게"배우기로 결정했습니다. 언어 디자인에서 가장 머리를 긁는 요소 중 하나는 상속 구현입니다. Ruby에서 경험을 쌓은 후 클로저와 동적 타이핑을 보게되어 정말 기뻤습니다. 그러나 내 인생에서 상속을 위해 다른 인스턴스를 사용하여 객체 인스턴스에서 어떤 이점이 있는지 알아낼 수 없습니다.
답변:
나는이 답변이 3 년 늦었다는 것을 알고 있지만, 현재 답변이 프로토 타입 상속이 클래식 상속보다 얼마나 좋은지에 대한 충분한 정보를 제공하지 못한다고 생각합니다 .
먼저 프로토 타입 상속을 방어 할 때 JavaScript 프로그래머가 언급하는 가장 일반적인 논증을 살펴 보자 (현재 논란에서이 논증을 취하고있다).
이제 이러한 주장은 모두 유효하지만 아무도 이유를 설명하지 않았습니다. 어린이에게 수학 공부가 중요하다고 말하는 것과 같습니다. 물론이지만, 아이는 확실히 신경 쓰지 않습니다. 중요하다고 말함으로써 수학과 같은 아이를 만들 수 없습니다.
프로토 타입 상속의 문제점은 JavaScript의 관점에서 설명된다는 것입니다. JavaScript를 좋아하지만 JavaScript의 프로토 타입 상속이 잘못되었습니다. 고전 상속과 달리 프로토 타입 상속에는 두 가지 패턴이 있습니다.
불행히도 JavaScript는 프로토 타입 상속의 생성자 패턴을 사용합니다. 이것은 JavaScript가 만들어 졌을 때 (JS의 제작자 인) Brendan Eich가 Java (고전 상속이있는)처럼 보이기를 원했기 때문입니다.
우리는 Visual Basic과 같은 보완 언어가 당시 Microsoft의 언어 패밀리에서 C ++로 사용 되었기 때문에 Java의 동생으로 밀어 붙였습니다.
사람들이 JavaScript에서 생성자를 사용할 때 생성자가 다른 생성자로부터 상속받는 것으로 생각하기 때문에 이것은 나쁘다. 이것은 잘못이다. 프로토 타입 상속에서 객체는 다른 객체로부터 상속됩니다. 생성자는 결코 그림에 들어오지 않습니다. 이것이 대부분의 사람들을 혼란스럽게하는 것입니다.
고전적인 상속 기능을 가진 Java와 같은 언어를 사용하는 사람들은 생성자가 클래스처럼 보이지만 클래스처럼 동작하지 않기 때문에 더욱 혼란스러워집니다. 로 더글러스 크록 포드는 말했다 :
이 간접적 인 의도는 언어를 고전적으로 훈련 된 프로그래머에게 친숙하게 보이도록하기위한 것이지만 Java 프로그래머가 JavaScript에 대해 매우 낮은 견해에서 볼 수 있듯이 그렇게하지 못했습니다. JavaScript의 생성자 패턴은 고전적인 군중에게는 호소력이 없었습니다. 또한 JavaScript의 진정한 프로토 타입 특성을가 렸습니다. 결과적으로 언어를 효과적으로 사용하는 방법을 아는 프로그래머는 거의 없습니다.
거기 있어요 말의 입에서 곧장.
프로토 타입 상속은 객체에 관한 것입니다. 객체는 다른 객체의 속성을 상속합니다. 그것이 전부입니다. 프로토 타입 상속을 사용하여 오브젝트를 작성하는 두 가지 방법이 있습니다.
참고 : JavaScript는 객체 위임 과 연결 을 복제하는 두 가지 방법을 제공합니다 . 이제부터는 "복제"라는 단어를 사용하여 위임을 통한 상속을 독점적으로, "복사"라는 단어를 사용하여 연결을 통한 상속을 독점적으로 언급합니다.
충분한 이야기. 몇 가지 예를 봅시다. 반경의 원이 있다고 가정 해보십시오 5
.
var circle = {
radius: 5
};
반지름으로부터 원의 면적과 원주를 계산할 수 있습니다.
circle.area = function () {
var radius = this.radius;
return Math.PI * radius * radius;
};
circle.circumference = function () {
return 2 * Math.PI * this.radius;
};
이제 다른 반경의 원을 만들고 싶습니다 10
. 이를 수행하는 한 가지 방법은 다음과 같습니다.
var circle2 = {
radius: 10,
area: circle.area,
circumference: circle.circumference
};
그러나 JavaScript는 더 나은 방법 위임을 제공합니다 . 이 Object.create
기능은 다음을 수행하는 데 사용됩니다.
var circle2 = Object.create(circle);
circle2.radius = 10;
그게 다야. JavaScript에서 프로토 타입 상속을 수행했습니다. 그렇게 간단하지 않습니까? 객체를 가져 와서 복제하고, 필요한 것을 변경하고, 자신에게 새로운 객체를 얻었습니다.
이제 "이것은 얼마나 간단합니까? 새 원을 만들 때마다 복제 circle
하고 수동으로 반지름을 지정해야합니다"라고 물을 수 있습니다. 해결책은 무거운 물건을 들어 올리는 기능을 사용하는 것입니다.
function createCircle(radius) {
var newCircle = Object.create(circle);
newCircle.radius = radius;
return newCircle;
}
var circle2 = createCircle(10);
실제로 다음과 같이이 모든 것을 단일 객체 리터럴로 결합 할 수 있습니다.
var circle = {
radius: 5,
create: function (radius) {
var circle = Object.create(this);
circle.radius = radius;
return circle;
},
area: function () {
var radius = this.radius;
return Math.PI * radius * radius;
},
circumference: function () {
return 2 * Math.PI * this.radius;
}
};
var circle2 = circle.create(10);
위 프로그램 create
에서이 함수는의 복제본을 만들고 circle
새 복제본을 할당 한 radius
다음 반환합니다. 이것이 바로 JavaScript에서 생성자가하는 일입니다.
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의 생성자 패턴은 반전 된 프로토 타입 패턴입니다. 객체를 생성하는 대신 생성자를 생성합니다. new
키워드는 결합 this
의 복제에 생성자 내부 포인터를 prototype
생성자.
혼란스러워? JavaScript의 생성자 패턴이 불필요하게 복잡하기 때문입니다. 이것은 대부분의 프로그래머가 이해하기 어려운 것입니다.
그들은 다른 객체로부터 상속받은 객체를 생각하는 대신 다른 생성자로부터 상속받은 생성자를 생각하고 완전히 혼란스러워합니다.
JavaScript의 생성자 패턴을 피해야하는 다른 많은 이유가 있습니다. 내 블로그 게시물에서 그 내용을 읽을 수 있습니다 : 생성자 대 프로토 타입
그렇다면 고전 상속에 비해 프로토 타입 상속의 이점은 무엇입니까? 가장 일반적인 논증을 다시 살펴보고 왜 그런지 설명해 봅시다 .
CMS 는 그의 답변에서 다음과 같이 말합니다.
제 생각에, 프로토 타입 상속의 주요 이점은 단순성입니다.
우리가 방금 한 일을 생각해 봅시다. circle
반지름이 반지름 인 객체 를 만들었습니다 5
. 그런 다음 복제하여 반지름을으로 지정했습니다 10
.
따라서 우리는 프로토 타입 상속 작업을하기 위해 두 가지만 필요합니다 :
Object.create
.반대로 고전 상속은 훨씬 더 복잡합니다. 고전 상속에는 다음이 있습니다.
당신은 아이디어를 얻습니다. 요점은 프로토 타입 상속이 이해하기 쉽고 구현하기 쉽고 추론하기 쉽다는 것입니다.
Steve Yegge가 자신의 고전 블로그 게시물 " Portrait of a N00b "에 다음 과 같이 덧붙였습니다
메타 데이터는 다른 종류의 설명이나 모델입니다. 코드의 주석은 계산에 대한 자연어 설명입니다. 메타 데이터를 메타 데이터로 만드는 것은 반드시 필요하지 않다는 것입니다. 가계 서류가있는 개가 있는데 서류를 잃어버린 경우에도 여전히 완벽하게 유효한 개가 있습니다.
같은 의미에서 클래스는 단지 메타 데이터입니다. 상속에 클래스가 반드시 필요한 것은 아닙니다. 그러나 일부 사람들 (보통 n00bs)은 수업에 더 편한 수업을 찾습니다. 그것은 잘못된 보안 감각을 제공합니다.
정적 유형은 메타 데이터 일 뿐이라는 것도 알고 있습니다. 프로그래머와 컴파일러라는 두 종류의 독자를 대상으로하는 특수한 종류의 주석입니다. 정적 유형은 두 독자 그룹이 프로그램의 의도를 이해하도록 돕기 위해 계산에 대한 이야기를 들려줍니다. 그러나 정적 유형은 런타임에 폐기 될 수 있습니다. 결국에는 양식화 된 주석 일뿐입니다. 그들은 가계도 서류와 같습니다. 개에 대해 안전하지 않은 성격 유형을 더 행복하게 만들 수 있지만 개는 확실히 신경 쓰지 않습니다.
앞서 언급했듯이 수업은 사람들에게 잘못된 보안 감각을 부여합니다. 예를 들어 NullPointerException
코드를 완벽하게 읽을 수있는 경우에도 Java에서 너무 많은 값을 얻습니다 . 고전적인 상속은 일반적으로 프로그래밍 방식으로 이루어 지지만 Java 일뿐입니다. 파이썬에는 놀라운 고전 상속 시스템이 있습니다.
고전적 배경에서 온 대부분의 프로그래머는 고전적 상속이 프로토 타입 상속보다 강력하다고 주장합니다.
이 주장은 허위입니다. 우리는 이미 JavaScript가 클로저를 통해 개인 변수를 지원한다는 것을 알고 있지만 다중 상속은 어떻습니까? JavaScript의 객체에는 하나의 프로토 타입 만 있습니다.
진실은 프로토 타입 상속이 여러 프로토 타입에서 상속을 지원한다는 것입니다. 프로토 타입 상속은 단순히 하나의 객체가 다른 객체로부터 상속됨을 의미합니다. 실제로 프로토 타입 상속을 구현하는 두 가지 방법 이 있습니다 .
예 JavaScript는 객체가 다른 객체에만 위임 할 수 있도록합니다. 그러나 임의 수의 객체 속성을 복사 할 수 있습니다. 예를 _.extend
들어이 작업을 수행합니다.
물론 많은 프로그래머가이 사실 상속으로 간주하지 아니한다 때문에 instanceof
와는 isPrototypeOf
달리 말한다. 그러나 이는 연결을 통해 프로토 타입에서 상속되는 모든 객체에 프로토 타입 배열을 저장하여 쉽게 해결할 수 있습니다.
function copyOf(object, prototype) {
var prototypes = object.prototypes;
var prototypeOf = Object.isPrototypeOf;
return prototypes.indexOf(prototype) >= 0 ||
prototypes.some(prototypeOf, prototype);
}
따라서 프로토 타입 상속은 클래식 상속만큼 강력합니다. 실제로 프로토 타입 상속에서는 복사 할 속성과 다른 프로토 타입에서 생략 할 속성을 직접 선택할 수 있기 때문에 클래식 상속보다 훨씬 강력합니다.
고전적인 상속에서는 상속하려는 속성을 선택하는 것이 불가능합니다 (또는 적어도 매우 어렵습니다). 그들은 가상 문제 를 해결하기 위해 가상 기본 클래스와 인터페이스를 사용 합니다.
그러나 JavaScript에서는 상속하려는 속성과 프로토 타입을 정확하게 제어 할 수 있기 때문에 다이아몬드 문제에 대해 전혀 듣지 못할 것입니다.
고전적 상속이 반드시 더 많은 중복 코드로 이어지지는 않기 때문에이 점을 설명하기가 조금 더 어렵습니다. 사실 고전이든 프로토 타입이든 상속은 코드의 중복성을 줄이기 위해 사용됩니다.
하나의 주장은 고전 상속이있는 대부분의 프로그래밍 언어는 정적으로 유형이 지정되며 암시 적 정적 유형을 갖는 Haskell과 달리 사용자가 유형을 명시 적으로 선언해야한다는 것입니다. 따라서 더 자세한 코드가 생성됩니다.
Java는이 동작으로 유명합니다. 나는 Bob Nystrom이 Pratt Parsers 에 대한 그의 블로그 포스트에서 다음의 일화를 언급 한 것을 분명히 기억합니다 .
Java의 "4 배로 서명하십시오"수준의 관료주의를 좋아해야합니다.
다시 말하지만, Java가 너무 빨라서 그럴 것이라고 생각합니다.
하나의 유효한 주장은 고전적 상속을 가진 모든 언어가 다중 상속을 지원하지는 않는다는 것입니다. 다시 자바가 떠오른다. 예 Java에는 인터페이스가 있지만 충분하지 않습니다. 때로는 다중 상속이 필요합니다.
프로토 타입 상속은 다중 상속을 허용하므로 다중 상속이 필요한 코드는 고전 상속은 있지만 다중 상속은없는 언어가 아닌 프로토 타입 상속을 사용하여 작성하면 덜 중복됩니다.
프로토 타입 상속의 가장 중요한 장점 중 하나는 프로토 타입을 만든 후 프로토 타입에 새 속성을 추가 할 수 있다는 것입니다. 이를 통해 프로토 타입에 새로운 메소드를 추가 할 수 있으며,이 메소드는 해당 프로토 타입에 위임 된 모든 객체에 자동으로 제공됩니다.
클래스를 만든 후에는 런타임에 클래스를 수정할 수 없으므로 클래식 상속에서는 불가능합니다. 이것은 아마도 고전 상속에 비해 프로토 타입 상속의 가장 큰 장점 일 것입니다. 그러나 나는 결국 최선을 다하는 것을 좋아합니다.
프로토 타입 상속이 중요합니다. 프로토 타입 상속의 프로토 타입 패턴을 선호하여 프로토 타입 상속의 생성자 패턴을 포기해야하는 이유에 대해 JavaScript 프로그래머에게 교육하는 것이 중요합니다.
JavaScript를 올바르게 가르치기 시작하면 새로운 프로그래머에게 생성자 패턴 대신 프로토 타입 패턴을 사용하여 코드를 작성하는 방법을 보여줄 수 있습니다.
프로토 타입 패턴을 사용하여 프로토 타입 상속을 설명하는 것이 더 쉬울뿐만 아니라 더 나은 프로그래머가 될 것입니다.
이 답변이 마음에 들면 " Prototypal Inheritance Matters " 에 대한 내 블로그 게시물을 읽어보십시오 . 나를 믿어 라. 실망하지 않을 것이다.
Object.create
지정된 프로토 타입으로 새 오브젝트를 작성 중입니다. 선택한 단어는 프로토 타입이 복제되었다는 인상을줍니다.
실제로 인라인으로 질문에 대답하겠습니다.
프로토 타입 상속에는 다음과 같은 장점이 있습니다.
그러나 다음과 같은 단점이 있습니다.
위의 줄을 읽고 전통적인 클래스 / 객체 체계의 해당 장단점을 생각해 볼 수 있다고 생각합니다. 물론 각 영역에는 더 많은 것이 있으므로 나머지는 응답하는 사람들에게 맡기십시오.
프로토 타입 상속의 주요 이점 인 IMO는 단순성입니다.
언어의 원형은 고전적으로 훈련받은 사람들을 혼란스럽게 할 수 있지만 실제로 이것은 실제로 단순하고 강력한 개념, 차등 상속 이라는 것이 밝혀졌습니다 .
분류 할 필요가 없으며 코드가 작고 중복성이 적으며 객체는 다른 일반적인 객체에서 상속됩니다.
프로토 타입으로 생각 하면 곧 수업이 필요하지 않다는 것을 알게 될 것입니다 ...
ECMAScript 5th Edition 사양 은 프로토 타입 상속이 가까운 미래에 훨씬 더 인기가있을 것입니다 Object.create
.
var obj = Object.create(baseInstance);
이 새로운 버전의 표준은 모든 브라우저 공급 업체에서 구현하고 있으며 더 순수한 프로토 타입 상속을 보게 될 것입니다 ...
두 가지 방법 중에서 선택해야 할 것이 많지 않습니다. 파악해야 할 기본 아이디어는 JavaScript 엔진에 읽을 객체의 속성이 주어지면 먼저 인스턴스를 확인하고 해당 속성이없는 경우 프로토 타입 체인을 확인한다는 것입니다. 다음은 프로토 타입과 클래식의 차이점을 보여주는 예입니다.
프로토 타입
var single = { status: "Single" },
princeWilliam = Object.create(single),
cliffRichard = Object.create(single);
console.log(Object.keys(princeWilliam).length); // 0
console.log(Object.keys(cliffRichard).length); // 0
// Marriage event occurs
princeWilliam.status = "Married";
console.log(Object.keys(princeWilliam).length); // 1 (New instance property)
console.log(Object.keys(cliffRichard).length); // 0 (Still refers to prototype)
클래식 한 인스턴스 메소드 (각 인스턴스가 자체 속성을 저장하기 때문에 비효율적 임)
function Single() {
this.status = "Single";
}
var princeWilliam = new Single(),
cliffRichard = new Single();
console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 1
효율적인 클래식
function Single() {
}
Single.prototype.status = "Single";
var princeWilliam = new Single(),
cliffRichard = new Single();
princeWilliam.status = "Married";
console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 0
console.log(cliffRichard.status); // "Single"
보시다시피, 고전적인 스타일로 선언 된 "클래스"프로토 타입을 조작 할 수 있기 때문에 프로토 타입 상속을 사용하면 아무런 이점이 없습니다. 고전적인 방법의 일부입니다.
웹 개발 : 프로토 타입 상속과 클래식 상속
http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html
클래식 대 프로토 타입 상속