2012-04-28 62 views
6

Tôi nghĩ rằng sự khác biệt đã được nhấp vào trong đầu của tôi, nhưng tôi chỉ muốn chắc chắn.Trong Javascript, sự khác biệt giữa 'Object.create' và 'new'

Trên Douglas Crockford trang Prototypal Inheritance in JavaScript, ông nói

Trong một hệ thống nguyên chủng, các đối tượng kế thừa từ đối tượng. Tuy nhiên, JavaScript, thiếu một toán tử thực hiện thao tác đó. Thay vào đó, có toán tử mới, chẳng hạn f() mới tạo ra một đối tượng mới mà kế thừa từ f.prototype.

Tôi không thực sự hiểu những gì anh ấy đang cố gắng nói trong câu đó vì vậy tôi đã thực hiện một số thử nghiệm. Dường như với tôi rằng sự khác biệt chính là nếu tôi tạo một đối tượng dựa trên một đối tượng khác trong một hệ thống nguyên mẫu thuần túy, thì tất cả các thành viên cha mẹ phải ở trên nguyên mẫu của đối tượng mới, chứ không phải trên chính đối tượng mới.

Dưới đây là các bài kiểm tra:

var Person = function(name, age) { 
     this.name = name; 
     this.age = age; 
} 
Person.prototype.toString = function(){return this.name + ', ' + this.age}; 

