1.命令式:一个函数中会把具体细节展现出来
2.声明式:把具体细节隐藏,只描述了做了什么
函数式的风格中的核心概念:不可变性、纯函数、数据转换、高阶函数、递归
不可变性:
let list = [{title:'Rad red'},{title:'Lawn}]; const addColor1 = (title,color)=>{ list.push({title,color}) return Array } const addColor2 =(title,color)=>[...list,{title,color}] addColor2保持了数据的不变性,使外部list没有被修改View Code
纯函数:基于参数做计算,并返回一个值的函数
纯函数至少接受一个参数,而且始终返回一个值或者另一个函数。这种函数没有副作用,不会设置全局变量,也不更改应用的状态。纯函数把数据视为不可变数据。
const frederick = { name:'React学习手册', canRead:false, canWrite:false } const foo1=() ={ frederick.canRead = true; rederick.canWrite = true; } const foo2 =(book)=> { book.canRead = true; book.canWrite = true; return book } 此时,foo1有副作用:没有参数,没有返回值,更改了全局变量;foo2则是一个纯函数,没有更改外部变量View Code
数据转换:如果数据都是不可变的,信息再一个应用中如何流动呢?再函数式编程中,数据从一种格式编程另一种格式。我们要做的就是使用转换后的副本。
map
map const schools= { 学校1:1, 学校2:2, 学校3:3 } const transformSchoolObjToschoolList = (school)=>Object.keys(schools).map( schoolName=>({schoolName:school[schoolName]})) // 将学校对象改成学校数组 const editName = (oldName,name,arr)=> arr.map(itme=>(item.name===oldName ? {...itme.name}:item) // 更新学校名称View Code
reduce
reduce函数可以把数组转换成任何值,包括数字、字符串、布尔值、对象、甚至是函数。 const ages = [11, 43, 52, 72, 31, 23, 42, 62, 21, 13]; const maxValue= ages.reduce( (initValue, age) => (age > initValue ? age : initValue), 0 ); console.log("maxValue", maxValue); //72 找出最大值 const obj1 = colors.reduce((initValue, color) => { initValue[color["id"]] = { ...color }; return initValue; }, {}); console.log(obj1); // 数组对象根据id转换为对象 const obj2 = colors.reduce((initValue,{id , title, rating}) => { initValue['id'] = { title,rating}; return initValue; }, {}); console.log(obj1); // 数组对象根据id转换为对象-简便写法 const uniqList = colorList.reduce( (list, color) => (!list?.includes(color) ? [...list, color] : list), [] ); console.log("uniqList: ", uniqList);//不重复数组View Code
高阶函数:用于处理其它函数为参数的函数。,其参数可以是函数,也可以返回函数,或者两者同时有。
Array.map/Array.filter/Array.reduce都是高阶函数
递归:创建着重调用自身的函数。
//倒计时 const countDown = (value, fn) => { fn(value); return value > 0 ? countDown(value - 1, fn) : value; }; countDown(10, (value) => console.log(value)); const countDown1 = (value, fn, delay = 1000) => { return value > 0 ? setTimeout(() => { fn(value); countDown1(value - 1, fn); }, delay) : value; }; countDown1(10, (value) => console.log(value)); // 从对象里根据路径取某个属性 const Dan = { type: "person", data: { gender: "male", info: { id: 22, fullname: { first: "Dan", last: "Deacon", }, }, }, }; const deepPick1 = (fields, object = {}) => { const [first, ...reaming] = fields.split("."); return reaming.length ? deepPick(reaming.join("."), object[first]) : object[first]; }; // console.log("102-", deepPick1("data.info.fullname.first", Dan)); // 对象指定属性填加一个值 const deepPick2 = (fields, object = {}) => { const [first, ...reaming] = fields.split("."); return reaming.length ? { ...object, [first]: deepPick2(reaming.join("."), object[first]), } : { ...object, [first]: object[first], name: "19999" }; }; // console.log("102-", deepPick2("data.info.fullname.first", Dan)); // 对对象里的某些属性进行加工,比如在嵌套的时候对整个对象里的日期进行格式化有用 const getObjByWrapFieldsList2 = (list, object) => list?.reduce((obj, item) => { if (item in object) { obj[item] = object[item] + "我被修改了"; } return obj; }, {}); const deepPick4 = (fields, object = {}, wrapFields) => { const [first, ...reaming] = fields.split("."); const obj = getObjByWrapFieldsList(wrapFields, object); return reaming.length ? { ...object, [first]: deepPick4(reaming.join("."), object[first], wrapFields), ...obj, } : { ...object, ...obj }; }; console.log( "102-", deepPick4("data.info.fullname.first", Dan, ["type", "id"]) );View Code
compose:把函数组合在一起
const compose = (...fns)=> args=> fns.reduce((compose,fn)=>fn(compose),args) // 这个里面reduce的初始值是参数,后面每次返回一个传入参数的函数的运算结果View Code