函数编程|函数编程 Functional Programming

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 【函数编程|函数编程 Functional Programming】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
  1. A pure function is a function that returns a value that is computed based on its arguments.
  2. pure functions take at least one argument and always return a value or another function
  3. 不设置全局变量,也不改变任何应用状态,把参数当作immutable data
  4. 纯函数是可测试的(testable)
  5. 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}

纯函数原则:

  1. 函数至少接受一个参数
  2. 函数应当返回一个值或者另一个函数
  3. 函数不能改变任何参数的状态
3.数据变形(Data Transformations) Functional programming is all about transforming data from one form to another.

我们通过函数产生变形复制品,这些函数使代码更少的命令性,降低复杂度
  1. array.map() & array.reduce() 核心函数
  2. array.join() 数组--> 字符串 原数组不改变
  3. 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();

    推荐阅读