Lợi ích của thừa kế nguyên mẫu so với cổ điển?


271

Vì vậy, cuối cùng tôi đã ngừng kéo chân mình suốt những năm này và quyết định học JavaScript "đúng cách". Một trong những yếu tố gây khó chịu nhất trong thiết kế ngôn ngữ là việc thực hiện kế thừa. Có kinh nghiệm về Ruby, tôi thực sự rất vui khi thấy các kiểu đóng và gõ động; nhưng đối với cuộc sống của tôi, tôi không thể tìm ra những lợi ích nào có được từ các thể hiện đối tượng sử dụng các thể hiện khác để thừa kế.



Câu trả lời:


560

Tôi biết rằng câu trả lời này trễ 3 năm nhưng tôi thực sự nghĩ rằng các câu trả lời hiện tại không cung cấp đủ thông tin về cách thừa kế nguyên mẫu tốt hơn so với thừa kế cổ điển .

Trước tiên, hãy xem các đối số phổ biến nhất mà các lập trình viên JavaScript nêu để bảo vệ tính kế thừa nguyên mẫu (Tôi đang lấy các đối số này từ nhóm câu trả lời hiện tại):

  1. Thật đơn giản.
  2. Nó mạnh mẽ.
  3. Nó dẫn đến mã nhỏ hơn, ít dự phòng hơn.
  4. Nó năng động và do đó tốt hơn cho các ngôn ngữ động.

Bây giờ tất cả các đối số đều hợp lệ, nhưng không ai bận tâm giải thích lý do tại sao. Nó giống như nói với một đứa trẻ rằng học toán là quan trọng. Chắc chắn là có, nhưng đứa trẻ chắc chắn không quan tâm; và bạn không thể tạo ra một đứa trẻ như Toán bằng cách nói rằng nó quan trọng.

Tôi nghĩ vấn đề với sự kế thừa nguyên mẫu là nó được giải thích từ quan điểm của JavaScript. Tôi yêu JavaScript, nhưng kế thừa nguyên mẫu trong JavaScript là sai. Không giống như thừa kế cổ điển, có hai kiểu thừa kế nguyên mẫu:

  1. Các mẫu nguyên mẫu của thừa kế nguyên mẫu.
  2. Các mẫu xây dựng của thừa kế nguyên mẫu.

Thật không may, JavaScript sử dụng mô hình hàm tạo của kế thừa nguyên mẫu. Điều này là do khi JavaScript được tạo, Brendan Eich (người tạo ra JS) muốn nó trông giống như Java (có sự kế thừa cổ điển):

Và chúng tôi đã đẩy nó như một đứa em trai với Java, vì một ngôn ngữ bổ sung như Visual Basic là C ++ trong các gia đình ngôn ngữ của Microsoft vào thời điểm đó.

Điều này là xấu bởi vì khi mọi người sử dụng các hàm tạo trong JavaScript, họ nghĩ rằng các hàm tạo kế thừa từ các hàm tạo khác. Cái này sai. Trong các đối tượng thừa kế nguyên mẫu kế thừa từ các đối tượng khác. Các nhà xây dựng không bao giờ đi vào hình ảnh. Đây là điều khiến hầu hết mọi người bối rối.

Những người từ các ngôn ngữ như Java, vốn có tính kế thừa cổ điển, thậm chí còn bối rối hơn bởi vì mặc dù các nhà xây dựng trông giống như các lớp mà họ không hành xử giống như các lớp. Như Douglas Crockford đã nêu:

Sự thiếu quyết đoán này nhằm mục đích làm cho ngôn ngữ dường như quen thuộc hơn với các lập trình viên được đào tạo bài bản, nhưng không thực hiện được, như chúng ta có thể thấy từ các lập trình viên Java có ý kiến ​​rất thấp về JavaScript. Mẫu constructor của JavaScript không thu hút đám đông cổ điển. Nó cũng che khuất bản chất nguyên mẫu thực sự của JavaScript. Kết quả là, có rất ít lập trình viên biết sử dụng ngôn ngữ một cách hiệu quả.

Có bạn có nó. Thẳng từ miệng ngựa.

Kế thừa nguyên mẫu thực sự

Kế thừa nguyên mẫu là tất cả về các đối tượng. Các đối tượng kế thừa các thuộc tính từ các đối tượng khác. Thats tất cả để có nó. Có hai cách tạo đối tượng bằng cách sử dụng kế thừa nguyên mẫu:

  1. Tạo một đối tượng hoàn toàn mới.
  2. Nhân bản một đối tượng hiện có và mở rộng nó.

