Kế thừa JavaScript: Object.create vs new


123

Trong JavaScript, sự khác biệt giữa hai ví dụ này là gì:

Điều kiện tiên quyết:

function SomeBaseClass(){
}

SomeBaseClass.prototype = {
    doThis : function(){
    },

    doThat : function(){
    }
}

Ví dụ kế thừa A sử dụng Object.create:

function MyClass(){
}

MyClass.prototype = Object.create(SomeBaseClass.prototype);

Ví dụ kế thừa B sử dụng từ khóa mới

function MyClass(){
}

MyClass.prototype = new SomeBaseClass();

Cả hai ví dụ dường như làm điều tương tự. Khi nào bạn sẽ chọn cái này hơn cái kia?

Một câu hỏi bổ sung: Xem xét mã trong liên kết bên dưới (dòng 15), trong đó tham chiếu đến hàm tạo riêng của hàm được lưu trữ trong nguyên mẫu. Tại sao điều này hữu ích?

https://github.com/mrdoob/three.js/blob/master/src/loaders/ImageLoader.js

Trích dẫn (nếu bạn không muốn mở liên kết):

THREE.ImageLoader.prototype = {

    constructor: THREE.ImageLoader
}

23
Tại sao cái quái này được đánh dấu là trùng lặp!?! Các câu hỏi khác, và câu trả lời, thậm chí không đề cập Object.create. Đây là một sai lầm, và nên được mở lại.
Scott Rippey

2
Nếu có bất cứ điều gì, đó là bản sao của stackoverflow.com/questions/4166616/ Khăn
Scott Rippey

1
13 phiếu trên bình luận đó và vẫn chưa mở lại ..?!
TJ

Câu trả lời:


111

Trong câu hỏi của bạn, bạn đã đề cập rằng Both examples seem to do the same thing, Điều đó không đúng chút nào, bởi vì

Ví dụ đầu tiên của bạn

function SomeBaseClass(){...}
SomeBaseClass.prototype = {
    doThis : function(){...},
    doThat : function(){...}
}
function MyClass(){...}
MyClass.prototype = Object.create(SomeBaseClass.prototype);

Trong ví dụ này, bạn chỉ đang thừa kế SomeBaseClass' prototypenhưng nếu bạn có một tài sản SomeBaseClassnhư thế nào

function SomeBaseClass(){ 
    this.publicProperty='SomeValue'; 
}

và nếu bạn sử dụng nó như

var obj=new MyClass();
console.log(obj.publicProperty); // undefined
console.log(obj);​

Đối objtượng sẽ không có publicPropertytài sản như trong ví dụ này .

Ví dụ thứ hai của bạn

MyClass.prototype = new SomeBaseClass();

Đó là thực thi constructorchức năng, tạo một thể hiện SomeBaseClassvà kế thừa toàn bộ SomeBaseClassđối tượng. Vì vậy, nếu bạn sử dụng

    var obj=new MyClass();
    console.log(obj.publicProperty); // SomeValue
    console.log(obj);​

Trong trường hợp này, thuộc tính của nó publicPropertycũng có sẵn cho objđối tượng như trong ví dụ này .

Object.createkhông có sẵn trong một số trình duyệt cũ, nên trong trường hợp đó bạn có thể sử dụng

if(!Object.create)
{
    Object.create=function(o){
        function F(){}
        F.prototype=o;
        return new F();
    }
}

Mã ở trên chỉ thêm Object.createchức năng nếu nó không có sẵn để bạn có thể sử dụng Object.createchức năng và tôi nghĩ mã ở trên mô tả những gì Object.createthực sự làm. Hy vọng nó sẽ giúp một cách nào đó.


6
Xin chào. Cảm ơn những nỗ lực của bạn về điều này. Vâng, sự khác biệt là hàm tạo được chạy trong ví dụ thứ hai nhưng không phải trong ví dụ đầu tiên. (đó là mong muốn trong trường hợp của tôi). Về tài sản công cộng không xác định không được kế thừa từ siêu triển khai, bạn chỉ cần gọi siêu trong hàm tạo của con: someBaseClass.call (cái này). Kiểm tra fiddle này: jsfiddle.net/NhQGB
ChrisRich

Tôi đã tìm kiếm một cách siêu đơn giản để kế thừa đúng cách trong JS mà không cần sử dụng bất kỳ thư viện / khung công tác nào. Tôi nghĩ ví dụ này (trong fiddle của tôi ở trên) là cách tiếp cận tốt nhất cho các trình duyệt hiện đại. Có lẽ polyfill Object.Create có thể thêm hỗ trợ cho các trình duyệt cũ?
ChrisRich

về cơ bản để tóm tắt, nếu bạn thực hiện
davidjnelson

Doest "MyClass.prototype = new someBaseClass ()" có nghĩa là phiên bản mới của someBaseClass được tạo khi phiên bản của MyClass chưa được tạo?

Không, chỉ khi bạn tạo đối tượng chính, nguyên mẫu mới được đặt thành đó SomeBaseClass.
Alpha

39

Cả hai ví dụ dường như làm điều tương tự.

Điều đó đúng trong trường hợp của bạn.

Khi nào bạn sẽ chọn cái này hơn cái kia?

