JavaScript ES6 (ES2015)
Arrow function
// ES6
[1, 2, 3].map(num => num * 2);
// ES5
[1, 2, 3].map(function(num) {
return num * 2;
});
// ES6
app.get('/', (req, res) => {
res.json({
message: 'Hello World'
});
});
// ES5
app.get('/', function(req, res) {
res.json({
message: 'Hello World'
});
});
() => ‘Hello’
() => {} (แบบ code block)
(num) => num * 2
(num1, num2) => {return num1 + num2;} (แบบ code block)
() => ({ foo: ‘bar’ }) (return เป็น Object ให้ใช้ปีกกา)
ลองเล่น
const f = x => x * 2
print(f(2));
//===========================
var materials = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];
//จัดให้ดูง่ายสำหรับผม
const a = materials.map(
function(material) {
return material.length;
}
);
//เปลื่อน function เป็น => ไว้ด้านหลังแทน
const b = materials.map(
(material) => {
return material.length;
}
);
// ลบ () ของ parameter ออก
รวมถึง {},return ของ Function
materials.map(material => material.length);
//output [8, 6, 7, 9]
//====================================
var simple = a => a > 15 ? 15 : a;
print(simple(16));
print(simple(10));
// 15 10
const a = (a,b=10) => a*b
print(a(2));
// 20
// ต้องใส่ () ด้วย ถ้าจะ Default
const a = (a=5) => a
print(a());
nested arrow functions
let add = (x,y) => x + y;
add(2,3); //=> 5
let add = x => y => x + y;
add(2)(3);
let add = function (x) {
return function (y) {
return x + y;
};
};
What is Functional Programming
แบบธรรมดา
let numbers = [1, 2, 3, 4, 5, 6 ,7, 8, 9, 10];
let squaredEvens = [];
for(let i = 0; i < numbers.length; i++) {
let currentNumber = numbers[i];
if(currentNumber % 2 === 0) {
squaredEvens.push(currentNumber * currentNumber)
}
}
แบบ FP
const numbers = [1, 2, 3, 4, 5, 6 ,7, 8, 9, 10];
const isEven = n => n % 2 === 0;
const square = n => n * n;
numbers.filter(isEven).map(square);
Note
filter รับ parameter เป็น Function ที่โยนไปค่าจาก array ไปเชคทีละตัว
Same
function isEven(n) { return n % 2 === 0; }
function square(n) { return n * n; }
หัวใจสำคัญของ Functional Programming
หลีกเลี่ยง side-effect หรือผลข้างเคียงที่จะเกิดต่อ function อื่นและต่อตัวเอง
เมื่อมี input ค่าหนึ่ง จะได้ต้องได้ output เท่าเดิมเสมอ
ไม่ไปเปลี่ยนแปลงค่าของตัวแปรจำพวก global variable หรือ static variable
## First-class function & Higher-order function
First-class function คือ feature ของ programming language ที่อนุญาตให้ function นั้นเป็น first-class citizen ประชากรอันดับหนึ่งของภาษา เทียบเท่ากับ value อื่น ๆ อย่างตัวเลข หรือ string เป็นต้น ซึ่งเราสามารถ assign function ให้กับตัวแปรได้ เป็น argument หรือ return value ของ function อื่นๆก็ได้
function ในลักษณะที่รับ argument เป็น function หรือ return function ออกมามีชื่อเรียกว่า Higher-order function
## Lambda expression & Closure
-
ตัวแปร (ในเชิงคณิตศาสตร์ ซึ่งเป็นตัวแทนของค่า หรือ argument ของ function ไม่ใช่ mutable variable หรือตัวแปรแบบที่เรา update ค่าได้เรื่อยๆเวลาเขียนโปรแกรมแบบ imperative)
- function ซึ่งใน Lambda calculus นั้น function ไม่มีชื่อ
- application ([function][argument]) ถ้าเปรียบเทียบกับพีชคณิตก็คล้ายๆกับแทนค่า parameter ใน function เช่น ƒ(3), g(21)
const foo = [1,2,3];
const baz = foo.map(function bar (n) { return n + 1; });
ส่วน Closure เป็นสิ่งที่ผมสงสัยอยู่นานมาก เพราะมักจะเจอตัวอย่างแบบนี้
function ticker() {
let count = 0;
return () => count++;
}
let t = ticker();
t(); // 0
t(); // 1
t(); // 2
t(); // 3
เขาบอกว่า Closures คือ functions ที่อ้างอิง free variables เจ้า free variables เนี่ย มันคือตัวแปรที่ไม่ได้ถูกประกาศไว้ใน parameter จากตัวอย่างด้านบน จะเห็นได้ว่า () => count++ ไม่มี parameter เลย แต่มีตัวแปร count ซึ่งโดยปกติแล้วเมื่อ execute ticker เสร็จ เจ้า count ควรจะถูก garbage collector เก็บไป แต่ในกรณีนี้ เมื่อเราบอกว่า let t = ticker(); แล้ว t จะกลายเป็น closure ซึ่งเป็น function ที่จำสภาพแวดล้อมที่สร้างมันขึ้นมา ทำให้ผลลัพธ์เป็นอย่างที่เห็น แต่ไม่เป็น FP
// Original
function makeAdder(a) {
return b => a + b;
}
const addFive = makeAdder(5);
const addTen = makeAdder(10);
addFive(20); // 5 + 20 => 25
addTen(9); // 10 + 9 => 19
// Refactoring
const makeAdder2 = a => b => a + b;
const addfuck = makeAdder2(5)
addfuck(20)
คราวนี้จะเห็นได้ว่าทั้ง makeAdder, addFive และ addTen ล้วนเป็น pure function หรือตัวอย่างที่ดูฉลาดขึ้นหน่อย
const makeSum = transFunc => (a, b) =>
transFunc(a) + transFunc(b);
const sumSquared = makeSum(n => n * n);
const sumCubed = makeSum(n => n * n * n);
sumSquared(1, 2); // 1^2 + 2^2 => 5
sumCubed(1, 2); // 1^3 + 2^3 => 9
## รู้จักกับ Javascript Callback Function
### แบบธรรมดา (sync )
console.log('Step 1');
funcSync();
console.log('Step 3');
function funcSync () {
console.log('Step 2');
}
### แบบไม่ธรรมดา (async )
setTimeout(function(){
console.log('Step 1')
}, 3000);
console.log('Step 2')
// ผลลัพท์
// Step 2
// Step 1
setTimeout(doSomeThing, 3000);
function doSomeThing () {
console.log('Hello')
}
ฟังชั่น setTimeout จะมีการรับ parameter อยู่ 2 ตัวก็คือ
Function ที่จะให้ทำงานเมื่อถึงเวลาที่กำหนด(หรือก็คือ Callback Function นั่นเอง)
Integer ที่ใช้กำหนดเวลา มีหน่วยเป็น ms
const myAsyncFunc = callback => callback();
myAsyncFunc(callbackFunc);
function callbackFunc () {
print('this is callback function')
}
มันจะทำ function ที่ใส่เข้าไปให้เสร็จก่อนแล้วค่อยออกมา
ลองเล่น
Example 1
// Original
asyncFunc(
function (str)
{
print('cb1 : ' + str);
},
function (str)
{
print('cb2 : ' + str);
}
);
function asyncFunc (cb1, cb2) {
cb1('A');
cb2('B');
}
// Refactoring
let asyncFunc = (cb1,cb2) => {
cb1('A');
cb2('B');
}
const f1 = str => print('cb1 : ' + str);
const f2 = str => print('cb2 : ' + str);
asyncFunc(f1,f2);
Example 2
// Original
asyncFunc(cb, cb);
function asyncFunc (cb1, cb2) {
cb1('A');
cb2('B');
}
function cb (str) {
print('cb : ' + str);
}
// Refactoring
const cb = str => print('cb : ' + str)
let asyncFunc = (cb1,cb2) => {
cb("A");
cb("B");
}
asyncFunc(cb,cb);
Example 3
// Original
asyncFunc(cb, cb);
function asyncFunc (running, done) {
for (var i = 0;i<10;i++) {
running('i = ' + i);
}
done('done');
}
function cb (str) {
print(str);
}
// Refactoring
let cb = str => print(str);
let asyncFunc = (running, done) =>{
for (var i = 0;i<10;i++) {
running('i = ' + i);
}
done('done');
}
asyncFunc(cb, cb);
จัดการ Callback
Promise
เป็นเหมือนกับคำสัญญาว่าจะทำคำสั่งนี้ให้เรานะ โดยมันจะมีอยู่ 3 สถานะก็คือ pending, resolved, rejected เมื่อเริ่มต้นทำคำสั่ง promise จะมีสถานะเป็น pending ถ้าทำเสร็จแล้วจะมีสถานะเป็น resolved โดยจะทำคำสั่งถัดไปที่อยู่ใน .then() และถ้าทำคำสั่งไม่สำเร็จ จะมีสถานะเป็น rejected และจะไม่ทำคำสั่งถัดไป แต่จะทำคำสั่งที่อยู่ใน .catch() แทน
var p = new Promise(function(resolve, reject) {
let a = 5;
if(a >= 5) {
resolve(a);
}else {
reject(a);
}
}).then(function(a) {
console.log(`result then : ${a}`);
return a + 2;
}).then(function(b) {
console.log(`result then : ${b}`);
}).catch(function(a) {
console.log(`result catch : ${a}`);
});
Note
การส่ง return จะไม่ออกมาข้างนอกนะ จะเข้า parameter then ถัดไป
async/await
const resolveAfter2Seconds = x => {
return new Promise(
resolve => {
setTimeout(() => {
resolve(x);
}, 2000);
}
);
}
console.log(resolveAfter2Seconds(50));
async function add1(x) {
const a = await resolveAfter2Seconds(20);
const b = await resolveAfter2Seconds(30);
return x + a + b;
}
console.log(add1(100));
add1(10).then(v => {
console.log(v); // prints 60 after 4 seconds.
});
async function add2(x) {
const p_a = resolveAfter2Seconds(20);
const p_b = resolveAfter2Seconds(30);
return x + await p_a + await p_b;
}
add2(10).then(v => {
console.log(v); // prints 60 after 2 seconds.
});
Function Composition
คือกระบวนการรวมกันของ functions มากกว่า 1 ขึ้นไป และก่อให้เกิด function ใหม่ขึ้นมา
หรือรูปแบบหนึ่งที่ใช้ในการอธิบายที่ง่ายที่สุดก็คือ f(g(x)) หรือการรวมกันของ function f และ g มากระทำกับ x
const compose = function(f, g) {
return function(x) {
return f(g(x))
}
}
const compose = (f, g) => x => f(g(x))
apply functions ไล่ไปจาก ขวา ไป ซ้าย
ตัวอย่าง
จินตนาการว่า เราจะสร้าง function ทำความสะอาด String ก่อนนำไปใช้งาน (sanitize function)
ขั้นตอนการทำงาน
ตัดช่องว่างที่ไม่จำเป็นหน้าหลังของคำ (trim)
แปลงคำทั้งหมดเป็นตัวพิมพ์เล็ก (trim)
- ตัดช่องว่างที่ไม่จำเป็นหน้าหลังของคำ (trim)
- แปลงคำทั้งหมดเป็นตัวพิมพ์เล็ก (trim)
function sanitize(str) {
return str.trim().toLowerCase()
}
sanitize(' Hello My Name is Ham ') // 'hello my name is ham'
ทีนี้ เราลองเขียนแบบ FP กันดู
const trim = s => s.trim()
const toLowerCase = s => s.toLowerCase()
function sanitize(str) {
return toLowerCase(trim(str))
}
sanitize(' Hello My Name is Ham ') // 'hello my name is ham'
const compose = (f, g) => x => f(g(x))
const sanitize = compose(toLowerCase, trim)
sanitize(' Hello My Name is Ham ') // 'hello my name is ham'
const trim = s => s.trim()
const toLowerCase = s => s.toLowerCase()
const join = separator => arr => arr.join(separator)
const split = separator => arr => arr.split(separator)
const toSlug = compose(
toLowerCase,
join('-'),
split(' '),
trim,
)
toSlug(' THIS is SluG ') // 'this-is-slug'
Pipe
ลักษณะการทำงานของ pipe คือ การส่งต่อ ผลลัพธ์ ที่ได้จากการ คำสั่งก่อนหน้า ไปให้แก่ คำสั่งด้านหลัง
- compose: apply function จาก ขวา ไป ซ้าย
- pipe: apply function จาก ซ้าย ไป ขวา
const toSlug = pipe(
trim,
split(' '),
join('-'),
toLowerCase,
)
toSlug(' THIS is SluG ') // 'this-is-slug'
https://github.com/NantipatSoftEn/FunctionalPrograming/blob/master/Higher-Order/README.md
Top comments (2)
^_^ เขียนบันทึกไว้ครับ
ไม่ได้นึกว่าจะมีใครมาอ่าน