bởi Kevin Kononenko

Nếu bạn đã từng nấu một bữa ăn ở nhà, thì bạn có thể hiểu cách viết mã trạng thái bằng phương pháp lập trình hướng đối tượng trong JavaScript.
Khi bạn bắt đầu viết các chương trình JavaScript đơn giản, bạn không cần phải lo lắng về số lượng biến bạn đang sử dụng hoặc cách các hàm và đối tượng khác nhau hoạt động cùng nhau.
Ví dụ: hầu hết mọi người bắt đầu bằng cách sử dụng nhiều biến toàn cục hoặc các biến nằm trong phạm vi cấp cao nhất của tệp. Chúng không phải là một phần của bất kỳ lớp, đối tượng hoặc chức năng riêng lẻ nào.
Ví dụ, đây là một biến toàn cầu được gọi là tiểu bang:
let state = "global";
Nhưng một khi chương trình của bạn bắt đầu liên quan đến nhiều chức năng và/hoặc đối tượng khác nhau, bạn sẽ cần tạo một bộ quy tắc chặt chẽ hơn cho mã của mình.
Đây là nơi khái niệm về trạng thái phát huy tác dụng. Trạng thái mô tả trạng thái của toàn bộ chương trình hoặc một đối tượng riêng lẻ. Nó có thể là văn bản, số, boolean hoặc kiểu dữ liệu khác.
Đó là một công cụ phổ biến để điều phối mã. Ví dụ: khi bạn cập nhật trạng thái, một loạt các chức năng khác nhau có thể phản ứng ngay lập tức với thay đổi đó.
Bài viết này mô tả trạng thái trong ngữ cảnh của React, một thư viện JavaScript phổ biến.
Nhưng đoán xem? Ngay cả trạng thái cũng có thể khiến bạn đau đầu khi mã của bạn trở nên phức tạp! Thay đổi trạng thái có thể gây ra những hậu quả không mong muốn.
Hãy dừng ngay tại đó. Trạng thái là một công cụ phổ biến trong lập trình hướng đối tượng, hay OOP. Nhưng nhiều lập trình viên thích lập trình chức năng hơn, điều này không khuyến khích thay đổi trạng thái. JavaScript hỗ trợ cả hai mô hình.
Được rồi, đó là rất nhiều thuật ngữ cùng một lúc. Tôi muốn tìm cách chỉ ra cách OOP và lập trình chức năng có thể hoàn thành các mục tiêu giống nhau, ngay cả khi lập trình chức năng không sử dụng trạng thái.
Hướng dẫn này sẽ chỉ ra cách bạn có thể nấu một bữa mì spaghetti và nước sốt từ góc độ OOP và chức năng.
Dưới đây là bản xem trước nhanh về hai cách tiếp cận khác nhau:

Hãy nhảy vào nó. Để hiểu hướng dẫn này, bạn chỉ cần hiểu các hàm và đối tượng trong JavaScript.
Phương pháp hướng đối tượng (Sử dụng trạng thái)
Trong hình trên, chúng tôi đã chỉ ra hai cách tiếp cận khác nhau để làm bữa tối với mì ống này:
- Một phương pháp tập trung vào trạng thái của các công cụ khác nhau, như bếp, nồi và mì ống.
- Một phương pháp tập trung vào sự tiến triển của thực phẩm, không đề cập đến trạng thái của các dụng cụ riêng lẻ (nồi, bếp, v.v.)
Cách tiếp cận hướng đối tượng tập trung vào việc cập nhật trạng thái, vì vậy mã của chúng tôi sẽ có trạng thái ở hai cấp độ khác nhau:
- Toàn cầu, hoặc trạng thái của toàn bộ bữa ăn này.
- cục bộ cho từng đối tượng.
Chúng ta sẽ sử dụng cú pháp ES6 trong hướng dẫn này để tạo các đối tượng. Đây là một ví dụ về trạng thái toàn cục và nguyên mẫu “Pot”.
let stoveTemp = 500;
function Pot(){ this.boilStatus=""; this.startBoiling = function(){ if( stoveTemp > 400) this.boilStatus = "boiling"; }}
let pastaPot = new Pot();pastaPot.startBoiling();
console.log(pastaPot);// Pot { boilStatus="boiling"; }
Ghi chú: tôi đã đơn giản hóa console.log
tuyên bố để tập trung vào bản cập nhật trạng thái.
Đây là một đại diện trực quan của logic đó:
Trước

Sau đó

