2010-11-11 32 views
73

Khá Gần đây tôi đọc về việc sử dụng cuộc gọi JavaScript trong MDCThừa kế Javascript: gọi super-constructor hoặc sử dụng chuỗi nguyên mẫu?

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call

một linke của ví dụ hình dưới đây, tôi vẫn không hiểu.

Tại sao họ sử dụng thừa kế ở đây như thế này

Prod_dept.prototype = new Product(); 

là này cần thiết? Bởi vì có một cuộc gọi đến các siêu constructor trong

Prod_dept() 

dù sao, như thế này

Product.call 

là điều này chỉ ra khỏi hành vi phổ biến? Khi nào thì tốt hơn khi sử dụng lệnh gọi cho siêu khởi tạo hoặc sử dụng chuỗi nguyên mẫu?

function Product(name, value){ 
    this.name = name; 
    if(value >= 1000) 
    this.value = 999; 
    else 
    this.value = value; 
} 

function Prod_dept(name, value, dept){ 
    this.dept = dept; 
    Product.call(this, name, value); 
} 

Prod_dept.prototype = new Product(); 

// since 5 is less than 1000, value is set 
cheese = new Prod_dept("feta", 5, "food"); 

// since 5000 is above 1000, value will be 999 
car = new Prod_dept("honda", 5000, "auto"); 

Cảm ơn đã khiến mọi thứ rõ ràng hơn

+0

Cách bạn sử dụng nó gần như đúng nhưng bạn có thể muốn sử dụng Object.create() thay vì khởi tạo cơ sở bằng từ khóa mới (có thể gây ra vấn đề nếu hàm tạo cơ sở cần đối số). Tôi có thông tin chi tiết trên blog của mình: http://ncombo.wordpress.com/2013/07/11/javascript-inheritance-done-right/ – Jon

+1

Cũng lưu ý rằng Product() được gọi hiệu quả hai lần. –

Trả lời

103

Câu trả lời cho câu hỏi thực sự là bạn cần phải làm gì cả:

  • Thiết nguyên mẫu để một thể hiện của phụ huynh khởi chuỗi nguyên mẫu (thừa kế), điều này được thực hiện chỉ một lần (từ nguyên mẫu đối tượng được chia sẻ).
  • Gọi hàm khởi tạo của cha mẹ khởi tạo chính đối tượng, điều này được thực hiện với mọi phiên âm (bạn có thể chuyển các tham số khác nhau mỗi khi bạn xây dựng nó).

Vì vậy, bạn không nên gọi hàm tạo của cha mẹ khi thiết lập thừa kế. Chỉ khi instantiating một đối tượng kế thừa từ một đối tượng khác.

Câu trả lời của Chris Morgan gần như hoàn tất, thiếu một chi tiết nhỏ (thuộc tính hàm tạo). Hãy để tôi đề xuất một phương pháp để thiết lập kế thừa.

function extend(base, sub) { 
    // Avoid instantiating the base class just to setup inheritance 
    // Also, do a recursive merge of two prototypes, so we don't overwrite 
    // the existing prototype, but still maintain the inheritance chain 
    // Thanks to @ccnokes 
    var origProto = sub.prototype; 
    sub.prototype = Object.create(base.prototype); 
    for (var key in origProto) { 
    sub.prototype[key] = origProto[key]; 
    } 
    // The constructor property was set wrong, let's fix it 
    Object.defineProperty(sub.prototype, 'constructor', { 
    enumerable: false, 
    value: sub 
    }); 
} 

// Let's try this 
function Animal(name) { 
    this.name = name; 
} 

Animal.prototype = { 
    sayMyName: function() { 
    console.log(this.getWordsToSay() + " " + this.name); 
    }, 
    getWordsToSay: function() { 
    // Abstract 
    } 
} 

function Dog(name) { 
    // Call the parent's constructor 
    Animal.call(this, name); 
} 

Dog.prototype = { 
    getWordsToSay: function(){ 
     return "Ruff Ruff"; 
    } 
}  

// Setup the prototype chain the right way 
extend(Animal, Dog); 

// Here is where the Dog (and Animal) constructors are called 
var dog = new Dog("Lassie"); 
dog.sayMyName(); // Outputs Ruff Ruff Lassie 
console.log(dog instanceof Animal); // true 
console.log(dog.constructor); // Dog 

