nguyên mẫu dựa trên kế thừa lớp


208

Trong JavaScript, mọi đối tượng đồng thời là một thể hiện và một lớp. Để thực hiện kế thừa, bạn có thể sử dụng bất kỳ đối tượng nào làm nguyên mẫu.

Trong Python, C ++, v.v., có các lớp và các thể hiện, như các khái niệm riêng biệt. Để thực hiện kế thừa, bạn phải sử dụng lớp cơ sở để tạo một lớp mới, sau đó có thể được sử dụng để tạo các thể hiện dẫn xuất.

Tại sao JavaScript đi theo hướng này (hướng đối tượng dựa trên nguyên mẫu)? những ưu điểm (và nhược điểm) của OO dựa trên nguyên mẫu đối với OO truyền thống, dựa trên lớp là gì?


10
JavaScript bị ảnh hưởng bởi Self, ngôn ngữ đầu tiên có sự kế thừa nguyên mẫu. Vào thời điểm đó, sự kế thừa cổ điển là tất cả những cơn thịnh nộ, lần đầu tiên được giới thiệu trong Simula. Tuy nhiên, thừa kế cổ điển là quá phức tạp. Sau đó David Ungar và Randall Smith đã có một bản hùng ca sau khi đọc GEB - "Sự kiện cụ thể nhất có thể đóng vai trò là một ví dụ chung về một lớp các sự kiện." Họ nhận ra rằng các lớp không cần thiết cho lập trình hướng đối tượng. Do đó Tự sinh ra. Để biết cách kế thừa nguyên mẫu tốt hơn so với thừa kế cổ điển, hãy đọc bài này: stackoverflow.com/a/16872315/783743 =)
Aadit M Shah

@AaditMShah Cái gì / là GEBai?
Alex

3
@Alex GEB là một cuốn sách được viết bởi Douglas Hofstadter. Đó là tên viết tắt của Gödel Escher Bach. Kurt Gödel là một nhà toán học. Escher là một nghệ sĩ. Bach là một nghệ sĩ piano.
Aadit M Shah

Câu trả lời:


201

Có khoảng một trăm vấn đề thuật ngữ ở đây, chủ yếu được xây dựng xung quanh ai đó (không phải bạn) đang cố gắng làm cho ý tưởng của họ nghe giống như The Best.

Tất cả các ngôn ngữ hướng đối tượng cần có khả năng xử lý một số khái niệm:

  1. đóng gói dữ liệu cùng với các hoạt động liên quan đến dữ liệu, được gọi là thành viên dữ liệu và chức năng thành viên, hoặc là dữ liệu và phương thức, trong số những thứ khác.
  2. kế thừa, khả năng nói rằng các đối tượng này giống như các đối tượng khác NGOẠI TRỪ cho những thay đổi này
  3. Tính đa hình ("nhiều hình dạng") trong đó một đối tượng tự quyết định phương thức nào sẽ được chạy, do đó bạn có thể phụ thuộc vào ngôn ngữ để định tuyến chính xác các yêu cầu của mình.

Bây giờ, khi so sánh:

Điều đầu tiên là toàn bộ câu hỏi "lớp" và "nguyên mẫu". Ý tưởng ban đầu bắt đầu ở Simula, trong đó với phương thức dựa trên lớp, mỗi lớp đại diện cho một tập hợp các đối tượng có chung không gian trạng thái (đọc "các giá trị có thể") và các hoạt động tương tự, từ đó tạo thành một lớp tương đương. Nếu bạn nhìn lại Smalltalk, vì bạn có thể mở một lớp và thêm các phương thức, điều này thực sự giống như những gì bạn có thể làm trong Javascript.

Các ngôn ngữ OO sau này muốn có thể sử dụng kiểm tra kiểu tĩnh, vì vậy chúng tôi có khái niệm về một lớp cố định được thiết lập tại thời gian biên dịch. Trong phiên bản lớp mở, bạn đã linh hoạt hơn; trong phiên bản mới hơn, bạn có khả năng kiểm tra một số loại chính xác tại trình biên dịch mà nếu không sẽ phải kiểm tra.

Trong ngôn ngữ "dựa trên lớp", việc sao chép đó xảy ra vào thời gian biên dịch. Trong ngôn ngữ nguyên mẫu, các hoạt động được lưu trữ trong cấu trúc dữ liệu nguyên mẫu, được sao chép và sửa đổi trong thời gian chạy. Mặc dù vậy, về mặt trừu tượng, một lớp vẫn là lớp tương đương của tất cả các đối tượng có chung không gian và phương thức trạng thái. Khi bạn thêm một phương thức vào nguyên mẫu, bạn thực sự tạo thành phần tử của lớp tương đương mới.