Lưu ý: JavaScript cung cấp hai cách để sao chép một đối tượng - phân quyềnghép nối . Do đó, tôi sẽ sử dụng từ "bản sao" để chỉ riêng việc thừa kế thông qua ủy quyền và từ "bản sao" để chỉ dành riêng cho thừa kế thông qua ghép nối.

Nói đủ. Hãy xem một số ví dụ. Nói rằng tôi có một vòng tròn bán kính 5:

var circle = {
    radius: 5
};

Chúng ta có thể tính diện tích và chu vi của vòng tròn từ bán kính của nó:

circle.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};

circle.circumference = function () {
    return 2 * Math.PI * this.radius;
};

Bây giờ tôi muốn tạo một vòng tròn bán kính khác 10. Một cách để làm điều này sẽ là:

var circle2 = {
    radius: 10,
    area: circle.area,
    circumference: circle.circumference
};

Tuy nhiên, JavaScript cung cấp một cách tốt hơn - ủy quyền . Các Object.createchức năng được sử dụng để làm điều này:

var circle2 = Object.create(circle);
circle2.radius = 10;

Đó là tất cả. Bạn vừa thực hiện kế thừa nguyên mẫu trong JavaScript. Điều đó không đơn giản sao? Bạn lấy một đối tượng, nhân bản nó, thay đổi bất cứ điều gì bạn cần và xin chào - bạn đã có cho mình một đối tượng hoàn toàn mới.

Bây giờ bạn có thể hỏi, "Làm thế nào đơn giản? Mỗi lần tôi muốn tạo một vòng tròn mới, tôi cần sao chép circlevà gán thủ công cho nó một bán kính". Giải pháp là sử dụng một chức năng để thực hiện việc nâng vật nặng cho bạn:

function createCircle(radius) {
    var newCircle = Object.create(circle);
    newCircle.radius = radius;
    return newCircle;
}

var circle2 = createCircle(10);

Trong thực tế, bạn có thể kết hợp tất cả những điều này thành một đối tượng theo nghĩa đen như sau:

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

Kế thừa nguyên mẫu trong JavaScript

Nếu bạn nhận thấy trong chương trình trên, createhàm sẽ tạo một bản sao circle, gán một cái mới radiuscho nó và sau đó trả về nó. Đây chính xác là những gì một nhà xây dựng làm trong 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);

Mẫu constructor trong JavaScript là mẫu nguyên mẫu được đảo ngược. Thay vì tạo một đối tượng, bạn tạo một hàm tạo. Các newtừ khóa liên kết với thiscon trỏ bên trong constructor để một bản sao của prototypecác nhà xây dựng.

Nghe có vẻ khó hiểu? Đó là vì mẫu constructor trong JavaScript làm phức tạp mọi thứ một cách không cần thiết. Đây là điều mà hầu hết các lập trình viên cảm thấy khó hiểu.

Thay vì nghĩ về các đối tượng kế thừa từ các đối tượng khác, họ nghĩ về các nhà xây dựng kế thừa từ các nhà xây dựng khác và sau đó trở nên hoàn toàn bối rối.

Có một loạt các lý do khác tại sao nên tránh mô hình hàm tạo trong JavaScript. Bạn có thể đọc về chúng trong bài đăng trên blog của tôi ở đây: Con constructor vs Prototypes


Vì vậy, những lợi ích của thừa kế nguyên mẫu so với thừa kế cổ điển là gì? Chúng ta hãy đi qua các đối số phổ biến nhất một lần nữa, và giải thích tại sao .

1. Kế thừa nguyên mẫu là đơn giản

CMS nêu trong câu trả lời của mình:

Theo tôi lợi ích chính của thừa kế nguyên mẫu là sự đơn giản của nó.

Hãy xem xét những gì chúng ta vừa làm. Chúng tôi tạo ra một đối tượng circlecó bán kính là 5. Sau đó, chúng tôi nhân bản nó và cho bản sao bán kính 10.

Do đó, chúng ta chỉ cần hai điều để làm cho kế thừa nguyên mẫu hoạt động:

  1. Một cách để tạo một đối tượng mới (ví dụ: đối tượng bằng chữ).
  2. Một cách để mở rộng một đối tượng hiện có (ví dụ Object.create).

