# ปูพื้น Dart ให้พอเขียน Flutter ตอนที่ 2
#Dev#Dart#Flutterบทความตอนนี้จะกล่าวถึงฟังก์ชันและ Operator ในภาษา Dart
ซึ่งผมจะเอาข้อมูลโดยส่วนใหญ่มาจาก Dart Tour (opens new window) แล้วตัดส่วนเฉพาะเท่าที่จำเป็นมาครับ
คลิกที่นี่สำหรับ ตอนที่ 1
Tips
สามารถใช้ Dart Pad (opens new window) ในการทดลองเขียนได้ เพื่อเพิ่มความเข้าใจ
![Header](/assets/img/00.90444c13.jpg)
# Functions
Dart เป็นภาษาเป็นเป็น OOP (Object-Oriented Programming) อย่างแท้จริง เพราะงั้นฟังก์ชันเองก็เป็น Object อันหนึ่ง และยังมีชนิดเป็นฟังก์ชัน
นั่นหมายความว่าเราสามารถกำหนดค่าฟังก์ชันใส่ตัวแปร หรือส่งผ่านเข้าไปในฟังก์ชันอื่นได้
โดยมีบาง Instance (เป็นสิ่งที่สร้างมาจาก Class) ที่สามารถเรียกเหมือนฟังก์ชันได้อีกด้วย
ตัวอย่างการประกาศฟังก์ชัน
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
bool
เป็นส่วนที่บอกว่าฟังก์ชันนี้คืนค่าออกมาเป็นชนิดอะไร ซึ่งในที่นี้เป็นค่า Boolean
isNoble
คือ ชื่อของฟังก์ชัน
int atomicNumber
คือ Parameter ที่ฟังก์ชันนี้รับ
return
เป็นส่วนที่บอกว่าฟังก์ชันจะคืนค่าอะไรเมื่อเรียกเสร็จ
Tips
เราสามารถไม่ใส่ bool หรือชนิดของค่าที่คืนได้
แต่การใส่ไว้ทำให้เราดูแล้วรู้ได้ทันทีว่าฟังก์ชันนี้จะคืนค่าอะไร
และ ถ้าภายในฟังก์ชันของเรามีการกระทำเพียงคำสั่งเดียว เราสามารถใช้รูปแบบที่สั้นกว่าได้ เช่น
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
โดย => คำสั่ง นั้นเป็นรูปแบบย่อของ { return คำสั่ง; }
ในบางครั้งเราจำเรียกรูปแบบ => ว่า Arrow Syntax
ฟังก์ชันจะรับ Parameter ได้ 2 แบบ คือ required (ต้องมี) หรือ optional (มีหรือไม่มีก็ได้) ซึ่ง required parameter นั้นจะขึ้นก่อนแล้วจึงตามด้วย optional parameter
# Optional Parameters
มีสองแบบให้เลือก named (กำหนดผ่านชื่อ) หรือ position (กำหนดผ่านตำแหน่ง)
# Named parameters
เมื่อเราเรียกใช้ฟังก์ชันเราสามารถส่ง Parameter ผ่านชื่อได้โดย ชื่อของParameter: ค่าที่จะส่ง เช่น
enableFlags(bold: true, hidden: false);
ซึ่งตอนที่เราประกาศฟังก์ชันนั้นเราจะต้องใส่ {bold, hidden, …} เพื่อกำหนดชื่อของ Parameter ที่เราจะรับ
void enableFlags({bool bold, bool hidden}) {...}
ถึงแม้ว่า Named Parameters จะเป็นแบบไม่บังคับก็ตามแต่ว่าเราสามารถใส่ @required
เพื่อบังคับให้ใส่ได้
import 'package:meta/meta.dart'; // ถ้าจะใช้ @required ต้อง import ตัวนี้ด้วย
void enableFlags({bool bold, bool hidden}) {...}
# Positional parameters
ถ้าต้องการกำหนด Positional parameters เราจะกำหนดโดยการใส่ Parameter ด้านใน [] เช่น
String say(String from, String msg, [String device]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
ตัวอย่างการเรียกฟังก์ชัน
say('Bob', 'Howdy') // เมื่อไม่กำหนด Parameter ตัวที่ 3 ค่าของ device จะเป็น null
// หรือ
say('Bob', 'Howdy', 'smoke signal')
# Default parameter values
เราสามารถใช้ = ในการกำหนดค่าเริ่มต้นได้หากผู้เรียกใช้ฟังก์ชันไม่ได้กำหนด Parameter มา
void enableFlags({bool bold = false, bool hidden = false}) {...}
enableFlags(bold: true); // bold จะเก็บค่า true ส่วน hidden จะเก็บค่า false
จากตัวอย่างข้างบนใช้ Default parameter กับแบบที่เป็น Named ซึ่งเราสามารถใช้กับ Positional ได้เหมือนกัน
String say(String from, String msg, [String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
say('Bob', 'Howdy') // device ถ้าไม่กำหนดค่าให้จะเป็น 'carrier pigeon' และ mood จะเป็น null
และถ้าอยากใส่ Default parameter List หรือ Map ก็ทำได้ แต่จะต้องใส่ const นำหน้า เช่น
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
# ฟังก์ชัน main()
ทุกๆแอปหรือโปรแกรมจะต้องมีฟังก์ชัน main() ซึ่งเป็นจุดเริ่มต้นของทุกๆอย่าง โดยฟังก์ชัน main() นั้นจะไม่คืนค่าอะไร และสามารถมี Optional Parameters ที่เป็น List<String> ได้หนึ่งตัว
void main() {
print('Hello, World!');
}
// เรียกผ่าน Command line เช่น dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
# การส่งผ่านฟังก์ชัน
เพราะว่าฟังก์ชันก็เป็น Object อันหนึ่งเราจึงสามารถส่งผ่านอีกฟังก์ชันได้
void printElement(int element) {
print(element);
}
var list = [1, 2, 3];
// ส่งฟังก์ชัน printElement ไปยังอีกฟังก์ชันหนึ่ง
list.forEach(printElement);
หรือจะกำหนดใส่ตัวแปรก็ยังได้
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
# Anonymous functions หรือฟังก์ชันนิรนาม
ฟังก์ชันโดยส่วนใหญ่จะมีชื่อทั้งนั้น เช่น main(), printElement() ถึงอย่างนั้นคุณก็สามารถสร้างฟังก์ชันที่ไม่มีชื่อได้เหมือนกัน
ซึ่งหน้าตาของฟังก์ชันนิรนามก็เหมือนกันฟังก์ชันที่มีชื่อ ต่างกันแค่ไม่มีชื่อให้เรียก โดยปกติแล้วเราจะกำหนดมันใส่ตัวแปร หรือส่งไปเป็น Parameter ของฟังก์ชันอื่น
([[Type] param1[, …]]) {
codeBlock;
};
ตัวอย่างด้านล่างจะเป็นการใช้ฟังก์ชันนิรนามส่งเข้าไปเป็น Parameter ของ .forEach โดยฟังก์ชันนี้รับ Parameter 1 ตัวชื่อ item
ฟังก์ชันนิรนามนี้จะถูกเรียกโดยส่งค่าสมาชิกที่อยู่ใน List ไปเป็นตัวแปรชื่อ item
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
print('${list.indexOf(item)}: $item');
});
ถ้าฟังก์ชันมีแค่การกระทำเดียวเหมือนด้านบนนี้ สามารถใช้ => ให้เขียนสั้นลงได้อีก
list.forEach((item) => print('${list.indexOf(item)}: $item'));
# Lexical scope หรือขอบเขตการทำงาน
ฟังก์ชันด้านในสามารถเข้าถึงตัวแปรที่อยู่ด้านนอกได้
bool topLevel = true;
void main() {
var insideMain = true;
void myFunction() {
var insideFunction = true;
void nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
# Return values
ฟังก์ชันทุกฟังก์ชันจะมีการคืนค่าทั้งหมด ถ้าไม่มีการกำหนด return ในฟังก์ชัน ฟังก์ชันจะคืนค่า null ออกมา
# Operators
มีมากมายหลายหลากโดยลำดับการทำงานจะเริ่มจากแถวที่อยู่สูงกว่า ถ้าลำดับเท่ากันจะเริ่มจากซ้ายไปขวา
Description | Operator |
---|---|
unary postfix | expr++ expr-- () [] . ?. |
unary prefix | -expr !expr ~expr ++expr --expr |
multiplicative | * / % ~/ |
additive | + - |
shift | << >> >>> |
bitwise AND | & |
bitwise XOR | ^ |
bitwise OR | | |
relational and type test | >= > <= < as is is! |
equality | == != |
logical AND | && |
logical OR | || |
if null | ?? |
conditional | expr1 ? expr2 : expr3 |
cascade | .. |
assignment | = *= /= += -= &= ^= etc. |
// ใส่วงเล็บเพื่อให้อ่านง่ายขึ้น
if ((n % i == 0) && (d % i == 0)) ...
// อ่านยากแต่ให้ผลที่เหมือนกัน
if (n % i == 0 && d % i == 0) ...
# Arithmetic operators
เป็น Operator ที่ใช้ในการคำนวณ
Operator | ความหมาย |
---|---|
+ | บวก |
- | ลบ |
-expr | เปลี่ยนค่าเป็นจำนวนลบ |
* | คูณ |
/ | หาร |
~/ | หาร (แต่ผลเป็น int เสมอ) |
% | หารเอาเศษ |
ตัวอย่าง เช่น
assert(2 + 3 == 5);
assert(2 - 3 == -1);
assert(2 * 3 == 6);
assert(5 / 2 == 2.5); // ผลลัพธ์เป็นชนิด double
assert(5 ~/ 2 == 2); // ผลลัพธ์เป็นชนิด int
assert(5 % 2 == 1); // ได้เศษจากการหาร
และ Dart ก็รองรับการใช้งาน ++ และ -- ด้วย ยกตัวอย่างเช่น
var a, b;
a = 0;
b = ++a; // บวกค่า a เพิ่มอีก 1 ก่อนที่จะกำหนดค่าให้กับ b
assert(a == b); // 1 == 1
a = 0;
b = a++; // กำหนดค่าของ a ให้กับ b ก่อนที่จะบวกค่าของ a เพิ่มอีก 1
assert(a != b); // 1 != 0
a = 0;
b = --a; // ลบค่า a ลงอีก 1 ก่อนที่จะกำหนดค่าให้กับ b
assert(a == b); // -1 == -1
a = 0;
b = a--; // กำหนดค่าของ a ให้กับ b ก่อนที่จะลบค่าของ a ลงอีก 1
assert(a != b); // -1 != 0
# Equality and relational operators
เป็น Operator ที่ใช้ในการเปรียบค่าระหว่างสองค่า โดยผลลัพธ์จะเป็น true หรือ false
Operator | ความหมาย |
---|---|
== | เท่ากัน |
!= | ไม่เท่ากัน |
> | มากกว่า |
< | น้อยกว่า |
>= | มากกว่าหรือเท่ากับ |
<= | น้อยกว่าหรือเท่ากับ |
ถ้าหากเราต้องการตรวจสอบว่า object หรือตัวแปรมีค่าเดียวกันหรือไม่ เราจะใช้ == ในการตรวจสอบ (ในกรณีที่เราอยากรู้ว่า object เป็นตัวเดียวกันแบบเป้ะๆเลยหรือไม่จะใช้ฟังก์ชัน identical() แทน)
assert(2 == 2);
assert(2 != 3);
assert(3 > 2);
assert(2 < 3);
assert(3 >= 3);
assert(2 <= 3);
# Type test operators
เป็น Operator ที่ใช้ในการตรวจสอบชนิดของตัวแปรหรือข้อมูล
Operator | ความหมาย |
---|---|
as | แปลงชนิดของข้อมูล |
is | เช็คว่าเป็นชนิดข้อมูลนั้นหรือไม่ |
is! | เช็คว่าไม่ใช่ชนิดข้อมูลนั้น |
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
จากตัวอย่างด้านบน อาจจะคล้ายๆกันแต่ไม่เหมือนกัน เราใช้ is ในการทดสอบก่อนแล้วจึงใช้งานตัวแปร และใช้ as ในการแปลงค่า emp ให้เป็นชนิด Person ก่อนแล้วจึงกำหนดค่า
คำเตือน
การใช้ as ตามด้านบนอาจจะทำให้เกิดปัญหาได้ถ้า emp ไม่สามารถแปลงเป็น Person ได้
# Assignment operators
เป็น Operator ที่ใช้ในการกำหนดค่าให้กับตัวแปร
a = 5;
b ??= 10; // ถ้า b เป็น null จะกำหนดค่า 10 ให้กับ b
เราสามารถเขียน Operator อื่นๆผสมกับ = ได้ เช่น
= | -= | /= | %= | >>= | ^= |
---|---|---|---|---|---|
+= | *= | ~/= | <<= | &= | |= |
หลักการก็คือ
a op= b
เหมือนกันกับ a = a op b
เช่น
a += b
เหมือนกันกับ a = a + b
var a = 2; // กำหนดค่าโดยใช้ =
a *= 3; // คูณและกำหนดค่าเหมือนกับเขียน a = a * 3
assert(a == 6);
# Logical operators
เป็น Operator ที่ใช้ในการกลับค่า หรือผสมค่า Boolean
Operator | ความหมาย |
---|---|
!expr | กลับค่าระหว่าง true หรือ false |
|| | เหมือน OR ในตรรกศาสตร์ |
&& | เหมือน AND ในตรรกศาสตร์ |
if (!done && (col == 0 || col == 3)) {
// ทำบางสิ่งบางอย่าง
}
# Conditional expressions
เป็น Operator ที่สามารถใช้แทน if-else ได้ในบางกรณี
เงื่อนไข ? ประโยค1 : ประโยค2 ถ้าเงื่อนไขเป็นจริงจะทำ ประโยค1 ไม่เช่นนั้นจะทำ ประโยค2 พร้อมทั้งคืนค่าของประโยคนั้นๆด้วย
ตัวแปร1 ?? ตัวแปร2 ถ้า ตัวแปร1 ไม่ใช่ null จะคืนค่าของ ตัวแปร1 ไม่เช่นนั้นจะคืนค่าของ ตัวแปร2
ถ้าต้องการกำหนดค่า โดยขึ้นอยู่กับเงื่อนไขบางอย่างเราจะใช้ ?:
var visibility = isPublic ? 'public' : 'private';
ถ้าต้องการตรวจสอบว่าตัวแปรเป็น null แล้วไปใช้อีกค่าแทน
String playerName(String name) => name ?? 'Guest';
โดยตัวอย่างด้านบนสามารถเขียนได้อีกแบบ
// เขียนแบบยาวกว่าโดยใช้ ?: operator
String playerName(String name) => name != null ? name : 'Guest';
// เขียนแบบยาวกว่ามากโดยใช้ if-else
String playerName(String name) {
if (name != null) {
return name;
} else {
return 'Guest';
}
}
# Cascade notation (..)
อันนี้ไม่ใช่ Operator แต่เป็นส่วนหนึ่งของ Syntax ในภาษา Dart ใช้ในการเข้าถึง object เดิมโดยไม่ต้องเขียน . ซ้ำๆ
querySelector('#confirm') // Get an object.
..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
ให้ผลเหมือนกับการเขียน
var button = querySelector('#confirm');
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((e) => window.alert('Confirmed!'));
# อื่นๆ
Operator | ความหมาย |
---|---|
() | แสดงถึงการเรียกใช้ฟังก์ชัน |
[] | ใช้ในการเข้าถึง List ในตำแหน่งที่กำหนด |
. | ใช้ในการเข้าถึง object เช่น foo.bar (แปลว่าเรียกหา bar ที่อยู่ใน foo) |
?. | เหมือนกับ . แต่มีการตรวจสอบว่า object เป็น null ก่อนเรียกใช้หรือไม่ |
Part หน้าเราจะมาเริ่มกันในเรื่องการเขียน if-else, loop, หรือ switch-case และการรับมือกับ error ทีเกิดขึ้นในภาษา Dart กันครับ 😁
# แบบฝึกหัด
var a = 0, b = 0;
void someFunc(newValue1, [newValue2 = 1]) {
var b;
a = newValue1;
b = newValue2;
}
someFunc(10);
print('a = $a, b = $b');
var answer = (3 + 10 * 3) / 11;
print(answer);
var a = 1;
var answer = a >= 1 && a < 10
print(answer);
var answer = true || false && false;
print(answer);
var a = false;
a = a ?? true;
var b = a ? 1: 2;
print('a = $a, b = $b');