Bây giờ, tại sao làm điều đó? chủ yếu bởi vì nó làm cho một cơ chế đơn giản, hợp lý, thanh lịch trong thời gian chạy. Bây giờ, để tạo một đối tượng mới hoặc để tạo một lớp mới, bạn chỉ cần thực hiện một bản sao sâu, sao chép tất cả dữ liệu và cấu trúc dữ liệu nguyên mẫu. Bạn nhận được sự kế thừa và đa hình ít nhiều miễn phí sau đó: tra cứu phương thức luôn bao gồm yêu cầu một từ điển để thực hiện phương thức theo tên.

Lý do kết thúc trong tập lệnh Javascript / ECMA về cơ bản là khi chúng tôi bắt đầu với điều này 10 năm trước, chúng tôi đã làm việc với các máy tính ít mạnh hơn và các trình duyệt kém tinh vi hơn nhiều. Chọn phương thức dựa trên nguyên mẫu có nghĩa là trình thông dịch có thể rất đơn giản trong khi vẫn giữ được các thuộc tính mong muốn của hướng đối tượng.


1
Phải, cái paragaph đó có đọc như thể tôi có ý gì khác không? Dahl và Nyqvist đã đưa ra "lớp" là tập hợp những thứ có cùng chữ ký phương thức.
Charlie Martin

1
Liệu sự thay đổi đó nói lên điều đó tốt hơn?
Charlie Martin

2
Không, xin lỗi, Clos là từ cuối 80 của dreamsongs.com/CLOS.html Smalltalk từ năm 1980 en.wikipedia.org/wiki/Smalltalk và Simula với định hướng đối tượng đầy đủ 1967-68 en.wikipedia.org/wiki/Simula
Charlie Martin

3
@Stephano, Chúng không quá khác biệt như tất cả: Python, Ruby, Smalltalk sử dụng từ điển để tra cứu phương thức và javascript và Self có các lớp. Ở một mức độ nào đó, bạn có thể lập luận rằng sự khác biệt chỉ là các ngôn ngữ hướng nguyên mẫu đang phơi bày việc triển khai của chúng. Vì vậy, có lẽ không nên biến nó thành một Thỏa thuận lớn: có lẽ giống như cuộc tranh luận giữa EMACS và vi.
Charlie Martin

21
Câu trả lời hữu ích . +1 Ít hữu ích rác trong các ý kiến. Ý tôi là nó có làm nên sự khác biệt dù là CLOS hay Smalltalk trước không? Hầu hết mọi người ở đây không phải là nhà sử học.
Adam Arold

40

Một so sánh, hơi thiên về cách tiếp cận dựa trên nguyên mẫu, có thể được tìm thấy trong bài báo Self: Sức mạnh của sự đơn giản . Bài viết đưa ra các lập luận sau đây có lợi cho các nguyên mẫu:

Sáng tạo bằng cách sao chép . Tạo các đối tượng mới từ các nguyên mẫu được thực hiện bằng một thao tác đơn giản, sao chép, với một phép ẩn dụ sinh học đơn giản, nhân bản. Việc tạo các đối tượng mới từ các lớp được thực hiện bằng cách khởi tạo, bao gồm việc giải thích thông tin định dạng trong một lớp. Khởi tạo tương tự như xây dựng một ngôi nhà từ một kế hoạch. Sao chép hấp dẫn chúng tôi như một phép ẩn dụ đơn giản hơn so với khởi tạo.

Ví dụ về các mô-đun có sẵn . Các nguyên mẫu cụ thể hơn các lớp vì chúng là ví dụ về các đối tượng hơn là các mô tả về định dạng và khởi tạo. Những ví dụ này có thể giúp người dùng sử dụng lại các mô-đun bằng cách làm cho chúng dễ hiểu hơn. Một hệ thống dựa trên nguyên mẫu cho phép người dùng kiểm tra một đại diện điển hình thay vì yêu cầu anh ta hiểu ý nghĩa của mô tả.

Hỗ trợ cho các đối tượng có một không hai . Tự cung cấp một khung có thể dễ dàng bao gồm các đối tượng có một không hai với hành vi của riêng họ. Vì mỗi đối tượng có các vị trí được đặt tên và các vị trí có thể giữ trạng thái hoặc hành vi, bất kỳ đối tượng nào cũng có thể có các vị trí hoặc hành vi duy nhất. Các hệ thống dựa trên lớp được thiết kế cho các tình huống có nhiều đối tượng có cùng hành vi. Không có hỗ trợ ngôn ngữ cho một đối tượng sở hữu hành vi độc đáo của riêng mình và thật khó xử khi tạo một lớp được đảm bảo chỉ có một trường hợp [ nghĩ mẫu đơn lẻ ]. Bản thân phải chịu đựng những bất lợi này. Bất kỳ đối tượng có thể được tùy chỉnh với hành vi của chính nó. Một đối tượng duy nhất có thể giữ hành vi duy nhất và không cần một "thể hiện" riêng biệt.