Ngược lại thừa kế cổ điển phức tạp hơn nhiều. Trong thừa kế cổ điển, bạn có:

  1. Các lớp học.
  2. Vật.
  3. Giao diện.
  4. Các lớp học trừu tượng.
  5. Lớp cuối cùng.
  6. Các lớp cơ sở ảo.
  7. Nhà xây dựng.
  8. Phá hủy.

Bạn có được ý tưởng. Vấn đề là sự kế thừa nguyên mẫu dễ hiểu hơn, dễ thực hiện hơn và dễ lý luận hơn.

Như Steve Yegge đã đặt nó trong bài viết trên blog cổ điển của mình " Portrait of a N00b ":

Siêu dữ liệu là bất kỳ loại mô tả hoặc mô hình của một cái gì đó khác. Các ý kiến ​​trong mã của bạn chỉ là một mô tả ngôn ngữ tự nhiên của tính toán. Điều làm cho siêu dữ liệu siêu dữ liệu là nó không thực sự cần thiết. Nếu tôi có một con chó với một số giấy tờ phả hệ, và tôi mất giấy tờ, tôi vẫn có một con chó hoàn toàn hợp lệ.

Trong cùng một nghĩa các lớp chỉ là siêu dữ liệu. Các lớp học không bắt buộc phải thừa kế. Tuy nhiên, một số người (thường là n00bs) thấy các lớp thoải mái hơn khi làm việc. Nó mang lại cho họ một cảm giác an toàn sai lầm.

Vâng, chúng tôi cũng biết rằng các loại tĩnh chỉ là siêu dữ liệu. Chúng là một loại bình luận chuyên biệt nhắm vào hai loại độc giả: lập trình viên và người biên dịch. Các loại tĩnh kể một câu chuyện về tính toán, có lẽ là để giúp cả hai nhóm độc giả hiểu được ý định của chương trình. Nhưng các kiểu tĩnh có thể bị loại bỏ trong thời gian chạy, vì cuối cùng chúng chỉ là các bình luận cách điệu. Chúng giống như giấy tờ phả hệ: nó có thể làm cho một loại tính cách không an toàn nào đó vui hơn về con chó của họ, nhưng con chó chắc chắn không quan tâm.

Như tôi đã nói trước đó, các lớp học mang lại cho mọi người cảm giác an toàn sai lầm. Ví dụ, bạn nhận được quá nhiều NullPointerExceptions trong Java ngay cả khi mã của bạn hoàn toàn dễ đọc. Tôi thấy sự kế thừa cổ điển thường cản trở việc lập trình, nhưng có lẽ đó chỉ là Java. Python có một hệ thống kế thừa cổ điển tuyệt vời.

2. Kế thừa nguyên mẫu là mạnh mẽ

Hầu hết các lập trình viên đến từ một nền tảng cổ điển cho rằng kế thừa cổ điển mạnh hơn kế thừa nguyên mẫu bởi vì nó có:

  1. Biến riêng.
  2. Đa thừa kế.

Yêu cầu này là sai. Chúng ta đã biết rằng JavaScript hỗ trợ các biến riêng tư thông qua các bao đóng , nhưng còn nhiều kế thừa thì sao? Các đối tượng trong JavaScript chỉ có một nguyên mẫu.

Sự thật là sự kế thừa nguyên mẫu hỗ trợ kế thừa từ nhiều nguyên mẫu. Kế thừa nguyên mẫu đơn giản có nghĩa là một đối tượng kế thừa từ một đối tượng khác. Thực tế có hai cách để thực hiện kế thừa nguyên mẫu :

  1. Đoàn hoặc thừa kế khác biệt
  2. Nhân bản vô tính hoặc kế thừa

Có JavaScript chỉ cho phép các đối tượng ủy quyền cho một đối tượng khác. Tuy nhiên, nó cho phép bạn sao chép các thuộc tính của một số lượng đối tượng tùy ý. Ví dụ _.extend, chỉ làm điều này.

Tất nhiên nhiều lập trình viên không coi đây là sự kế thừa thực sự bởi vì instanceofisPrototypeOfnói khác đi. Tuy nhiên, điều này có thể được khắc phục dễ dàng bằng cách lưu trữ một loạt các nguyên mẫu trên mọi đối tượng kế thừa từ một nguyên mẫu thông qua ghép nối:

