函数编程 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

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();

你可能感兴趣的:(函数编程 Functional Programming)