Loại bỏ hồi quy meta . Không có đối tượng trong một hệ thống dựa trên lớp có thể tự cung cấp; một đối tượng khác (lớp của nó) là cần thiết để thể hiện cấu trúc và hành vi của nó. Điều này dẫn đến một hồi quy meta vô hạn về mặt khái niệm: a pointlà một thể hiện của lớp Point, là một thể hiện của metaclass Point, là một thể hiện của metametaclass Point, ad infinitum. Mặt khác, trong các hệ thống dựa trên nguyên mẫu, một đối tượng có thể bao gồm hành vi của chính nó; không có đối tượng khác là cần thiết để hít thở sự sống vào nó. Nguyên mẫu loại bỏ hồi quy meta.

Bản thân có lẽ là ngôn ngữ đầu tiên để thực hiện các nguyên mẫu (nó cũng đi tiên phong trong các công nghệ thú vị khác như JIT, sau này được đưa vào JVM), vì vậy việc đọc các bài báo khác cũng nên được hướng dẫn.


5
RE: Loại bỏ hồi quy meta: Trong Hệ thống đối tượng Lisp chung, dựa trên lớp, một pointthể hiện của lớp Point, là một thể hiện của siêu dữ liệu standard-class, là một thể hiện của chính nó, finitum quảng cáo.
Tối đa Nanasy

Liên kết đến một giấy tờ tự chết. Liên kết làm việc: Bản thân: Sức mạnh của sự đơn giản | Một thư mục tự
user1201917

24

Bạn nên xem một cuốn sách tuyệt vời về JavaScript của Douglas Crockford . Nó cung cấp một lời giải thích rất tốt về một số quyết định thiết kế được đưa ra bởi những người tạo JavaScript.

Một trong những khía cạnh thiết kế quan trọng của JavaScript là hệ thống kế thừa nguyên mẫu của nó. Các đối tượng là công dân hạng nhất trong JavaScript, rất nhiều chức năng thông thường cũng được triển khai dưới dạng đối tượng (chính xác là đối tượng 'Hàm'). Theo tôi, khi ban đầu nó được thiết kế để chạy bên trong một trình duyệt, nó có nghĩa là được sử dụng để tạo ra nhiều đối tượng đơn lẻ. Trong trình duyệt DOM, bạn tìm thấy cửa sổ, tài liệu, vv tất cả các đối tượng singleton. Ngoài ra, JavaScript là ngôn ngữ động được gõ một cách lỏng lẻo (trái ngược với Python được gõ mạnh, ngôn ngữ động), do đó, một khái niệm về mở rộng đối tượng đã được triển khai thông qua việc sử dụng thuộc tính 'nguyên mẫu'.

Vì vậy, tôi nghĩ rằng có một số ưu điểm đối với OO dựa trên nguyên mẫu được triển khai trong JavaScript:

  1. Thích hợp trong môi trường gõ lỏng lẻo, không cần xác định loại rõ ràng.
  2. Làm cho nó cực kỳ dễ thực hiện mẫu đơn (so sánh JavaScript và Java về vấn đề này và bạn sẽ biết tôi đang nói về điều gì).
  3. Cung cấp các cách áp dụng một phương thức của một đối tượng trong ngữ cảnh của một đối tượng khác, thêm và thay thế các phương thức một cách linh hoạt từ một đối tượng, v.v. (những điều không thể có trong một ngôn ngữ được gõ mạnh).

Dưới đây là một số nhược điểm của OO nguyên mẫu:

  1. Không có cách dễ dàng để thực hiện các biến riêng tư. Có thể triển khai các vars riêng bằng cách sử dụng thuật sĩ của Crockford bằng cách sử dụng các bao đóng , nhưng chắc chắn nó không tầm thường như sử dụng các biến riêng tư trong Java hoặc C #.
  2. Tôi chưa biết cách triển khai nhiều kế thừa (với giá trị của nó) trong JavaScript.

2
Chỉ cần sử dụng quy ước đặt tên cho các vars riêng, giống như Python.
aehlke

1
trong js cách làm vars riêng là đóng cửa và không phụ thuộc vào kiểu thừa kế bạn chọn.
Benja

6
Crockford đã làm rất nhiều để làm hỏng JavaScript, trong đó một ngôn ngữ kịch bản khá đơn giản đã bị biến thành một niềm đam mê chính với các phần bên trong của nó. JS không có phạm vi từ khóa riêng tư thực sự hoặc nhiều kế thừa thực sự: đừng cố giả mạo chúng.
Hal50000
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.