function as first citizen
1- More than one arrow means that we have a higher order function;
2- functions are first citizens, this means that functions are data. they can
be saved, retrived, or flow through your applications just like variables.
3- atom
中报告使用"esversion: 6"错误,请在全局作用域下添加 .jshintrc
文件
```
{
esversion: 6
}
```
4- the core concepts of functional programming: immutability, purity, data abstraction, higher-order functions, recursion and composition
- 在函数编程中, data is immutable, it never changes; 不改变原始数据,而改变它的复制品
1.Immutable
1- Object.assign()
is the copy machine
let color_lawn = {
title: "lawn",
color: "#ff00ff",
rating: 0
};
const colorRate = function(color, rating) {
return Object.assign({}, color, {rating: rating});
};
colorRate(color_lawn, 5);
console.log(color_lawn.rating); // 5
2- concat 少使用push等能改变数组大小的方法
let list = [
{title: "red"},
{title: "blue"},
{title: "yellow"}
];
const addColor = (title, array) => array.concat({title:title});
// 或者直接写为
// const addColor = (title, array) => array.concat({title});
addColor("purple", list);
addColor.length; // 4
list.length; // 3 原数组没有变化
2.Pure Functions
A pure function is a function that returns a value that is computed based on its arguments.
pure functions take at least one argument and always return a value or another function
不设置全局变量,也不改变任何应用状态,把参数当作immutable data
- 纯函数是可测试的(testable)
- In React, UI is expressed with pure functions
// 不纯DOM操作
function Header(text) {
let h1 = document.createElement("h1");
h1.innerText = text;
document.boby.appendChild(h1);
}
// 纯函数
const Header = (props) => {props.title}
纯函数原则:
- 函数至少接受一个参数
- 函数应当返回一个值或者另一个函数
- 函数不能改变任何参数的状态
3.数据变形(Data Transformations)
Functional programming is all about transforming data from one form to another
.
我们通过函数产生变形复制品,这些函数使代码更少的命令性,降低复杂度
- array.map() & array.reduce() 核心函数
- array.join() 数组--> 字符串 原数组不改变
- array.filter() 返回一个新数组, 当要移除某个item时,应该使用此方程,避免使用array.pop(),array.splice()
1.filter()
// filter()
const schools = [
"Yorktown",
"Washington & Lee",
"Wakefield"
];
const cutSchool = (cut, list) =>
list.filter(school => school !== cut);
// 移除 "Yorktown"
cutSchool("Yorktown", schools); // 返回一个新数组
2.map()
将对象数组中的name更改
// map()
let schools = [
{name: "Yorktown"},
{name: "Stratford"},
{name: "Wakefield"}
];
// 将其中一个学校的名字改掉
const editName = (oldName, name, arr) =>
arr.map(item => {
if (item.name === oldName) {
return {
...item, // 使用ES7新特性,spread operator对对象进行操作
name
}
} else {
return item
}
});
editName("Wakefield", "New Wakefield", schools);
// 或者写为
const editName = (oldName, name, arr) =>
arr.map(item => (item.name === oldName) ?
({...item, name}) :
item
)
// 编译为
"use strict";
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var schools = [{ name: "A" }, { name: "B" }, { name: "C" }];
var editName = function editName(oldName, name, arr) {
return arr.map(function (item) {
return oldName === item.name ? _extends({}, item, { name: name }) : item;
});
};
将一个对象变为对象 Object.keys()
const schools = {
"Yorktown": 10,
"Washington & Lee": 2,
"Wakefield": 5
};
const schoolArray = Object.keys(schools).map(key =>
({
name: key,
wins: schools[key]
})
);
3.reduce() & reduceRight()
Can be used transform an array into any value, any value means a number, string, boolean, object, or even object
reduceRight 和 reduce 的区别在于这个函数是从数组的尾部开始操作,而不是从头到尾
两个参数:
- callback function 这个回调函数有2个参数:previous, current
- an orginal value, previous的初始值
1.将数组转变为一个numeric类型
const ages = [12, 14, 89, 67, 24];
// 获取最大值
const max = ages.reduce(
(previous, current) => (previous > current) ? previous : current,
0
); // 89
// 当然也可以采用Math.max
var max = Math.max(...ages); // 89
// 或者
var max = Math.max.apply(null, ages);
2.将数组 --> 对象
const colors = [
{
id: "-xeket",
title: "rad red",
rating: 3
},
{
id: "-qwer",
title: "rad blue",
rating: 4
},
{
id: "-asdf",
title: "rad yellow",
rating: 1
}
];
// 利用解构参数
const hashColors = colors.reduce(
(hash, {id, title, rating}) => {
hash[id] = {title, rating};
return hash;
}, {}); // {}参数为hash的初始值
// 相当于
var hashColors = colors.reduce(function (hash, _ref) {
var id = _ref.id;
var title = _ref.title;
var rating = _ref.rating;
hash[id] = { title: title, rating: rating };
return hash;
}, {});
hashColors;
{
-xeket: {
title: "rad red",
rating: 3
},
-qwer: {
title: "rad blue",
rating: 4
},
-asdf: {
title: "rad yellow",
rating: 1
}
}
3.array --> array
数组去重
使用reduce
var colors = ["red", "blue", "blue", "yellow", "yellow"];
var distinctColors = colors.reduce(
(distinct, color) => {
(distinct.indexOf(color) !== -1) ?
distinct : [...distinct, color]
}, []); // ["red", "blue", "yellow"]
当然也可以使用Set集合去重
var colorSet = new Set(colors);
var distinctColors = [...colorSet];
// 或者
var distinctColors = Array.from(colorSet);
4.Higher Order functions
即function当作参数或者返回值
1.Currying functions
Currying is a functional technique that involves the use of higher order functions
2.Recursion
递归调用
const countdown = (value, fn) => {
fn(value);
return (value > 0) ? countdown(value - 1, fn) : value
}
countdown(10, value => console.log(value));
设置一个倒计时
const countdown = (value, fn, delay = 1000) => {
fn(value);
return (value > 0) ? setTimeout(() => countdown(value - 1, fn), delay) : value
}
countdown(10, value => console.log(value));
递归取回嵌套对象中的属性值
下面例子也可以考虑直接使用对象解构
let dan = {
type: "person",
data: {
gender: "male",
info: {
id: 22,
fullname: {
first: "Dan",
last: "Deacon"
}
}
}
};
// 递归调用
const deapPick = (first, object = {}) => {
const [first, ...remaining] = first.split(".");
return (remaining.length > 0) ?
deapPick(remaining.join("."), object[first]) :
obejct[first]
};
deapPick("type", dan); // "person"
deapPick("data.info.fullname.first", dan); // "Dan"
5.Compose
显示时间
const compose = (...fns) =>
(arg) =>
fns.reduce(
(composed, f) => f(composed),
arg
)
const oneSecond = () => 1000;
const getCurrentTime = () => new Date();
const clear = () => console.clear();
const log = message => console.log(message);
// serializeClockTime
const serializeClockTime = date =>
({
hours: date.getHours(),
minutes: date.getMinutes(),
seconds: date.getSeconds()
});
const civilianHours = clockTime =>
({
...clockTime,
hours: (clockTime.hours > 12) ?
clockTime.hours - 12 :
clockTime.hours
})
const appendAMPM = clockTime =>
({
...clockTime,
ampm: (clockTime.hours >= 12) ? "PM" : "AM"
})
const display = target => time => target(time) ;
const formatClock = format =>
time =>
format.replace("hh", time.hours)
.replace("mm", time.minutes)
.replace("ss", time.seconds)
.replace("tt", time.ampm)
const prependZero = key => clockTime =>
({
...clockTime,
[key]: (clockTime[key] < 10) ?
"0" + clockTime[key] :
clockTime[key]
})
const convertToCivilianTime = clockTime =>
compose(
appendAMPM,
civilianHours
)(clockTime)
const doubleDigits = civilianTime =>
compose(
prependZero("hours"),
prependZero("minutes"),
prependZero("seconds")
)(civilianTime)
const startTicking = () =>
setInterval(
compose(
clear,
getCurrentTime,
serializeClockTime,
convertToCivilianTime,
doubleDigits,
formatClock("hh:mm:ss:tt"),
display(log)
),
oneSecond()
)
startTicking();