2009-05-21 45 views
162

Tôi mới sử dụng JavaScript OOP. Bạn có thể giải thích sự khác biệt giữa các khối mã sau không. Tôi đã thử nghiệm và cả hai khối đều hoạt động. Thực hành tốt nhất là gì và tại sao?Hiểu sự thừa kế nguyên mẫu trong JavaScript

khối đầu tiên:

function Car(name){ 
    this.Name = name; 
} 

Car.prototype.Drive = function(){ 
    document.write("My name is " + this.Name + " and I'm driving. <br />"); 
} 

SuperCar.prototype = new Car(); 
SuperCar.prototype.constructor = SuperCar; 

function SuperCar(name){ 
    Car.call(this, name); 
} 

SuperCar.prototype.Fly = function(){ 
    document.write("My name is " + this.Name + " and I'm flying! <br />"); 
} 

var myCar = new Car("Car"); 
myCar.Drive(); 

var mySuperCar = new SuperCar("SuperCar"); 
mySuperCar.Drive(); 
mySuperCar.Fly(); 

khối thứ hai:

function Car(name){ 
    this.Name = name; 
    this.Drive = function(){ 
     document.write("My name is " + this.Name + " and I'm driving. <br />"); 
    } 
} 

SuperCar.prototype = new Car(); 

function SuperCar(name){ 
    Car.call(this, name); 
    this.Fly = function(){ 
     document.write("My name is " + this.Name + " and I'm flying! <br />"); 
    } 
} 

var myCar = new Car("Car"); 
myCar.Drive(); 

var mySuperCar = new SuperCar("SuperCar"); 
mySuperCar.Drive(); 
mySuperCar.Fly(); 

Tại sao tác giả thêm DriveFly phương pháp sử dụng prototype, nhưng không khai báo như this.Drive phương pháp bên Car lớp và this.Fly trong lớp học SuperCar?

Tại sao SuperCar.prototype.constructor cần được đặt lại thành SuperCar? Thuộc tính hàm tạo có bị ghi đè khi prototype được đặt không? Tôi nhận xét ra dòng này và không có gì thay đổi.

Tại sao gọi Car.call(this, name); trong số SuperCar hàm tạo? Sẽ không tài sản và phương pháp Car được 'thừa hưởng' khi tôi làm

var myCar = new Car("Car"); 
+3

Cách hiện đại hơn là sử dụng để Object.create () thay vì mới: http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

+2

Chỉ cần một lưu ý ví dụ này có thể gây nhầm lẫn: 'siêu' trong trường hợp này đề cập đến một chiếc xe bay, trong khi 'siêu' trong OOP thường đề cập đến nguyên mẫu/lớp cha mẹ. – mikemaccana

+2

Tôi nghĩ rằng phần làm cho sự thừa kế nguyên mẫu của JavaScript khó hiểu là nơi nó kết hợp nó với một hàm tạo. Điều gì sẽ xảy ra nếu chúng ta loại bỏ ý tưởng của hàm tạo và lớp, và chúng ta chỉ có các đối tượng. Mỗi đối tượng được thừa kế từ không có gì (null) hoặc từ một đối tượng khác. Điều đó sẽ dễ hiểu hơn rất nhiều, và đó chính xác là những gì mà Object.create() làm. –

Trả lời

78

Hai khối khác nhau trong một cách mà trong ví dụ đầu tiên Drive() sẽ chỉ tồn tại một lần trong khi tiếp cận thứ hai Drive() sẽ tồn tại mỗi trường hợp (Mỗi khi bạn làm new Car() chức năng drive() sẽ được tạo ra một lần nữa). Hoặc khác nhau cho biết đầu tiên sử dụng nguyên mẫu để lưu trữ các chức năng và thứ hai các nhà xây dựng. Tra cứu các hàm là constructor và sau đó là prototype. Vì vậy, cho tra cứu của bạn Drive() nó tìm thấy nó bất kể nếu nó là trong constructor hoặc trong nguyên mẫu. Sử dụng nguyên mẫu hiệu quả hơn vì thường bạn chỉ cần một hàm một lần cho mỗi loại.

Cuộc gọi new tự động đặt hàm tạo trong mẫu thử nghiệm. Nếu bạn đang ghi đè nguyên mẫu, do đó bạn phải thiết lập hàm tạo theo cách thủ công.

Thừa kế trong javascript không có gì giống như super. Vì vậy, nếu bạn có một phân lớp cơ hội duy nhất để gọi các nhà xây dựng siêu là bởi tên của nó.

1