Xem bài đăng trên blog của tôi để biết thêm đường cú pháp nữa khi tạo lớp học.http://js-bits.blogspot.com/2010/08/javascript-inheritance-done-right.html

Kỹ thuật sao chép từ Ext JS và http://www.uselesspickles.com/class_library/ và một bình luận từ https://stackoverflow.com/users/1397311/ccnokes

+6

Trong EcmaScript5 + (tất cả các trình duyệt hiện đại), bạn có thể làm cho nó không thể đếm được nếu bạn định nghĩa nó như thế này 'Object.defineProperty (sub.protoype, 'constructor', {enumerable: false, value: sub}); cách bạn sẽ nhận được chính xác "hành vi" giống như khi javascript tạo một thể hiện mới của hàm (hàm tạo được đặt là enumerable = false tự động) – Adaptabi

+2

Bạn không thể đơn giản hóa phương thức mở rộng thành hai dòng? Cụ thể là: sub.prototype = Object.create (base.prototype); sub.prototype.constructor = sub; – Appetere

+0

@Steve Có thể, khi tôi lần đầu tiên viết bài này, 'Object.create' không được hỗ trợ tốt ... cập nhật nó. Lưu ý rằng hầu hết các polyfills 'Object.create' được thực hiện bằng cách sử dụng kỹ thuật tôi đã cho thấy ban đầu. –

28

Cách lý tưởng để làm điều đó là để không làm Prod_dept.prototype = new Product();, bởi vì đây gọi constructor Product. Vì vậy, cách lý tưởng là để sao chép nó ngoại trừ các nhà xây dựng, một cái gì đó như thế này:

function Product(...) { 
    ... 
} 
var tmp = function(){}; 
tmp.prototype = Product.prototype; 

function Prod_dept(...) { 
    Product.call(this, ...); 
} 
Prod_dept.prototype = new tmp(); 
Prod_dept.prototype.constructor = Prod_dept; 

Sau đó, các nhà xây dựng siêu được gọi vào thời điểm xây dựng, đó là những gì bạn muốn, bởi vì sau đó bạn có thể vượt qua các thông số, quá.

Nếu bạn nhìn vào những thứ như Thư viện đóng cửa của Google, bạn sẽ thấy đó là cách họ thực hiện.

+0

Tôi gọi hàm tạo được sử dụng để thiết lập thừa kế hàm tạo thay thế. Ví dụ của bạn vẫn quên đặt lại thuộc tính hàm tạo sau khi thiết lập thừa kế để bạn có thể phát hiện đúng hàm tạo –

+1

@Juan: OK, được cập nhật để thêm 'Prod_dept.prototype.constructor = Prod_dept;'. –

+0

@ChrisMorgan Tôi đang gặp khó khăn khi hiểu dòng cuối cùng trong mẫu của bạn: 'Prod_dept.prototype.constructor = Prod_dept;'. Trước hết, tại sao nó lại cần thiết và tại sao nó trỏ đến 'Prod_dept' thay vì' Product'? –

6

Nếu bạn đã làm Object Oriented Programming trong JavaScript, bạn sẽ biết rằng bạn có thể tạo ra một lớp như sau:

Person = function(id, name, age){ 
    this.id = id; 
    this.name = name; 
    this.age = age; 
    alert('A new person has been accepted'); 
} 

Cho đến nay lớp học của chúng tôi chỉ có hai tài sản và chúng tôi sẽ cung cấp cho nó một số phương pháp. Cách làm sạch là để sử dụng đối tượng 'nguyên mẫu' của nó. Bắt đầu từ JavaScript 1.1, đối tượng nguyên mẫu được giới thiệu trong JavaScript. Đây là một đối tượng được xây dựng trong đó đơn giản hóa quá trình thêm thuộc tính và phương thức tùy chỉnh vào tất cả các phiên bản của đối tượng. Hãy thêm 2 phương pháp để lớp của chúng tôi sử dụng đối tượng 'nguyên mẫu' của nó như sau:

Person.prototype = { 
    /** wake person up */ 
    wake_up: function() { 
     alert('I am awake'); 
    }, 

    /** retrieve person's age */ 
    get_age: function() { 
     return this.age; 
    } 
} 