function copyOf(object, prototype) {
    var prototypes = object.prototypes;
    var prototypeOf = Object.isPrototypeOf;
    return prototypes.indexOf(prototype) >= 0 ||
        prototypes.some(prototypeOf, prototype);
}

Do đó, thừa kế nguyên mẫu cũng mạnh mẽ như thừa kế cổ điển. Trong thực tế, nó mạnh hơn nhiều so với thừa kế cổ điển bởi vì trong thừa kế nguyên mẫu, bạn có thể tự tay chọn các thuộc tính để sao chép và các thuộc tính nào được bỏ qua từ các nguyên mẫu khác nhau.

Trong thừa kế cổ điển, không thể (hoặc ít nhất là rất khó) để chọn thuộc tính nào bạn muốn thừa kế. Họ sử dụng các lớp cơ sở ảo và giao diện để giải quyết vấn đề kim cương .

Tuy nhiên, trong JavaScript rất có thể bạn sẽ không bao giờ nghe về vấn đề kim cương vì bạn có thể kiểm soát chính xác các thuộc tính bạn muốn thừa kế và từ nguyên mẫu nào.

3. Kế thừa nguyên mẫu ít dư thừa

Điểm này khó giải thích hơn một chút vì kế thừa cổ điển không nhất thiết dẫn đến mã thừa. Trong thực tế thừa kế, cho dù cổ điển hay nguyên mẫu, được sử dụng để giảm sự dư thừa trong mã.

Một lập luận có thể là hầu hết các ngôn ngữ lập trình có tính kế thừa cổ điển được gõ tĩnh và yêu cầu người dùng khai báo rõ ràng các loại (không giống như Haskell có kiểu gõ tĩnh). Do đó điều này dẫn đến mã dài hơn.

Java nổi tiếng với hành vi này. Tôi nhớ rất rõ Bob Nystrom đã đề cập đến giai thoại sau đây trong bài đăng trên blog của mình về Pratt Parsers :

Bạn phải yêu thích mức độ quan liêu "tăng gấp bốn lần" của Java ở đây.

Một lần nữa, tôi nghĩ đó chỉ là do Java hút rất nhiều.

Một đối số hợp lệ là không phải tất cả các ngôn ngữ có thừa kế cổ điển đều hỗ trợ nhiều kế thừa. Một lần nữa Java xuất hiện trong tâm trí. Có Java có giao diện, nhưng điều đó là không đủ. Đôi khi bạn thực sự cần nhiều sự kế thừa.

Vì thừa kế nguyên mẫu cho phép nhiều kế thừa, mã yêu cầu nhiều kế thừa sẽ ít dư thừa hơn nếu được viết bằng cách sử dụng thừa kế nguyên mẫu thay vì trong một ngôn ngữ có thừa kế cổ điển nhưng không có nhiều kế thừa.

4. Kế thừa nguyên mẫu là năng động

Một trong những lợi thế quan trọng nhất của thừa kế nguyên mẫu là bạn có thể thêm các thuộc tính mới vào các nguyên mẫu sau khi chúng được tạo. Điều này cho phép bạn thêm các phương thức mới vào một nguyên mẫu sẽ tự động được cung cấp cho tất cả các đối tượng ủy quyền cho nguyên mẫu đó.

Điều này là không thể trong kế thừa cổ điển bởi vì một khi một lớp được tạo, bạn không thể sửa đổi nó trong thời gian chạy. Đây có lẽ là lợi thế lớn nhất của kế thừa nguyên mẫu so với thừa kế cổ điển, và nó nên được đặt lên hàng đầu. Tuy nhiên tôi thích tiết kiệm tốt nhất cho đến cuối cùng.

Phần kết luận

Vấn đề thừa kế nguyên mẫu. Điều quan trọng là giáo dục các lập trình viên JavaScript về lý do tại sao từ bỏ mô hình xây dựng của kế thừa nguyên mẫu để ủng hộ mô hình nguyên mẫu của kế thừa nguyên mẫu.

Chúng ta cần bắt đầu dạy JavaScript một cách chính xác và điều đó có nghĩa là chỉ cho các lập trình viên mới cách viết mã bằng cách sử dụng mẫu nguyên mẫu thay vì mẫu xây dựng.

Không chỉ dễ dàng hơn để giải thích sự kế thừa nguyên mẫu bằng cách sử dụng mẫu nguyên mẫu, mà còn giúp các lập trình viên tốt hơn.

