Contents
Chào mừng bạn đến với Fx Studio. Chúng ta tiếp tục chuyến du lịch với series Dart Tour không đồng đầy thú vị. Chủ đề cho bài viết này là Functions. Đây cũng làm 1 trong những khái niệm cơ bản của Dart & Flutter. Bạn sẽ hơi bỡ ngỡ một tí, vì với ngôn ngữ Dart có nhiều thứ huyền bí lắm.
Nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Về mặt công cụ editor thì khá đơn giản:
- TextEditor
- Visual Studio Code (nên dùng)
Hoặc bạn vào trang https://dartpad.dev/ để tiến hành code luôn. Khá là giống với Playground của Swift.
Vè mặt lý thuyết, nếu bạn chưa biết gì về Dart, thì có thể theo dõi lại các bài viết trong series Dart Tour nhóe.
Functions là gì?
Câu hỏi hơi ngáo một chút. Nhưng lâu nay, ít ai tự trả lời những cái mà ai cũng nghĩ là hiển nhiên hết. Về định nghĩa, functions (hàm) là một khối lệnh nhằm thực hiện một nhiệm vụ nhất định nào đó.
Cấu trúc cơ bản sẽ là:
Input => [ Function ] => Output
- Input là các đối số được truyền vào cho các tham số của function
- Output là giá trị trả về của function, nếu không có gì trả về kiểu
void
(không phải là không có trả gì về hết)
Mục đích duy nhất mà function được sinh ra là:
Tránh lặp lại những đoạn code giống nhau về cách xử lý thông tin.
Cú pháp
Ta sẽ phân tích cú pháp của một function tiêu chuẩn trong ngôn ngữ Dart thông qua ví dụ sau nhoé:
String hello(String name) { return 'Hello. I am $name'; }
Trong đó:
- Return Type: đó là phần đầu tiên trong dòng khai báo function. Trong ví dụ là kiểu
String
- Function Name: là tên function, phần tiếp theo sau Return Type. Trong ví dụ tên function là
hello
- Parameters: là các tham số cho function, có thể có 1 hoặc nhiều tham số, hoặc không có tham số nào hết. Chúng được khai báo trong dấu
()
. Chia thành 2 phần:- Parameter Type: kiểu của tham số (phần từ trước)
- Parameter Name: là tên của tham số (phần tử sau)
- Return Value: giá trị mà function trả về, nó sẽ được gửi trong lệnh
return
ở bên trong function - Function body: toàn bộ các dòng lệnh trong 2 dấu
{ }
Gọi hàm
Sau khi định nghĩa xong function trong Dart, chúng ta sẽ thực hiện việc gọi hàm thực thi. Bạn xem tiếp ví dụ nha:
void main() { var str = hello("Fx Studio"); print(str); }
Trong đó:
- Bạn sử dụng 2 dấu
()
để gọi thực thi một function. Bằng việc để nó ở sau tên hàm, ví dụhello()
Fx Studio
là đối số được truyền vào function. Nó tương ứng cho tham sốname
mà ta đã khai báo ở trênOutput
được gán cho biếnstr
. Đây là String được trả về trong lệnhreturn
Ngoài ra, functions trong Dart vẫn giống như các ngôn ngữ khác. Bạn hoàn toàn tự tin mà áp dụng theo các cách bạn đã biết.
Parameters
Chúng ta sẽ nói về các tham số cho các functions trong Dart tiếp nhoé. Chúng cũng khá linh hoạt đó.
Sử dụng nhiều tham số
Dart vẫn hỗ trợ khai báo nhiều tham số trong 1 function. Các tham số cách nhau bởi dấy ,
. Xem ví dụ nha:
void helloPersonAndPet(String person, String pet) { print('Hello, $person, and your furry friend, $pet!'); }
Ta có function và 2 tham số. Bạn muốn thêm gì, thì cứ thêm vào nhoé. Còn gọi hàm thì đơn giản như sau:
helloPersonAndPet('Nick', 'Boo');
Các đối số truyền vào lần lượt và cũng cách nhau bởi dấu ,
.
Tới đây, Swift & Objective-C của chúng ta đã khác xa với phần còn lại của thế giới. Khi chúng có thêm
name
&label
cho các tham số của function.
Parameters optional
Dart hỗ trợ thêm cho function một chức năng khá là hay. Biến tham số khai báo trở lên là optional. Có nghĩa là:
Có thể có trong lời gọi hàm, hoặc không có vẫn không sao.
Các tham số này sẽ được khai báo trong 2 dấu []
và kiểu dữ liệu của chúng sẽ là Optional (tìm hiểu sau nhoé).
Ví dụ nhoé!
String fullName(String first, String last, [String? title]) { if (title != null) { return '$title $first $last'; } else { return '$first $last'; } }
Trong đó:
title
là tham số tuỳ chọn.
Và cách gọi hàm đó như sau:
print(fullName('Fx', 'Studio')); print(fullName('Albert', 'Einstein', 'Professor'));
Bạn sẽ thấy ưu điểm của nó trong lời gọi hàm. Bạn sẽ không cần viết nhiều functions với việc chỉ bỏ đi 1 vài tham số.
Default values
Để cung cấp giá trị mặc định cho các tham số, thì giá trị sẽ được để sau dấu =
cho từng tham số. Khi có giá trị mặc định, khi gọi hàm nếu không được cung cấp đối số, thì function sẽ lấy giá trị mặc định được khai báo.
Nếu không có giá trị mặc định, thì sẽ là
null
. Giống ở phần trên. Do đó, chúng ta cũng có thể khai báo chúng vào trong 2 dấu[]
.
Xem ví dụ nhoé:
String fullName2(String first, String last, [String title = 'Ahihi']) { return '$title $first $last'; }
Cung cấp giá trị mặc định cho title
là ahihi
. Cách gọi cũng tương tự.
print(fullName2('Fx', 'Studio')); print(fullName2('Albert', 'Einstein', 'Professor'));
Với cách cung cấp Default Values thì sẽ loại trừ đi các trường hợp null
của các Parameters optional. Nên bạn cần hết sức lưu ý khi code nhóe.
Naming parameters
Để không thua kém Swift, thì Dart cũng hỗ trợ thêm các name
cho các tham số. Trong khai báo, chúng ta sẽ đặt các tham số này vào trong các dấu {}
. Chúng có:
- tên khi gọi hàm, làm cho ý nghĩa của các tham số được rõ ràng hơn
- có thể optional để bỏ qua giá trị, tức là
null
- có giá trị mặc định, được gán sau dấu
=
của mỗi tham số
Ví dụ sau:
bool withinTolerance(int value, {int min = 0, int max = 10}) { return min <= value && value <= max; }
Gọi thực thi như sau:
withinTolerance(9, min: 7, max: 11);
Bạn để ý giữa 9
và min: 7
và max: 11
. Sự khác biệt ở các dữ min
& max
xuất hiện trong lời gọi hàm.
Hình bóng của Swift đã quay về rồi. Ahihi!
Named parameters required
Có một lỗi nhẹ, thực chất là sự bỏ qua suggestion cho lời gọi làm. Khi các tham số trong {}
không được liệt kê ra. Và để muốn chắc chắn bạn phải cung cấp giá trị cho các tham số đó. Thì hãy sử dụng thêm từ khoá required
vào trước các tham số nào cần thiết phải có trong lời gọi hàm.
Sửa lại ví dụ trên như sau:
bool withinTolerance2({required int value, required int min, required int max}) { return min <= value && value <= max; }
Bạn sẽ thấy khác biệt khi gọi hàm này với việc suggestion từ các IDE nhoé.
Lưu ý, với từ khoá
required
thì sẽ không dùng được giá trị mặc định cho tham số.
Optional types
Được xem là một tính năng đặc biệt của functions trong Dart. Bạn có thể lượt bỏ đi phần Return Type trong khai báo function.
compliment(number) { return '$number is a very nice number.'; }
Với khai báo function như trên, Dart sẽ hiểu kiểu trả về của function là một kiểu dạng dynamic
. Mặc dù, Dart có thể suy luận ra kiểu trả về là String
bên trong hàm. Ngoài ra, với khai báo kiểu này, bạn có thể bỏ qua cả kiểu dữ liệu của tham số. Nếu bạn chú ý kĩ về khai báo number
ở ví dụ trên.
Và ví dụ trên, tương đương với khai báo sau:
String compliment(dynamic number) { return '$number is a very nice number.'; }
Thực thi function và cảm nhận kết quả nhóe!
Anonymous functions
Dart cũng rất thú vị, khi bạn có thể bỏ luôn tên functions biến chúng trở thành hàm ẩn danh (anonymuos function). Chúng ta tiếp tục khám phá chúng nó qua các phần sau đây.
Nghe qua thì cao sang quyền quý như vậy, nhưng thực chất đó là phiên bản …
Closure của Swift cho Dart mà thôi.
Ý nghĩa
- Biến 1 function thành một biến hay khai báo 1 biến với kiểu dữ liệu là 1 function
- Khai báo các tham số của một function là một kiểu function
- Return giá trị trả về là một function
Tất cả những thứ này giống như Closure làm được bên Swift yêu dấu của chúng ta nhoé.
Sử dụng cơ bản
Bạn sẽ viết function mà không có kiểu trả về và tên hàm. Thì bạn sẽ có được một hàm ẩn danh. Cú pháp cơ bản như sau:
([[Type] param1[, …]]) { codeBlock; };
Trong đó:
- Các tham số ở trong dấu
()
- Code sẽ ở trong 2 dấu
{}
- Kiểu trả về tuỳ thuộc vào giá trị
return
của function này
Ví dụ nhoé:
final multiply = (int a, int b) { return a * b; };
Lúc này, multiply
được xem là một biến. Tuy nhiên, kiểu dữ liệu của nó là một function và giá trị trả về tuỳ thuộc vào giá trị tham số truyền vào. Xem cách gọi biến thực thi nhoé.
print(multiply(2, 3));
Returning a function
Chúng ta sẽ tới phần 1 function return về một function. Tất nhiên, kiểu dữ liệu ở đây sẽ là Function
, đây cũng chính là từ khoá cho kiểu viết này.
Ví dụ:
Function applyMultiplier(num multiplier) { return (num value) { return value * multiplier; }; }
Trong đó, Function
ở đầu tiên được xem là một kiểu dữ liệu cho function applyMultiplier
. Và kết quả trả về của applyMultiplier
lại là 1 hàm ẩn danh.
Bắt đầu hơi điên về Dart rồi nhoé!
Xem tiếp ví dụ, với 1 biến tạo ra bởi kiểu Function trên.
final triple = applyMultiplier(3);
Lúc này, triple
vẫn là 1 biến với kiểu là applyMultiplier
. Chúng ta không thực thi được triple
ngay lúc này. Mà nó vẫn là chỉ là function mà thôi.
Muốn thực thi tiếp thì như sau:
print(triple(6)); print(triple(14.0));
Vì hàm ẩn danh trong phần return
lại cần 1 tham số nữa. Do đó, khi gọi biến này thực thi, bạn lại cung cấp thêm cho chúng 1 đối số tiếp.
Mệt não rồi nhoé!
Closures and scope
Anonymous functions in Dart act as what are known as closures.
Đây cũng là thưa nhận về Dart cho functions ẩn danh của nó là một bao đóng (closure). Chính vì các bao đóng này, mà sinh ra cái gọi làm phạm vi của biến.
Các biến có các phạm vi nhất định trong chính khối lệnh của nó, tức là 2 dấu
{}
nhoé.
Lấy lại ví dụ trên:
Function applyMultiplier(num multiplier) { return (num value) { return value * multiplier; }; }
Trong đó:
multiplier
có phạm vi ảnh hưởng toàn bộ function nàyvalue
chỉ ảnh hưởng trong phạm vi function ẩn danh được trả về mà thôi
Ta có 1 ví dụ khác:
var counter = 0; final incrementCounter = () { counter += 1; };
Khi biến được khai báo ở ngoài bao đóng, thì hàm ẩn danh vẫn có thể truy cập và thay đổi giá trị của biến từ bên trong. Và ta gọi thực thi nhiều lần, thì đối tượng tác động chính là biến khai báo ở ngoài đó.
incrementCounter(); incrementCounter(); incrementCounter(); incrementCounter(); incrementCounter(); print(counter); //5
Kết quả lúc này sẽ là 5
. Và cách này, cũng có ý nghĩa khi bạn sử dụng kiểu function trả về function. Xem ví dụ sau nhoé.
Function countingFunction() { var counter = 0; final incrementCounter = () { counter += 1; return counter; }; return incrementCounter; }
Cách sử dụng như sau:
final counter1 = countingFunction(); final counter2 = countingFunction(); print(counter1()); // 1 print(counter2()); // 1 print(counter1()); // 2 print(counter1()); // 3 print(counter2()); // 2
Các giá trị của counter1
và counter2
sẽ khác nhau và chúng nó không liên quan gì tới nhau. Bạn sẽ hiểu đơn giản là các biến countingFunction
sẽ còn tồn tại theo các biến counter1
và counter2
còn tồn tại. Chúng sẽ tiếp tục được các functions ẩn danh bên trong sử dụng.
Nổ não nữa rồi!
Arrow functions
Cú pháp
Trong khi Swift với function chỉ có 1 dòng thì sẽ loại bỏ đi từ khoá return
. Thì Dart từ trước đó, đã chơi trội hơn nhiều rồi. Khi Dart đưa ra một cú pháp đặc biệt cho function chỉ có 1 dòng.
Đó là
=>
Ta lấy ví dụ như sau:
int add(int a, int b) { return a + b; }
Thì có thể dùng cú pháp =>
để viết lại function trên cho gọn. Xem ví dụ nhoé!
int add(int a, int b) => a + b;
Và chúng ta cũng có thể áp dụng cú pháp này cho các function ẩn danh. Khi chỉ còn các tham số trong dấu ()
và biểu thức sau dấu =>
.
(parameters) => expression;
Okay! I’m fine.
Ví dụ 1 cho các function ẩn danh
Bạn có function sau:
final multiply = (int a, int b) { return a * b; };
Chuyển đổi lại như sau:
final multiply = (int a, int b) => a * b;
Sử dụng thì như ri:
print(multiply(2, 3)); // 6
Ví dụ 2 cho các function trả về function
Ta lại có một ví dụ tiếp theo như sau:
Function applyMultiplier(num multiplier) { return (num value) { return value * multiplier; }; }
Đây là function trả về function và sử dụng một function ẩn danh để làm giá trị trả về. Ta cũng có thể viết lại như sau:
Function applyMultiplier(num multiplier) { return (num value) => value * multiplier; }
Thấy gọn gàng hơn nhiều khi so với cách viết các functions lồng nhau bên trong thân hàm.
Ví dụ với các ForEach
Ví dụ, bạn có 1 array số nguyên và nhân mỗi phần tử với 4.
numbers.forEach((number) { final tripled = number * 4; print(tripled); });
Ta sẽ viết lại cho no xịn sò hơn nhiều, thì trông như ri nè!
numbers.forEach((number) => print(number * 4));
Như vậy là bạn đã xong cơ bản về functions trong Dart rồi đó. Mệt não quá!
Tạm kết
- Tìm hiểu về functions trong Dart
- Cấu tạo của một function
- Các vấn đề liên quan tới các tham số của hàm
- Các hàm ẩn danh trong Dart
- Tìm hiểu về các Arrow functions & các ví dụ sử dụng chúng
Okay! Tới đây, mình xin kết thúc bài viết về Functions trong series Dart Tour. Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)