Tôi không chắc chắn 100%, nhưng tôi tin rằng sự khác biệt là ví dụ thứ hai chỉ đơn giản là bản sao nội dung của lớp Car vào siêu xe đối tượng, trong khi đầu tiên liên kết nguyên mẫu SuperCar với lớp Car, do đó các thay đổi thời gian chạy tới lớp Car cũng ảnh hưởng đến lớp SuperCar.

136

Để thêm vào Norbert Hartl's answer, SuperCar.prototype.constructor là không cần thiết, nhưng một số người sử dụng nó như là một cách thuận tiện để nhận hàm xây dựng của một đối tượng (đối tượng SuperCar trong trường hợp này).

Chỉ cần từ ví dụ đầu tiên, Car.call (điều này, tên) là trong hàm constructor siêu xe bởi vì khi bạn làm điều này:

var mySuperCar = new SuperCar("SuperCar"); 

Đây là những gì hiện JavaScript:

  1. Một đối tượng mới, trống được khởi tạo.
  2. Nguyên mẫu nội bộ của đối tượng mới được đặt thành Ô tô.
  3. Hàm khởi tạo SuperCar chạy.
  4. Đối tượng đã hoàn thành được trả về và được đặt trong mySuperCar.

Lưu ý cách JavaScript không gọi xe cho bạn. Nguyên mẫu là như họ đang có, bất kỳ tài sản hoặc phương pháp mà bạn không đặt mình cho SuperCar sẽ được nhìn lên trong xe. Đôi khi điều này là tốt, ví dụ: SuperCar không có phương pháp Drive, nhưng nó có thể chia sẻ một chiếc xe, vì vậy tất cả các SuperCars sẽ sử dụng cùng một phương pháp Drive. Lần khác bạn không muốn chia sẻ, giống như mỗi SuperCar có tên riêng của nó. Vậy làm cách nào để đặt tên của mỗi SuperCar thành thứ riêng của nó? Bạn có thể đặt this.Name bên trong hàm tạo hàm SuperCar:

function SuperCar(name){ 
    this.Name = name; 
} 

Công việc này, nhưng chờ một giây. Chúng ta không làm chính xác những điều tương tự trong các nhà xây dựng xe? Không muốn lặp lại chính mình. Kể từ khi xe đặt tên đã, chúng ta hãy chỉ cần gọi nó.

function SuperCar(name){ 
    this = Car(name); 
} 

Rất tiếc, bạn không bao giờ muốn thay đổi tham chiếu đối tượng this đặc biệt. Nhớ 4 bước? Treo lên đối tượng mà JavaScript cung cấp cho bạn, bởi vì đó là cách duy nhất để giữ liên kết nguyên mẫu nội bộ quý giá giữa đối tượng SuperCar và Ô tô của bạn. Vậy làm thế nào để chúng ta đặt Tên, mà không lặp lại chính mình và không vứt bỏ đối tượng SuperCar mới của chúng ta, JavaScript đã dành rất nhiều nỗ lực đặc biệt để chuẩn bị cho chúng ta?

Hai điều. Một: ý nghĩa của this là linh hoạt. Hai: Xe là một chức năng. Có thể gọi Car, không phải với một đối tượng khởi tạo mới, nguyên sơ, mà thay vào đó là đối tượng SuperCar. Cung cấp cho chúng ta những giải pháp cuối cùng, đó là một phần của ví dụ đầu tiên trong câu hỏi của bạn:

function SuperCar(name){ 
    Car.call(this, name); 
} 

Là một chức năng, xe được phép được gọi với của chức năng call method, làm thay đổi ý nghĩa của this trong Xe đưa Trường hợp SuperCar chúng tôi đang xây dựng. Mau! Bây giờ mỗi SuperCar nhận được thuộc tính Tên riêng của nó.

Để kết thúc, Car.call(this, name) trong hàm tạo SuperCar, mỗi đối tượng SuperCar mới là thuộc tính Tên duy nhất của riêng nó, nhưng không sao chép mã đã có trong Ô tô.

Nguyên mẫu không đáng sợ khi bạn hiểu chúng, nhưng chúng hoàn toàn không giống mô hình OOP cổ điển/kế thừa. Tôi đã viết một bài báo về the prototypes concept in JavaScript. Nó được viết cho một công cụ trò chơi sử dụng JavaScript, nhưng đó là cùng một công cụ JavaScript được Firefox sử dụng, vì vậy tất cả phải có liên quan. Hi vọng điêu nay co ich.

+4

"SuperCar.prototype.constructor" là cần thiết nếu bạn muốn ' Object.getPrototypeOf' mô phỏng trong các công cụ cũ để làm việc – Raynos

+3