Có hai trạng thái, và khi pastaPot
được tạo thông qua Pot
nguyên mẫu, ban đầu nó có một cái trống boilStatus
. Nhưng sau đó, có một sự thay đổi trạng thái.
Chúng ta chạy pastaPot.startBoiling()
và bây giờ là boilStatus
(trạng thái cục bộ) đang “sôi sục”, vì trạng thái toàn cầu của stoveTemp
là hơn 400.
Bây giờ chúng ta hãy tiến thêm một bước nữa. Chúng tôi sẽ cho phép mì ống được đun sôi do trạng thái pastaPot
.
Đây là mã chúng tôi sẽ thêm vào đoạn mã trên:
function Pasta (){ this.cookedStatus = false; this.addToPot = function (boilStatus){ if(boilStatus == "boiling") this.cookedStatus = true; }}
let myMeal = new Pasta();myMeal.addToPot(pastaPot.boilStatus);
console.log(myMeal.cookedStatus);// true
Ồ! Đó là rất nhiều cùng một lúc. Đây là những gì đã xảy ra.
- Chúng tôi đã tạo một nguyên mẫu mới của “Pasta”, trong đó mọi đối tượng sẽ có một trạng thái cục bộ được gọi là
cookedStatus
- Chúng tôi đã tạo một phiên bản Pasta mới có tên là
myMeal
- Chúng tôi đã sử dụng trạng thái từ
pastaPot
đối tượng mà chúng tôi đã tạo trong đoạn mã cuối cùng để xác định xem chúng tôi có nên cập nhật trạng thái được gọi làcookedStatus
TrongmyMeal
để nấu chín. - Kể từ khi tình trạng của
boilStatus
TrongpastaPot
đã “sôi”, mì ống của chúng tôi bây giờ đã được nấu chín!
Đây là quá trình trực quan:
Trước

Sau đó

Vì vậy, bây giờ chúng ta có trạng thái cục bộ của một đối tượng, phụ thuộc vào trạng thái cục bộ của đối tượng khác. Và trạng thái cục bộ đó phụ thuộc vào một số trạng thái toàn cầu! Bạn có thể thấy điều này có thể khó khăn như thế nào. Tuy nhiên, ít nhất hiện tại nó rất dễ theo dõi, vì các trạng thái được cập nhật rõ ràng.
Phương thức chức năng (không có trạng thái)
Để hiểu đầy đủ về trạng thái, bạn sẽ có thể tìm ra cách đạt được kết quả tương tự như mã ở trên mà không thực sự sửa đổi trạng thái. Đây là nơi lập trình chức năng giúp!
Lập trình hàm có hai giá trị cốt lõi tách biệt nó khỏi OOP: tính bất biến và hàm thuần túy.
Tôi sẽ không đi sâu vào các chủ đề đó, nhưng nếu bạn muốn tìm hiểu thêm, tôi khuyên bạn nên xem hướng dẫn này về lập trình chức năng trong JavaScript.
Cả hai nguyên tắc này đều không khuyến khích sử dụng sửa đổi trạng thái trong mã của bạn. Điều đó có nghĩa là chúng ta không thể sử dụng trạng thái cục bộ hoặc toàn cầu.
Thay vào đó, lập trình hàm khuyến khích chúng ta truyền tham số cho các hàm riêng lẻ. Chúng ta có thể sử dụng các biến bên ngoài, nhưng chúng ta không thể sử dụng chúng làm trạng thái.
Đây là một ví dụ về một chức năng sẽ đun sôi mì ống:
const stoveTemp = 500;
const cookPasta = (temp) => { if(temp > 400) return 'cooked';}
console.log(cookPasta(stoveTemp));// 'cooked'
Mã này sẽ trả về thành công một chuỗi ‘đã nấu chín’. Nhưng lưu ý – không có đối tượng nào mà chúng tôi đang cập nhật. Hàm chỉ trả về giá trị sẽ được sử dụng trong bước tiếp theo.
Thay vào đó, chúng tôi tập trung vào đầu vào và đầu ra của một chức năng: cookPasta
.
Quan điểm này xem xét sự biến đổi của chính thực phẩm, hơn là các công cụ được sử dụng để nấu nó. Khó hình dung hơn một chút, nhưng chúng ta không cần hàm phụ thuộc vào trạng thái bên ngoài.
Đây là những gì nó trông giống như:

Hãy coi nó như một “chế độ xem dòng thời gian” cho tiến trình của bữa ăn — chức năng đặc biệt này chỉ bao gồm phần đầu tiên, quá trình chuyển đổi từ mì ống khô sang mì ống nấu chín.
Bây giờ chúng ta hãy xem phần thứ hai khi thức ăn được phục vụ. Đây là mã sẽ phục vụ bữa ăn. Nó sẽ xuất hiện sau khối mã ở trên:
const serveMeal = (pasta) => { if (pasta == 'cooked') return 'Dinner is ready.'}
console.log( serveMeal(cookPasta(stoveTemp)) );// 'Dinner is ready.'
Bây giờ, chúng tôi đang cung cấp các kết quả của cookPasta
hoạt động trực tiếp vào serveMeal
chức năng. Một lần nữa, chúng tôi có thể làm điều này mà không cần thay đổi trạng thái hoặc thay đổi cấu trúc dữ liệu.
Đây là sơ đồ sử dụng “chế độ xem dòng thời gian” để hiển thị cách hai chức năng này hoạt động cùng nhau:

Quan tâm đến hướng dẫn trực quan hơn?
Nếu bạn thích hướng dẫn này, hãy cho nó một “vỗ tay”!
Và, nếu bạn muốn đọc thêm các hướng dẫn trực quan về HTML, CSS và JavaScript, hãy xem trang web chính của CodeAnalogies để biết hơn 50 hướng dẫn.