So sánh Factory Function, Constructor và Class

So sánh Factory Function, Constructor và Class

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 methoddata 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  hay  class , 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ì?

  1. Khởi tạo một object mới và  bind  giá trị cho từ khóa  this
  2. Bind  instance.__proto__  vào  Constructor.prototype
  3. Bind  instance.__proto__.constructor  vào  Constructor
  4. 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óa  this
  • 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.

CodeGym Full-stack

Leave a Reply

Your email address will not be published. Required fields are marked *