// The old way... 
var jim = new Person("Jim",13); 
for (n in jim) { 
    if (jim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output 'name' and 'age'. 

// The pure way... 
var tim = Object.create(new Person("Tim",14)); 
for (n in tim) { 
    if (tim.hasOwnProperty(n)) { 
     console.log(n); 
    } 
} 
// This will output nothing because all the members belong to the prototype. 
// If I remove the hasOwnProperty check then 'name' and 'age' will be output. 

là hiểu biết của tôi đúng khi cho rằng sự khác biệt duy trở nên rõ ràng khi thử nghiệm cho các thành viên trên các đối tượng riêng của mình?

+3

xem http://stackoverflow.com/questions/4166616/understanding-the-difference-between-object-create-and-new-somefunction-in-j –

+0

Tôi đã thấy câu hỏi đó ngày hôm qua, tôi nghĩ, nhưng câu trả lời không rõ ràng với tôi. Bây giờ tôi đã thực hiện các bài kiểm tra của mình và gõ câu hỏi của tôi, nó là rõ ràng! – Jules

Trả lời

3

Giả định của bạn là chính xác, nhưng có một mẫu khác mà Douglas không nói nhiều - nguyên mẫu có thể được sử dụng cho các thuộc tính. lớp người của bạn có thể đã được viết như sau:

var Person = function(name, age) { 
    this.name = name; 
    this.age = age; 
} 
Person.prototype.name = null; //default value if you don't init in ctor 
Person.prototype.age = null; 
Person.prototype.gender = "male"; 
Person.prototype.toString = function(){return this.name + ', ' + this.age;}; 

Trong trường hợp này, lặp lại đối với tài sản của một thể hiện của lớp này, như bạn làm trong ví dụ của bạn, sẽ tạo ra không có đầu ra cho 'giới tính' bất động sản.

EDIT 1: Việc gán tên và tuổi trong hàm tạo làm làm cho thuộc tính hiển thị bởi hasOwnProperty (nhờ @matt để nhắc tôi về điều này). Thuộc tính giới tính chưa được gán sẽ không hiển thị cho đến khi ai đó đặt nó trên cá thể.

EDIT 2: Tiếp tục thêm vào điều này, tôi trình bày một mô hình thừa kế thay thế - người mà tôi đã cá nhân sử dụng cho các dự án rất lớn:

var inherits = function(childCtor, parentCtor) { 
    function tempCtor() {}; 
    tempCtor.prototype = parentCtor.prototype; 
    childCtor.superclass = parentCtor.prototype; 
    childCtor.prototype = new tempCtor(); 
    childCtor.prototype.constructor = childCtor; 
}; 

var Person = function(name){ 
    this.name = name; 
} 
Person.prototype.name = ""; 
Person.prototype.toString = function(){ 
    return "My name is " + this.name; 
} 

var OldPerson = function(name, age){ 
    OldPerson.superclass.constructor.call(this); 
    this.age = age 
}; 
inherits(OldPerson, Person); 
OldPerson.prototype.age = 0; 
OldPerson.prototype.toString = function(){ 
    var oldString = OldPerson.superclass.toString.call(this); 
    return oldString + " and my age is " + this.age; 
} 

Đây là một mô hình khá phổ biến với một twist nhỏ - lớp cha được gắn với đứa trẻ thông qua thuộc tính "siêu lớp" cho phép bạn truy cập các phương thức/thuộc tính được ghi đè bởi đứa trẻ. Về mặt kỹ thuật, bạn có thể thay thế OldPerson.superclass bằng Person, tuy nhiên điều đó không lý tưởng. Nếu bạn đã từng thay đổi OldPerson thành kế thừa từ một lớp không phải là Person, bạn cũng phải cập nhật tất cả các tham chiếu đến Person.

EDIT 3: Chỉ cần mang vòng tròn đầy đủ này, đây là một phiên bản của "kế thừa" chức năng mà lợi dụng Object.create và chức năng giống hệt nhau như trước đây tôi đã mô tả:

var inherits = function(childCtor, parentCtor) { 
    childCtor.prototype = Object.create(parentCtor.prototype); 
    childCtor.superclass = parentCtor.prototype; 
}; 
3

EDIT : Câu trả lời này ban đầu là một câu trả lời cho câu trả lời của @ jordancpaul, mà anh ta đã sửa chữa.Tôi sẽ để lại phần câu trả lời của tôi giúp giải thích sự khác biệt quan trọng giữa các thuộc tính mẫu và các đặc tính mẫu:

Trong một số trường hợp, các thuộc tính được chia sẻ giữa mọi trường hợp và bạn cần phải rất cẩn thận bất cứ khi nào bạn khai báo các thuộc tính trên nguyên mẫu. Hãy xem xét ví dụ sau:

Person.prototype.favoriteColors = []; //Do not do this!

Bây giờ, nếu bạn tạo một đối tượng Person mới sử dụng một trong hai Object.create hoặc new, nó không hoạt động như bạn mong đợi ...

var jim = new Person("Jim",13); 
jim.favoriteColors.push('red'); 
var tim = new Person("Tim",14); 
tim.favoriteColors.push('blue'); 

console.log(tim.favoriteColors); //outputs an array containing red AND blue! 

này doesn' t có nghĩa là bạn không bao giờ có thể khai báo tài sản trên nguyên mẫu, nhưng nếu bạn làm thế, bạn và mọi nhà phát triển làm việc trên mã của bạn cần phải nhận thức được điều này. Trong một trường hợp như thế này, nếu bạn thích tuyên bố tài sản trên nguyên mẫu vì lý do gì, bạn có thể làm:

Person.prototype.favoriteColors = null

Và khởi tạo nó vào một mảng trống trong constructor:

var Person = function(name, age) { 
    ... 
    this.favoriteColors = []; 
} 

Các Quy tắc chung khi sử dụng phương pháp này là các giá trị mặc định cho các thuộc tính đơn giản (chuỗi, số, booleans) có thể được đặt trực tiếp trên nguyên mẫu, nhưng bất kỳ thuộc tính nào kế thừa từ Object (bao gồm mảng và ngày) phải được đặt thành null và sau đó khởi tạo trong hàm tạo.

Cách an toàn hơn là chỉ khai báo các phương thức trên nguyên mẫu và luôn khai báo các thuộc tính trong hàm tạo.

Dù sao, câu hỏi là về Object.create ...

Đối số đầu tiên truyền cho Object.create được thiết lập như nguyên mẫu của các trường hợp mới. Sử dụng tốt hơn sẽ là:

var person = { 
    initialize: function(name, age) { 
     this.name = name; 
     this.age = age; 
     return this; 
    }, 

    toString: function() { 
     return this.name + ', ' + this.age; 
    } 
}; 

var tim = Object.create(person).initialize("Tim",14); 

Bây giờ đầu ra sẽ giống như trong ví dụ đầu tiên của bạn.

Như bạn có thể thấy, đó là một cách tiếp cận triết học khác với phong cách cổ điển hơn của OOP trong Javascript. Với Object.create, sự nhấn mạnh là tạo ra các đối tượng mới từ các đối tượng hiện có, thay vì trên hàm tạo. Khởi tạo sau đó trở thành một bước riêng biệt.

Cá nhân tôi có cảm xúc lẫn lộn về cách tiếp cận Object.create; nó rất tốt cho việc thừa kế vì tham số thứ hai mà bạn có thể sử dụng để thêm các thuộc tính bổ sung vào một nguyên mẫu hiện có, nhưng nó cũng tiết hơn và làm cho nó kiểm tra instanceof không còn hoạt động nữa (ví dụ thay thế trong ví dụ này là kiểm tra person.isPrototypeOf(tim)) .

Lý do chính tôi nói Object.create là verbose là vì tham số thứ hai, nhưng có một số thư viện hữu ích trên mạng địa chỉ đó rằng:

https://github.com/Gozala/selfish

https://github.com/Raynos/pd

(và những người khác)

Tôi hy vọng điều đó sẽ khai sáng hơn là khó hiểu!

+1

Tôi vừa viết một thư viện * rất * đơn giản để giúp thừa kế theo sau phương pháp thừa kế nguyên mẫu truyền thống, nhưng nó sử dụng Object.create khi mở rộng "các lớp": https://github.com/mbrowne/simpleoo. js –

+0

Yup, nạn nhân bị rơi vào bản thân 'Class.prototype.property = []'. Tôi đã cập nhật bài đăng gốc của mình với thông tin thêm một chút - có lẽ bạn đã thấy mẫu này trước đây. – jordancpaul

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