Câu trả lời:
Sự khác biệt cơ bản là hàm xây dựng được sử dụng với new
từ khóa (khiến JavaScript tự động tạo một đối tượng mới, đặt this
trong hàm cho đối tượng đó và trả về đối tượng):
var objFromConstructor = new ConstructorFunction();
Hàm nhà máy được gọi là hàm "thông thường":
var objFromFactory = factoryFunction();
Nhưng để nó được coi là một "nhà máy", nó sẽ cần trả về một thể hiện mới của một đối tượng nào đó: bạn sẽ không gọi nó là hàm "nhà máy" nếu nó vừa trả về một boolean hoặc một cái gì đó. Điều này không xảy ra tự động như với new
, nhưng nó cho phép linh hoạt hơn đối với một số trường hợp.
Trong một ví dụ thực sự đơn giản, các hàm được tham chiếu ở trên có thể trông giống như thế này:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Tất nhiên bạn có thể làm cho các chức năng của nhà máy phức tạp hơn nhiều so với ví dụ đơn giản đó.
Một lợi thế cho các chức năng của nhà máy là khi đối tượng được trả về có thể có nhiều loại khác nhau tùy thuộc vào một số tham số.
someMethod
các đối tượng được trả về bởi nhà máy và đó là nơi có một chút sương mù. Bên trong chức năng của nhà máy, nếu chỉ cần thực hiện var obj = { ... , someMethod: function() {}, ... }
, điều đó sẽ dẫn đến mỗi đối tượng được trả về giữ một bản sao khác nhau someMethod
mà chúng ta có thể không muốn. Đó là nơi sử dụng new
và prototype
bên trong chức năng của nhà máy sẽ giúp ích.
new
với chức năng xây dựng; Tôi nghĩ đó là nơi mà người ta có thể cần phải xem cách thay thế các nhà xây dựng bằng ví dụ về chức năng của nhà máy và đó là nơi tôi nghĩ rằng tính nhất quán trong các ví dụ là bắt buộc. Dù sao, câu trả lời là đủ thông tin. Đây chỉ là một điểm tôi muốn nêu ra, không phải là tôi đang giảm chất lượng câu trả lời bằng mọi cách.
new
nội bộ, hoặc sử dụng Object.create()
để tạo một đối tượng với một nguyên mẫu cụ thể.
Hầu hết các cuốn sách dạy bạn sử dụng các nhà xây dựng và new
this
đề cập đến đối tượng mới
Một số người thích cách var myFoo = new Foo();
đọc.
Chi tiết về khởi tạo được rò rỉ vào API gọi (thông qua new
yêu cầu), vì vậy tất cả các trình gọi được liên kết chặt chẽ với việc triển khai hàm tạo. Nếu bạn cần sự linh hoạt bổ sung của nhà máy, bạn sẽ phải cấu trúc lại tất cả người gọi (phải thừa nhận trường hợp ngoại lệ, thay vì quy tắc).
Quên new
là một lỗi phổ biến như vậy, bạn nên cân nhắc thêm việc kiểm tra bản tóm tắt để đảm bảo rằng hàm tạo được gọi chính xác ( if (!(this instanceof Foo)) { return new Foo() }
). EDIT: Vì ES6 (ES2015) bạn không thể quên new
với hàm class
tạo, hoặc hàm tạo sẽ gây ra lỗi.
Nếu bạn thực hiện instanceof
kiểm tra, nó sẽ để lại sự mơ hồ về việc có new
cần thiết hay không . Theo tôi, không nên như vậy. Bạn đã thực sự rút ngắn new
yêu cầu, điều đó có nghĩa là bạn có thể xóa nhược điểm # 1. Nhưng sau đó, bạn vừa có một chức năng của nhà máy trừ tên , với bản tóm tắt bổ sung, chữ in hoa và this
bối cảnh kém linh hoạt hơn .
Nhưng mối quan tâm chính của tôi là nó vi phạm nguyên tắc mở / đóng. Bạn bắt đầu xuất một hàm tạo, người dùng bắt đầu sử dụng hàm tạo, sau đó xuống đường bạn nhận ra rằng bạn cần sự linh hoạt của một nhà máy, thay vào đó (ví dụ, để chuyển việc triển khai sang sử dụng nhóm đối tượng hoặc để khởi tạo qua các bối cảnh thực thi hoặc để khởi tạo qua các bối cảnh thực thi hoặc để có tính linh hoạt kế thừa hơn bằng cách sử dụng OO nguyên mẫu).
Bạn đang bị mắc kẹt, mặc dù. Bạn không thể thực hiện thay đổi mà không phá vỡ tất cả mã gọi hàm tạo của bạn vớinew
. Chẳng hạn, bạn không thể chuyển sang sử dụng nhóm đối tượng để tăng hiệu suất.
Ngoài ra, việc sử dụng các hàm tạo cho bạn một sự lừa đảo instanceof
không hoạt động trong các bối cảnh thực thi và không hoạt động nếu nguyên mẫu hàm tạo của bạn bị tráo đổi. Nó cũng sẽ thất bại nếu bạn bắt đầu quay trở lạithis
từ hàm tạo của mình và sau đó chuyển sang xuất một đối tượng tùy ý, điều bạn phải làm để kích hoạt hành vi giống như nhà máy trong hàm tạo của bạn.
Ít mã hơn - không yêu cầu nồi hơi.
Bạn có thể trả về bất kỳ đối tượng tùy ý nào và sử dụng bất kỳ nguyên mẫu tùy ý nào - giúp bạn linh hoạt hơn để tạo các loại đối tượng khác nhau thực hiện cùng một API. Ví dụ: trình phát phương tiện có thể tạo phiên bản của cả trình phát HTML5 và flash hoặc thư viện sự kiện có thể phát ra các sự kiện DOM hoặc sự kiện ổ cắm web. Các nhà máy cũng có thể khởi tạo các đối tượng trên các bối cảnh thực hiện, tận dụng các nhóm đối tượng và cho phép các mô hình kế thừa nguyên mẫu linh hoạt hơn.
Bạn sẽ không bao giờ có nhu cầu chuyển đổi từ nhà máy sang nhà xây dựng, vì vậy tái cấu trúc sẽ không bao giờ là vấn đề.
Không mơ hồ về việc sử dụng new
. Đừng. (Nó sẽ làm cho this
hành vi xấu, xem điểm tiếp theo).
this
hoạt động như bình thường - vì vậy bạn có thể sử dụng nó để truy cập đối tượng cha mẹ (ví dụ, bên trong player.create()
, this
đề cập đến player
, giống như bất kỳ lời gọi phương thức nào khác. call
và apply
cũng chỉ định lại this
, như mong đợi. Nếu bạn lưu trữ nguyên mẫu trên đối tượng cha, điều đó có thể là một cách tuyệt vời để tự động trao đổi chức năng và cho phép đa hình rất linh hoạt cho việc khởi tạo đối tượng của bạn.
Không mơ hồ về việc có nên viết hoa hay không. Đừng. Các công cụ Lint sẽ phàn nàn, và sau đó bạn sẽ cố gắng sử dụng new
, và sau đó bạn sẽ hoàn tác các lợi ích được mô tả ở trên.
Một số người thích cách var myFoo = foo();
hoặc var myFoo = foo.create();
đọc.
new
không hành xử như mong đợi (xem ở trên). Giải pháp: không sử dụng nó.
this
không đề cập đến đối tượng mới (thay vào đó, nếu hàm tạo được gọi bằng ký hiệu dấu chấm hoặc ký hiệu dấu ngoặc vuông, ví dụ foo.bar () - this
đề cập đến foo
- giống như mọi phương thức JavaScript khác - xem lợi ích).
new
vi phạm nguyên tắc mở / đóng. Xem Medium.com/javascript-scene/ trên để biết một cuộc thảo luận lớn hơn nhiều so với những nhận xét này cho phép.
new
từ khóa, tôi không tin rằng new
từ khóa thực sự cung cấp bất kỳ khả năng đọc bổ sung nào. IMO, có vẻ ngớ ngẩn khi nhảy qua các vòng để cho phép người gọi nhập nhiều hơn.
Một constructor trả về một thể hiện của lớp mà bạn gọi nó. Một chức năng nhà máy có thể trả lại bất cứ điều gì. Bạn sẽ sử dụng hàm xuất xưởng khi bạn cần trả về các giá trị tùy ý hoặc khi một lớp có quy trình thiết lập lớn.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
tạo một đối tượng được tạo nguyên mẫu User.prototype
và gọi User
với đối tượng được tạo làm this
giá trị của nó .
new
coi một biểu thức đối số cho toán hạng của nó là tùy chọn:
let user = new User;
sẽ gây ra new
cuộc gọi User
mà không có đối số.
new
trả về đối tượng mà nó đã tạo, trừ khi hàm tạo trả về một giá trị đối tượng , được trả về thay thế. Đây là một trường hợp cạnh mà phần lớn có thể bỏ qua.
Các đối tượng được tạo bởi các hàm xây dựng kế thừa các thuộc tính từ thuộc tính của hàm tạo prototype
và trả về true bằng instanceOf
toán tử trên hàm xây dựng.
Các hành vi trên có thể thất bại nếu bạn thay đổi động giá trị của thuộc tính của hàm tạo prototype
sau khi đã sử dụng hàm tạo. Làm như vậy là rất hiếm và không thể thay đổi nếu hàm tạo được tạo bằng class
từ khóa.
Các hàm xây dựng có thể được mở rộng bằng cách sử dụng extends
từ khóa.
Hàm xây dựng không thể trả về null
dưới dạng giá trị lỗi. Vì nó không phải là kiểu dữ liệu đối tượng, nên nó bị bỏ qua new
.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Ở đây chức năng nhà máy được gọi là không có new
. Hàm hoàn toàn chịu trách nhiệm cho việc sử dụng trực tiếp hoặc gián tiếp nếu các đối số của nó và loại đối tượng mà nó trả về. Trong ví dụ này, nó trả về một [Đối tượng] đơn giản với một số thuộc tính được đặt từ các đối số.
Dễ dàng che giấu sự phức tạp thực hiện của việc tạo đối tượng từ người gọi. Điều này đặc biệt hữu ích cho các chức năng mã gốc trong trình duyệt.
Hàm nhà máy không cần phải luôn trả về các đối tượng cùng loại và thậm chí có thể trả về null
dưới dạng chỉ báo lỗi.
Trong các trường hợp đơn giản, các chức năng của nhà máy có thể đơn giản về cấu trúc và ý nghĩa.
Các đối tượng được trả về thường không được thừa kế từ thuộc tính của hàm nhà máy prototype
và trả về false
từ đó instanceOf factoryFunction
.
Hàm nhà máy không thể được mở rộng một cách an toàn bằng extends
từ khóa vì các đối tượng mở rộng sẽ kế thừa từ thuộc tính chức năng của nhà máy prototype
thay vì từ thuộc prototype
tính của hàm tạo được sử dụng bởi hàm nhà máy.
Các nhà máy "luôn luôn" tốt hơn. Khi sử dụng ngôn ngữ hướng đối tượng thì
Việc triển khai (các đối tượng thực tế được tạo mới) không được hiển thị cho người dùng / người tiêu dùng của nhà máy. Điều này có nghĩa là nhà phát triển nhà máy có thể mở rộng và tạo các triển khai mới miễn là họ không phá vỡ hợp đồng ... và nó cho phép người tiêu dùng nhà máy chỉ được hưởng lợi từ API mới mà không phải thay đổi mã của họ ... nếu họ đã sử dụng mới và triển khai "mới" thì họ phải đi và thay đổi mọi dòng sử dụng "mới" để sử dụng triển khai "mới" ... với mã nhà máy của họ không thay đổi ...
Các nhà máy - tốt hơn tất cả mọi thứ khác - khung mùa xuân được xây dựng hoàn toàn xung quanh ý tưởng này.
Các nhà máy là một lớp trừu tượng, và giống như tất cả các trừu tượng mà chúng có a.cost về độ phức tạp. Khi gặp API dựa trên nhà máy, việc tìm ra nhà máy dành cho API cụ thể có thể là thách thức đối với người tiêu dùng API. Với các nhà xây dựng khám phá là tầm thường.
Khi quyết định giữa các nhà máy và nhà máy, bạn cần phải quyết định xem sự phức tạp có hợp lý bởi lợi ích hay không.
Đáng lưu ý rằng các nhà xây dựng Javascript có thể là các nhà máy tùy ý bằng cách trả lại một cái gì đó không phải cái này hoặc không xác định. Vì vậy, trong js, bạn có thể tận dụng tốt nhất cả hai thế giới - API có thể khám phá và nhóm đối tượng / bộ đệm.
new
, Thay đổi hành vi this
, Thay đổi giá trị trả về, Kết nối một tham chiếu nguyên mẫu, Kích hoạt instanceof
(nằm và không nên được sử dụng cho mục đích này). Rõ ràng, tất cả những thứ đó là "tính năng". Trong thực tế, họ làm tổn thương chất lượng mã của bạn.