Bây giờ chúng ta đã xác định Person lớp của chúng tôi. Điều gì sẽ xảy ra nếu chúng ta muốn định nghĩa một lớp khác được gọi là Trình quản lý kế thừa một số thuộc tính từ Person. Không có điểm nào xác định lại tất cả các thuộc tính này một lần nữa khi chúng ta định nghĩa lớp Manager, chúng ta có thể thiết lập nó để kế thừa từ lớp Person. JavaScript không đã xây dựng trong kế thừa nhưng chúng ta có thể sử dụng một kỹ thuật để thực hiện quyền thừa kế như sau:

Inheritance_Manager = {}; // Chúng tôi tạo ra một lớp quản lý thừa kế (tên là tùy ý)

Bây giờ chúng ta hãy cung cấp cho lớp kế thừa của chúng tôi một phương thức được gọi là extend, lấy các đối số baseClass và subClassas. Trong phương thức mở rộng, chúng ta sẽ tạo một lớp bên trong được gọi là thừa kế hàm thừa kế() {}. Lý do tại sao chúng ta đang sử dụng lớp bên trong này là để tránh nhầm lẫn giữa các nguyên mẫu baseClass và subClass. Tiếp theo, chúng ta tạo nguyên mẫu của điểm lớp kế thừa của chúng ta thành nguyên mẫu baseClass như sau: inheritance.prototype = baseClass. nguyên mẫu; Sau đó, chúng ta sao chép mẫu thử nghiệm kế thừa vào nguyên mẫu subClass như sau: subClass.prototype = new inheritance(); Điều tiếp theo là xác định hàm tạo cho lớp con của chúng ta như sau: subClass.prototype.constructor = subClass; Sau khi kết thúc với việc tạo mẫu con SubClass, chúng ta có thể chỉ định hai dòng mã tiếp theo để thiết lập một số con trỏ lớp cơ sở.

subClass.baseConstructor = baseClass; 
subClass.superClass = baseClass.prototype; 

Đây là mã đầy đủ cho chức năng mở rộng của chúng tôi:

Inheritance_Manager.extend = function(subClass, baseClass) { 
    function inheritance() { } 
    inheritance.prototype = baseClass.prototype; 
    subClass.prototype = new inheritance(); 
    subClass.prototype.constructor = subClass; 
    subClass.baseConstructor = baseClass; 
    subClass.superClass = baseClass.prototype; 
} 

Bây giờ chúng ta đã thực hiện thừa kế của chúng tôi, chúng ta có thể bắt đầu sử dụng nó để mở rộng các lớp học của chúng tôi. Trong trường hợp này chúng ta sẽ mở rộng lớp người chúng tôi vào một lớp học quản lý như sau:

Chúng ta định nghĩa lớp Manager

Manager = function(id, name, age, salary) { 
    Person.baseConstructor.call(this, id, name, age); 
    this.salary = salary; 
    alert('A manager has been registered.'); 
} 

chúng ta làm cho nó kế thừa hình thức Person

Inheritance_Manager.extend(Manager, Person); 

Nếu bạn chú ý, chúng ta vừa gọi phương thức extend của lớp Inheritance_Manager của chúng ta và thông qua subClass Manager trong trường hợp của chúng ta và sau đó là baseClass Person. Lưu ý rằng thứ tự rất quan trọng ở đây. Nếu bạn hoán đổi chúng, thừa kế sẽ không hoạt động như bạn dự định nếu có. Cũng lưu ý rằng bạn sẽ cần phải xác định thừa kế này trước khi bạn thực sự có thể xác định subClass của chúng tôi. Bây giờ, hãy xác định subClass của chúng tôi:

Chúng tôi có thể thêm các phương thức khác làm phương thức bên dưới. Lớp Manager của chúng ta sẽ luôn có các phương thức và thuộc tính được định nghĩa trong lớp Person bởi vì nó kế thừa từ nó.

Manager.prototype.lead = function(){ 
    alert('I am a good leader'); 
} 

Bây giờ để kiểm tra nó cho phép chúng ta tạo ra hai đối tượng, một từ Person lớp và một từ lớp Manager thừa hưởng:

var p = new Person(1, 'Joe Tester', 26); 
var pm = new Manager(1, 'Joe Tester', 26, '20.000'); 

Hãy thoải mái để lấy mã đầy đủ và bình luận chi tiết tại: http://www.cyberminds.co.uk/blog/articles/how-to-implement-javascript-inheritance.aspx

+2

http://blog.codinghorror.com/i-shall-call-it-somethingmanager/ –

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