共计 3487 个字符,预计需要花费 9 分钟才能阅读完成。
最近在学习大前端的进阶,因而想把课程中学到的一些知识点联合这几年的工作所感记录下来。
丑陋的程序千千万,乏味的思维各不同
何为函数式编程
函数式编程是一种思维,能够和面向对象编程和面向过程编程并列。
- 面向过程,以过程为核心的编程思维,剖析出解决问题所须要的步骤, 而后用函数把这些步骤一步一步实现, 应用的时候一个一个顺次调用。
- 面向对象,以对象为核心的编程思维,艰深的讲,就是把事实世界中的某个或者一组事物形象成为一个对象,通过属性和办法形容其应该具备能力,利用继承,多态等办法形容其变动。(集体感觉,面向过程编程的关键点是如何找到事物的相同点,并依照肯定的规定将其设计为对象。)
- 函数式编程,顾名思义,以函数为核心的编程思维,然而须要留神的是,此函数非咱们惯例意义上写代码时写的函数,更趋势数学上的函数,即 x => y 的推导过程 f(x), 当 f(x)固定后,肯定有一个可推导且固定的 y 值与 x 值绝对应。
函数式编程益处
函数式编程蕴含以下益处:
- 超级不便的代码复用(个人感觉当初公司中的局部前端开发将复制粘贴也当作了代码复用的一种,当和他们提出既然多个页面都用到,为什么不把这个解决逻辑提出来为一个专用的办法呢,失去的答复是粘贴一下就好了,为什么要提出来?额,其实逻辑的使用者不须要关怀你外部逻辑是怎么实现的,只须要能保障我输出一组变量,失去我想要的后果就行了)。
- 无 this(vue2.0 对 ts 反对不是很敌对就倒在了这个 this 上,vue3.0 就提出了 Composition API 解决代码复用和 ts 反对)。
- 不便 treeshaking(指的是代码打包过程中,通过剖析,能够将无用代码剔除,只保留有用代码,从而缩小打包体积,优化加载性能)。
- 不便测试(个人感觉在编写前端单元测试用例的时候,如果某段逻辑对外的依赖越强,那么测试用例越不好写,因而在开发的时候通过正当的拆分逻辑可能不便编写测试用例。那么,测试用例是否不便编写是不是掂量逻辑单元是否正当的标记呢?)。
函数式编程个性
函数是一等公民
所谓一等公民指的是函数与其余数据类型一样, 处于平等位置, 能够赋值给其余变量, 也能够作为参数, 传入另一个函数, 或者作为别的函数的返回值。(一等公民 = 啥都能够?)
# 变量值
let handler = function () {}
# 参数
let forEach = function (array, handleFn) {}
# 返回值
let handler = function () {return function () {}}
# 实例化
let handler = new Function()
高阶函数
高阶函数指的是能够传递一个函数作为参数的函数,艰深的讲,高阶函数也是一个函数,只不过它的参数中有一个是函数。
高阶函数的终极益处是:屏蔽实现细节,关注具体指标。
上文中的 forEach 就是一个高阶函数,屏蔽实现细节指的是使用者不必关怀函数外部是如何对数组进行遍历,如何获取数组中的每个元素。关注具体指标指的是,使用者只关系在获取到数组中的每个元素后须要做什么操作。
闭包
函数和对其四周状态(lexical environment,词法环境 )的援用捆绑在一起形成 闭包(closure)。也就是说,闭包能够让你从外部函数拜访内部函数作用域。在 JavaScript 中,每当函数被创立,就会在函数生成时生成闭包。
- 闭包是随同 js 函数产生的。
- 闭包是在函数作用域内引入非其作用域内的内部状态。
以 once 函数展现根本的闭包
function once(fn) {
let done = false
return function() {
// 在函数外部作用域内引入内部作用域的 done 状态,使得 done 不会随着 once 的执行结束被销毁,缩短其作用工夫
if(!done) {
done = true
fn.apply(fn, arguments)
}
}
}
闭包的实质:函数执行结束之后会被执行栈开释,然而因为其作用域中的状态被内部援用,所以援用的状态不能被开释,还能够被应用。
纯函数
前提:函数必须有输入输出。
要求:雷同的输出永远会失去雷同的输入(输入输出的数据流是显式的),没有可察看的副作用。
副作用是指当调用时,除了返回值之外,还对主调用产生附加的影响。副作用的不仅仅只是返回了一个值,而且还做了其余的事件。艰深的讲就是函数依赖了内部状态或者批改了内部状态。函数式编程要求函数无副作用,然而副作用是无奈齐全打消,只能将其管制在可控的范畴内。
为什么会有纯函数(纯函数有哪些益处)?
- 因为输入输出能够互相对应,因而能够针对纯函数的计算结果做缓存。
function momerize(fn) {let cache = {}
return function(...args) {let key = JSON.stringify(args)
cache[key] = cache[key] || fn.apply(fn, args)
return cache[key]
}
}
- 因为纯函数没有副作用,所以不便测试。
- 因为纯函数没有副作用,所以能够在多线程中调用,能够并行处理。
js 尽管是单线程的,然而最新的规范中增加了 WebWork API,反对异步操作。
# 创建者
let worker = new Worker('test.js')
// 向执行者传递数据
worker.postMessage(1)
worker.onmessage = function(evt) {
// 执行者返回的数据
console.log(evt.data)
}
# 执行者 test.js
onmessage = function(evt) {postMessage(evt.data + 1)
}
## 感觉和 electron 中主窗口和其余窗口之间通信很类似
柯里化
柯里化(Currying)是把承受多个参数的函数变换成承受一个繁多参数 (最后函数的第一个参数) 的函数,并且返回承受余下的参数且返回后果的新函数的技术。
柯里化是对纯函数的封装,将一个多元(蕴含多个参数)纯函数变为可间断调用的多元或一元函数。也能够了解为,通过柯里化,能够将函数细粒化,达到最大限度的代码重用。
// 简略的柯里化函数
function curry(fn) {return function curriedFn(...args) {
// 如果传入的实参个数和 fn 的形参个数不一样,那么返回一个函数
// 调用 fn.length 能够获取形参个数
if (args.length < fn.length) {return function () {return curriedFn(...args.concat(Array.from(arguments)))
}
}
// 如果雷同,则调用 fn 返回后果
return fn.apply(fn, args)
}
}
// 多元函数
function sum(a, b, c) {return a + b + c}
// 失常调用
console.log(sum(1, 2, 3))
let curried = curry(sum)
// 柯里化调用
console.log(curried(1)(2)(3))
函数组合
事实编程的过程中,会呈现这样的状况,一个数据须要通过多个函数的解决之后能力成为你想要的数据,那么调用时可能会呈现相似 y = n(g(f(x)))这样的“洋葱“式代码,这种代码既不利于了解,也不利于调试。想要避开这种写法,就能够利用函数组合。函数组合就是将多个细粒化的纯函数组装成一个函数,数据能够在这个组装后的函数中依照肯定的程序执行。
// 简略的组合函数
function compose(...args) {return function () {
// reverse 反转是为了实现从右到左一次执行
return args.reverse().reduce(function (result, fn) {return fn(...result)
}, arguments)
}
}
// 上面三个纯函数是为了实现获取数组的最初一项并大写
function reverse(array) {return array.reverse()
}
function first(array) {return array[0]
}
function toUpper(str) {return str.toUpperCase()
}
const arr = ['a', 'b', 'c']
// 原始调用
console.log(toUpper(first(reverse(arr))))
const composed = compose(toUpper, first, reverse)
// 组合后调用
console.log(composed(arr))
从上例中能够看出,如果想要函数组合,那么有个必要前提:被组合的函数必须是有输入输出的纯函数。