Khi SomeBaseClasscó một thân hàm, điều này sẽ được thực hiện với newtừ khóa. Điều này thường không có ý định - bạn chỉ muốn thiết lập chuỗi nguyên mẫu. Trong một số trường hợp, nó thậm chí có thể gây ra sự cố nghiêm trọng vì bạn thực sự khởi tạo một đối tượng, có các biến trong phạm vi riêng tư được chia sẻ bởi tất cả các MyClasstrường hợp khi chúng thừa hưởng cùng các phương thức đặc quyền. Các tác dụng phụ khác là có thể tưởng tượng.

Vì vậy, bạn thường nên thích Object.create. Tuy nhiên, nó không được hỗ trợ trong một số trình duyệt cũ; đó là lý do bạn thấy new-approach quá thường xuyên vì nó thường không gây hại (rõ ràng). Cũng có một cái nhìn vào câu trả lời này .


Đây là câu trả lời hay nhất, được giải thích rõ ràng
spex

8

Sự khác biệt trở nên rõ ràng nếu bạn sử dụng Object.create()như dự định. Trên thực tế, nó hoàn toàn ẩn náuprototype từ trong mã của bạn, nó sẽ thực hiện công việc dưới mui xe. Sử dụng Object.create(), chúng ta có thể đi như

var base =  {
    doThis : function(){
    },

    doThat : function(){
    }
};

Và sau đó chúng ta có thể mở rộng / kế thừa các đối tượng khác từ điều này

var myObject = Object.create( base );
// myObject will now link to "base" via the prototype chain internally

Vì vậy, đây là một khái niệm khác, một cách hít vào "hướng đối tượng" hơn. Không có "hàm xây dựng" nào ngoài hộp sử dụngObject.create() . Nhưng tất nhiên bạn chỉ có thể tạo và gọi một hàm xây dựng tự xác định trong các đối tượng đó.

Một đối số để sử dụng Object.create()là nó có thể trông tự nhiên hơn khi trộn / * kế thừa * từ các đối tượng khác, hơn là sử dụng cách mặc định của Javascripts .


7
Object.createthực sự không thể thay thế cách tiếp cận cổ điển bằng newkhi cần một hàm tạo
Bergi

1
Nó chắc chắn có thể @Bergi. Nhìn vào bài đăng trên blog này: davidwalsh.name/javascript-objects-deconloyment . Bạn cũng có thể truyền đối tượng khởi tạo làm tham số thứ hai cho Object.create ().
LocalPCGuy

@LocalPCGuy: Không, không thể, bởi vì Object.createkhông gọi một chức năng cần thiết để tạo các bao đóng. Tất nhiên, với Object.createbạn có thể đặt một initphương thức vào các đối tượng của bạn và gọi nó ngay lập tức và nó thậm chí có thể trả về thisđể bạn có được cú pháp ngắn gọn - nhưng chờ đã, đó một hàm tạo.
Bergi

1
Gọi một hàm init hoạt động tương tự như hàm tạo, nhưng nó không phải là hàm tạo. Và phương pháp đó cho phép bạn thay thế chính xác phương pháp cổ điển bằng Object.create. Và mô hình tinh thần của liên kết đối tượng đơn giản hơn nhiều so với mô hình được tạo thông qua 'mới'.
LocalPCGuy

Chà, mô hình tinh thần của tôi newsử dụng "liên kết đối tượng", nên không có nhiều khác biệt. Trong thực tế, chỉ có hai điều: .constructorđược gọi .initvà hàm đó không có thuộc .prototypetính trỏ lại đối tượng nguyên mẫu của bạn. Phần còn lại chỉ là cú pháp - và tôi thích new Xhơn Object.create(x).init().
Bergi

0

Tôi không phải là một chuyên gia về java script nhưng đây là một ví dụ đơn giản để hiểu sự khác biệt giữa "Object.create" và "new" ..

Bước 1: tạo hàm cha với một số thuộc tính và hành động ..

function Person() {

this.name = 'venkat';

this.address = 'dallas';

this.mobile='xxxxxxxxxx'

}

Person.prototype.func1 = function () {

    return this.name + this.address;
}

Bước 2: tạo một hàm con (PersonSalary) mở rộng ở trên hàm Person bằng từ khóa mới ..

function PersonSalary() {
    Person.call(this);
}
PersonSalary.prototype = new Person();

PersonSalary();

Bước 3: tạo hàm con thứ hai (PersonLeaves) mở rộng trên hàm Person bằng từ khóa Object.create ..

function PersonLeaves() {
 Person.call(this);
}
PersonLeaves.prototype = Object.create(Person.prototype);


PersonLeaves();

// Bây giờ kiểm tra cả hai nguyên mẫu hàm con.

PersonSalary.prototype
PersonLeaves.prototype

cả hai hàm con này sẽ liên kết với nguyên mẫu Person (hàm cha) và có thể truy cập các phương thức của nó, nhưng nếu bạn tạo hàm con bằng cách sử dụng mới, nó sẽ trả về một đối tượng hoàn toàn mới với tất cả các thuộc tính cha mẹ mà chúng ta không cần và cả khi bạn tạo bất kỳ đối tượng hoặc hàm sử dụng "Mới" mà hàm cha được thực thi mà chúng ta không muốn.

Đây là những bài học

nếu bạn chỉ muốn ủy quyền cho một số phương thức trong hàm cha và không muốn tạo một đối tượng mới, sử dụng Object.create là cách tốt nhất.

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.