Nếu bạn thích câu trả lời này thì bạn cũng nên đọc bài đăng trên blog của tôi về " Tại sao các vấn đề kế thừa nguyên mẫu ". Tin tôi đi, bạn sẽ không phải thất vọng.


33
Mặc dù tôi hiểu bạn đến từ đâu và tôi đồng ý rằng thừa kế nguyên mẫu rất hữu ích, tôi nghĩ bằng cách đưa ra giả định "thừa kế nguyên mẫu tốt hơn thừa kế cổ điển", bạn đã bị thất bại. Để cung cấp cho bạn một số quan điểm, thư viện jTypes của tôi là một thư viện kế thừa cổ điển cho JavaScript. Vì vậy, là một chàng trai đã dành thời gian để làm điều đó, tôi vẫn sẽ ngồi đây và nói rằng thừa kế nguyên mẫu là tuyệt vời và rất hữu ích. Nhưng nó chỉ đơn giản là một công cụ trong số nhiều công cụ mà một lập trình viên có. Vẫn còn nhiều bất lợi cho việc thừa kế nguyên mẫu.
đường dây đỏ

7
Tôi hoàn toàn đồng ý với những gì bạn nói. Tôi cảm thấy rằng quá nhiều lập trình viên từ chối JavaScript vì thiếu tính kế thừa cổ điển hoặc tố cáo nó là một ngôn ngữ đơn giản và ngu ngốc. Nó thực sự là một khái niệm cực kỳ mạnh mẽ, tôi đồng ý và nhiều lập trình viên nên chấp nhận nó và học nó. Như đã nói, tôi cũng cảm thấy có một số lượng lớn các nhà phát triển trong JavaScript phản đối bất kỳ hình thức thừa kế cổ điển nào cùng với JavaScript, khi họ thực sự không có cơ sở cho các lập luận của họ. Cả hai đều mạnh mẽ như nhau theo cách riêng của họ, và cũng hữu ích như nhau.
đường dây đỏ

9
Đó là ý kiến ​​của bạn, nhưng tôi sẽ tiếp tục không đồng ý và tôi nghĩ rằng sự phổ biến ngày càng tăng của những thứ như CoffeeScript và TypeScript cho thấy có một cộng đồng lớn các nhà phát triển muốn sử dụng chức năng này khi thích hợp. Như bạn đã nói, ES6 thêm đường cú pháp, nhưng vẫn không cung cấp tính mở rộng của jTypes. Bên cạnh đó, tôi không phải là người chịu trách nhiệm cho downvote của bạn. Trong khi tôi không đồng ý với bạn, tôi không cảm thấy điều đó cấu thành bạn có một câu trả lời tồi. Bạn đã rất kỹ lưỡng.
đường dây đỏ

25
Bạn đang sử dụng từ clone rất nhiều, điều này đơn giản là sai. Object.createđang tạo đối tượng mới với nguyên mẫu được chỉ định. Việc bạn chọn từ cho ấn tượng rằng nguyên mẫu được nhân bản.
Pavel Horal

7
@Aadit: thực sự không cần phải phòng thủ như vậy. Câu trả lời của bạn rất chi tiết và xứng đáng với số phiếu. Tôi không gợi ý rằng "liên kết" nên là sự thay thế thả xuống cho "bản sao", nhưng nó mô tả chính xác hơn mối liên hệ giữa một đối tượng và nguyên mẫu mà nó thừa hưởng, cho dù bạn có khẳng định định nghĩa riêng của mình về thuật ngữ "nhân bản" " hay không. Thay đổi nó hoặc không thay đổi nó, đó hoàn toàn là sự lựa chọn của bạn.
Andy E

42

Cho phép tôi thực sự trả lời câu hỏi nội tuyến.

Kế thừa nguyên mẫu có những đức tính sau:

  1. Nó phù hợp hơn với các ngôn ngữ động vì tính kế thừa cũng năng động như môi trường. (Khả năng áp dụng JavaScript phải rõ ràng ở đây.) Điều này cho phép bạn thực hiện mọi việc nhanh chóng như tùy chỉnh các lớp mà không cần số lượng lớn cơ sở hạ tầng .
  2. Việc thực hiện một sơ đồ đối tượng tạo mẫu dễ dàng hơn so với các sơ đồ phân đôi đối tượng lớp / đối tượng cổ điển.
  3. Nó loại bỏ sự cần thiết cho các cạnh sắc nét phức tạp xung quanh mô hình đối tượng như "siêu dữ liệu" (tôi chưa bao giờ siêu dữ liệu tôi thích ... xin lỗi!) Hoặc "giá trị riêng" hoặc tương tự.

