NỘI DUNG BÀI VIẾT
Trong bài viết này, chúng ta sẽ cùng tìm hiểu về Factory Function và chỉ ra điểm khác biệt của Factory Function, Constructor và Class.
Để tạo một object
với các method và data dựng sẵn, chúng ta có các phương pháp để làm trong JS:
// class
class ClassCar {
drive() {
console.log('GOOO');
}
}
const car1 = new ClassCar();
console.log(car1.drive());
// constructor function
function ConstructorCar() {}
ConstructorCar.prototype.drive = function() {
console.log('GOOO');
}
const car2 = new ConstructorCar();
console.log(car2.drive());
// factory
const proto = {
drive() {
console.log('GOOO');
}
};
const factoryCar = () => Object.create(proto);
const car3 = factoryCar();
console.log(car3.drive());
Code language: JavaScript (javascript)
Về tính năng, thì cả 3 là như nhau, và có thể dùng thay thế cho nhau được.
Có thể bạn chưa biết: Trong JS, bất cứ function nào trả về một object, mà không phải là
constructor function
hayclass
, thì được gọi là factory function
Vài so sánh giữa Factory Function và Constructor
Constructor bắt buộc phải khởi tạo bằng keyword
new
. Factory thì không.
Vậy thì keyword new
của Constructor và Class nó làm gì?
- Khởi tạo một object mới và
bind
giá trị cho từ khóathis
- Bind
instance.__proto__
vàoConstructor.prototype
- Bind
instance.__proto__.constructor
vàoConstructor
- Ngầm trả về
this
(refer vào giá trịinstance
)
Về mặt lợi ích khi sử dụng Constructor và Class
- Dễ tiếp cận với những người có xuất phát điểm từ những ngôn ngữ lập trình có hỗ trợ
class
this
luôn prefer đến một object mới- Nhiều người thích cách viết
myFoo = new Foo()
Nhược điểm của Constructor và Class
- Bắt buộc phải dùng từ khóa
new
để khởi tạo - Tất cả những thằng sử dụng đều dùng chung một constructor, rất khó nếu muốn thay đổi hiện thực bên trong constructor từ bên ngoài.
- Không đáp ứng dụng nguyên tắc
open/closed
: API chỉ cho phép extend, nhưng không cho phép modify - Kết thừa
class
và các vấn đề mà nó sinh ra là câu chuyện không mới khi các bạn viết object oriented (có thể tra cứu google bằng các từ khóa sau: the fragile base class problem, the gorilla banana problem, the duplication by necessity problem)
Lợi ích việc sử dụng Factory
- Linh động hơn
class
vàconstructor function
- Bạn sẽ không bao giờ đụng vô từ khóa
extend
vốn là một con đường đã gây ra đau khổ bấy năm nay. - Không còn cần dùng từ khóa
new
, không còn loằn ngoằn rối rắm với từ khóathis
- Nhiều người thích đọc code dạng này
myFoo = createFoo()
Nhược điểm của Factory
- Không thể check
instanceof
, do không có liên kết giữa instance vàFactory.prototype
this
không còn refer vào object mới tạo (this
cũng có ưu nhược điểm của nó chứ không phải toàn nhược điểm)- Có thể chậm hơn một chút. Thật ra cũng không cần quá bận tâm việc này, vì chưa ai chứng minh được nó ảnh hưởng đến tốc độ, hiệu năng của ứng dụng, lý thuyết là chậm hơn xíu xiu nhưng máy tính giờ nhanh lắm rồi.
Có nên dùng Factory Function?
Có rất nhiều quan điểm đưa ra để khuyên bạn đừng dùng contructor trong JS, bài viết Constructors Are Bad For JavaScript có liệt kê khá khá lý do bạn có thể tham khảo.
Một ví dụ tương đối đầy đủ về factory function
const Player = (name, level) => {
let health = level * 2;
const getLevel = () => level;
const getName = () => name;
const die = () => {
// uh oh
};
const damage = x => {
health -= x;
if (health <= 0) {
die();
}
};
const attack = enemy => {
if (level < enemy.getLevel()) {
damage(1);
console.log(`${enemy.getName()} has damaged ${name}`);
}
if (level >= enemy.getLevel()) {
enemy.damage(1);
console.log(`${name} has damaged ${enemy.getName()}`);
}
};
return {attack, damage, getLevel, getName}
};
const jimmie = Player('jim', 10);
const badGuy = Player('jeff', 5);
jimmie.attack(badGuy);
Code language: JavaScript (javascript)
Để kế thừa trong factory function, các bạn có thể làm như sau
const Person = (name) => {
const sayName = () => console.log(`my name is ${name}`)
return {sayName}
}
const Nerd = (name) => {
// tạo Person, sau đó trả về hàm sayName
const {sayName} = Person(name)
const doSomethingNerdy = () => console.log('nerd stuff')
return {sayName, doSomethingNerdy}
}
const jeff = Nerd('jeff')
jeff.sayName() //my name is jeff
jeff.doSomethingNerdy() // nerd stuff
Code language: JavaScript (javascript)
Với cách trên, chỉ định rất cụ thể hàm nào sẽ được trả về, còn nếu muốn trả tất cả những gì của Person, đơn giản là merge object
const Nerd = (name) => {
const prototype = Person(name)
const doSomethingNerdy = () => console.log('nerd stuff')
return Object.assign({}, prototype, {doSomethingNerdy})
}
Code language: JavaScript (javascript)
Nghe có vẻ hơi trái tai, mặc dù JS đã có hỗ trợ class, nhưng các bạn đừng nên dùng nó.
Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.