Liên kết hiện tại tới "khái niệm nguyên mẫu trong Javascript" bị hỏng. Có nơi nào khác tôi có thể đọc bài viết này không? – shockawave123

8

Norbert, bạn nên lưu ý rằng ví dụ đầu tiên của bạn là khá nhiều những gì Douglas Crockford gọi là giả kế thừa kế. Một số điều cần lưu ý về điều này:

  1. Bạn sẽ gọi cho Người xây dựng xe hai lần, một lần từ dòng SuperCar.prototype = new Car() và một từ dòng "bắt buộc xây dựng" Car.call (điều này .. Bạn có thể tạo một phương thức trợ giúp để kế thừa các nguyên mẫu thay thế và phương thức khởi tạo ô tô của bạn sẽ chỉ phải chạy một lần để thiết lập hiệu quả hơn
  2. Dòng SuperCar.prototype.constructor = SuperCar sẽ cho phép bạn sử dụng instanceof để xác định hàm tạo. Một số người muốn người khác chỉ cần tránh sử dụng instanceof
  3. Vars tham chiếu như: var arr = ['one', 'two'] khi được xác định trên siêu (ví dụ: Ô tô) sẽ được chia sẻ bởi TẤT CẢ các trường hợp. pu sh ['ba'], inst2.arr.push ['bốn'], v.v., sẽ hiển thị cho tất cả các trường hợp!Về cơ bản, hành vi tĩnh mà bạn có thể không muốn.
  4. Khối thứ hai xác định phương thức bay trong hàm tạo. Điều này có nghĩa là mỗi khi nó được gọi, một "đối tượng phương thức" sẽ được tạo ra. Tốt hơn để sử dụng một mẫu thử nghiệm cho các phương pháp! Tuy nhiên bạn có thể giữ nó trong constructor nếu bạn muốn - bạn chỉ cần bảo vệ để bạn chỉ thực sự khởi tạo nguyên mẫu một lần (giả): if (SuperCar.prototype.myMethod! = 'Function') ... sau đó xác định nguyên mẫu của bạn.
  5. 'Tại sao gọi Car.call (tên này, tên) ....': Tôi không có thời gian để xem xét cẩn thận mã của bạn vì vậy tôi có thể sai nhưng điều này thường là để mỗi trường hợp có thể giữ trạng thái riêng của nó để khắc phục vấn đề hành vi 'tĩnh' của chuỗi mẫu thử nghiệm mà tôi đã mô tả ở trên.

Cuối cùng, tôi muốn đề cập đến rằng tôi có một số ví dụ mã TDD Javascript thừa kế mà làm việc ở đây: TDD JavaScript Inheritance Code and Essay Tôi rất muốn nhận được phản hồi của bạn như tôi hy vọng để cải thiện nó và giữ nó mã nguồn mở . Mục đích là để giúp các lập trình viên cổ điển bắt kịp tốc độ với JavaScript một cách nhanh chóng và cũng bổ sung cho việc nghiên cứu cả sách Crockford và Zakas.

+1

Tôi đã đi qua điểm (1) trước bây giờ. Tôi thấy rằng nếu không sử dụng các từ khóa mới và chỉ sử dụng Superclass.call (this) (vì vậy SuperCar = {Car.call (this);}; SuperCar.prototype = Car;) mà điều này dường như làm việc. Có lý do nào để không làm điều này không? (xin lỗi cho necro) – obie

+0

Xuất phát từ * ngôn ngữ OOP * cổ điển, tôi luôn thấy * lạ * cần phải tạo một trường hợp 'mới' * chỉ * để điền vào chuỗi nguyên mẫu – superjos

+0

Sách hay. Bạn nên lưu ý rằng liên kết ở đầu trang github bị hỏng, tuy nhiên, tất nhiên, nó có thể được tìm thấy trong nội dung. –

0
function abc() { 
} 

phương pháp thử nghiệm và tài sản tạo ra cho chức năng abc

abc.prototype.testProperty = 'Hi, I am prototype property'; 
abc.prototype.testMethod = function() { 
    alert('Hi i am prototype method') 
} 

Tạo trường mới cho chức năng abc

var objx = new abc(); 

console.log(objx.testProperty); // will display Hi, I am prototype property 
objx.testMethod();// alert Hi i am prototype method 

var objy = new abc(); 

console.log(objy.testProperty); //will display Hi, I am prototype property 
objy.testProperty = Hi, I am over-ridden prototype property 

console.log(objy.testProperty); //will display Hi, I am over-ridden prototype property 

http://astutejs.blogspot.in/2015/10/javascript-prototype-is-easy.html

Các vấn đề liên quan