Tuy nhiên, nó có những nhược điểm sau:

  1. Kiểu kiểm tra một ngôn ngữ nguyên mẫu không phải là không thể, nhưng nó rất, rất khó. Hầu hết "kiểm tra kiểu" của các ngôn ngữ nguyên mẫu là kiểm tra kiểu "gõ vịt" trong thời gian chạy thuần túy. Điều này không phù hợp với mọi môi trường.
  2. Thật khó khăn để làm những việc như tối ưu hóa việc gửi phương thức bằng cách phân tích tĩnh (hoặc, thường xuyên, thậm chí là động!). Nó có thể (tôi nhấn mạnh: có thể ) rất kém hiệu quả rất dễ dàng.
  3. Tương tự việc tạo đối tượng có thể (và thường là) chậm hơn nhiều trong ngôn ngữ tạo mẫu so với ngôn ngữ phân đôi đối tượng lớp / đối tượng thông thường hơn.

Tôi nghĩ rằng bạn có thể đọc giữa các dòng trên và đưa ra những ưu điểm và nhược điểm tương ứng của các sơ đồ lớp / đối tượng truyền thống. Tất nhiên, có nhiều hơn ở mỗi khu vực vì vậy tôi sẽ để phần còn lại cho người khác trả lời.


1
Hãy nhìn xem, một câu trả lời súc tích mà không phải fanboy. Thực sự muốn đây là câu trả lời hàng đầu cho câu hỏi.
siliconrockstar

Hôm nay chúng ta có các trình biên dịch Chỉ thời gian động có thể biên dịch mã trong khi mã đang chạy tạo các đoạn mã khác nhau cho mỗi phần. JavaScript thực sự nhanh hơn Ruby hoặc Python sử dụng các lớp cổ điển vì điều này ngay cả khi bạn sử dụng các nguyên mẫu vì nhiều công việc đã được thực hiện để tối ưu hóa nó.
aoeu256

28

IMO lợi ích chính của thừa kế nguyên mẫu là sự đơn giản của nó.

Bản chất nguyên chủng của ngôn ngữ có thể nhầm lẫn giữa những người đang cổ điển được đào tạo, nhưng nó chỉ ra rằng trên thực tế đây là một thực sự khái niệm đơn giản và mạnh mẽ, thừa kế khác biệt .

Bạn không cần phải phân loại , mã của bạn nhỏ hơn, ít dự phòng hơn, các đối tượng kế thừa từ các đối tượng khác, tổng quát hơn.

Nếu bạn nghĩ nguyên mẫu, bạn sẽ sớm nhận thấy rằng bạn không cần các lớp ...

Kế thừa nguyên mẫu sẽ phổ biến hơn nhiều trong tương lai gần, đặc tả ECMAScript 5 Edition đã giới thiệu Object.createphương thức này, cho phép bạn tạo một thể hiện đối tượng mới kế thừa từ một đối tượng khác theo cách thực sự đơn giản:

var obj = Object.create(baseInstance);

Phiên bản mới này của tiêu chuẩn đang được thực hiện bởi tất cả các nhà cung cấp trình duyệt và tôi nghĩ rằng chúng ta sẽ bắt đầu thấy sự kế thừa nguyên mẫu thuần túy hơn ...


11
"Mã của bạn nhỏ hơn, ít dư thừa hơn ...", tại sao? Tôi đã xem qua liên kết wikipedia về "thừa kế khác biệt" và không có gì hỗ trợ cho các xác nhận này. Tại sao kế thừa cổ điển sẽ dẫn đến mã lớn hơn, dư thừa hơn?
Noel Abrahams

4
Chính xác, tôi đồng ý với Noel. Kế thừa nguyên mẫu chỉ đơn giản là một cách để hoàn thành công việc, nhưng điều đó không làm cho nó đúng cách. Các công cụ khác nhau sẽ thực hiện theo những cách khác nhau cho các công việc khác nhau. Kế thừa nguyên mẫu có vị trí của nó. Đó là một khái niệm cực kỳ mạnh mẽ và đơn giản. Với điều đó đã được nói, việc thiếu hỗ trợ cho đóng gói thực sự và đa hình thực sự đặt JavaScript vào một bất lợi đáng kể. Các phương pháp này đã tồn tại lâu hơn nhiều so với JavaScript và chúng là âm thanh trong hiệu trưởng của chúng. Vì vậy, suy nghĩ nguyên mẫu là "tốt hơn" hoàn toàn là tâm lý sai lầm.
đường dây đỏ

1
Bạn có thể mô phỏng kế thừa dựa trên lớp bằng cách sử dụng kế thừa dựa trên nguyên mẫu, nhưng không phải ngược lại. Đó có thể là một cuộc tranh luận tốt. Ngoài ra tôi thấy đóng gói nhiều hơn như một quy ước hơn là tính năng ngôn ngữ (thông thường bạn có thể phá vỡ đóng gói thông qua sự phản chiếu). Về đa hình - tất cả những gì bạn đạt được là không phải viết một số điều kiện "nếu" đơn giản khi kiểm tra các đối số phương thức (và một chút tốc độ nếu phương thức đích được giải quyết trong quá trình biên dịch). Không có nhược điểm JavaScript thực sự ở đây.
Pavel Horal

Nguyên mẫu là tuyệt vời, IMO. Tôi đang nghĩ đến việc tạo ra một ngôn ngữ chức năng như Haskell ... nhưng thay vì tạo ra sự trừu tượng, tôi sẽ dựa trên mọi nguyên mẫu. Thay vì khái quát hóa tổng và giai thừa để "gấp", bạn nên tạo nguyên mẫu của hàm tổng và thay thế + bằng * và 0 bằng 1 để tạo ra sản phẩm. Tôi sẽ giải thích "Monads" là các nguyên mẫu thay thế "sau đó" từ các lời hứa / cuộc gọi lại với FlatMap là từ đồng nghĩa của "sau đó". Tôi nghĩ rằng các nguyên mẫu có thể giúp đưa lập trình chức năng đến với công chúng.
aoeu256

11

Thực sự không có nhiều lựa chọn giữa hai phương pháp. Ý tưởng cơ bản cần nắm bắt là khi công cụ JavaScript được cung cấp một thuộc tính của một đối tượng để đọc, đầu tiên nó sẽ kiểm tra thể hiện và nếu thuộc tính đó bị thiếu, nó sẽ kiểm tra chuỗi nguyên mẫu. Dưới đây là một ví dụ cho thấy sự khác biệt giữa nguyên mẫu và cổ điển:

Nguyên mẫu

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)

Cổ điển với các phương thức cá thể (Không hiệu quả vì mỗi phiên bản lưu trữ thuộc tính riêng của nó)

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

Hiệu quả cổ điển

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"

Như bạn có thể thấy, vì có thể thao tác nguyên mẫu của "các lớp" được khai báo theo kiểu cổ điển, thực sự không có lợi ích gì khi sử dụng thừa kế nguyên mẫu. Nó là một tập hợp con của phương pháp cổ điển.


2
Nhìn vào các câu trả lời và tài nguyên khác về chủ đề này, câu trả lời của bạn sẽ xuất hiện: "Kế thừa nguyên mẫu là một tập hợp con đường cú pháp được thêm vào JavaScript để cho phép xuất hiện kế thừa cổ điển". OP dường như đang hỏi về lợi ích của kế thừa nguyên mẫu trong JS so với kế thừa cổ điển trong các ngôn ngữ khác thay vì so sánh các kỹ thuật khởi tạo trong JavaScript.
Một Fader tối

2

Phát triển web: Kế thừa nguyên mẫu so với kế thừa cổ điển

http://chamnapchhorn.blogspot.com/2009/05/prototypal-inherribution-vs- classic.html

Kế thừa nguyên mẫu Vs cổ điển - Stack Overflow

Kế thừa nguyên mẫu Vs cổ điển


20
Tôi nghĩ tốt hơn là tóm tắt nội dung của các liên kết thay vì dán liên kết (điều mà bản thân tôi đã từng làm), trừ khi liên kết SO khác của nó. Điều này là do các liên kết / trang web không hoạt động và bạn mất phản hồi cho câu hỏi và nó có khả năng ảnh hưởng đến kết quả tìm kiếm.
James Westgate

Liên kết 1 không trả lời câu hỏi tại sao thừa kế nguyên mẫu? Nó chỉ đơn giản là mô tả nó.
viebel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.