-
异常会让函数变得不纯,Either 函子可以用来做异常处理
-
示例
const { truncate } = require(“fs”)
class Left {
static of (value) {
return new Left(value)
}
constructor (value) {
this._value = value
}
map (fn) {
return this
}
}
class Right {
static of (value) {
return new Right(value)
}
constructor (value) {
this._value = value
}
map (fn) {
return Right.of(fn(this._value))
}
}
// let r1 = Right.of(12).map(x => x + 2)
// let r2 = Left.of(12).map(x => x + 2)
// console.log(r1) // Right { _value: 14 }
// console.log(r2) // Left { _value: 12 }
/**
-
我们需要先定义两个类,两个类之间有区别,主要是Map方法中有所区别
-
我们打印后的两个输出的结果是不一样的,两个代码都是一样的,只是一个使用left创建,一个试用right创建
-
right中的值 +2 返回了14,但是 left 中直接是返回了传入的数据,没有做任何处理
-
我们可以对比两个类中的map方法
-
Left 中 直接把当前对象返回了,并没有调用我们传入的fn,这样做是为了让Left中嵌入一个错误消息
-
下边我们来掩饰一个可能会发生错误的函数,比如我们要把一个json形式的字符串,转成一个json对象
-
*/
function parseJSON (str) {
try{
return Right.of(JSON.parse(str))
} catch (e) {
return Left.of({error : e.message})
}
}
// let r1 = parseJSON(‘{ name: zs }’)
// console.log(r1) // Left { _value: { error: ‘Unexpected token n in JSON at position 2’ } }
// let r2 = parseJSON(‘{ “name”: “zs” }’)
// console.log(r2) // Right { _value: { name: ‘zs’ } }
let r3 = parseJSON(‘{ “name”: “zs” }’)
.map(x => x.name.toUpperCase())
console.log(r3) // Right { _value: ‘ZS’ }
/**
-
我们先声明一个叫做 parseJSON 的函数,并给他接收一个参数
-
接下来我们要调用JSON.parse把我们传入的字符串转为json对象并且返回
-
因为调用JSON.parse时可能会出现异常,所以我们使用 try {} catch(e) {}
-
因为出现错误情况我们不去处理的话,那就不是一个纯函数
-
现在我们希望用函数式的方式去处理,所以我们需要写一个纯函数
-
我们现在要在try里return一个函子,我们会把我们转换后的结果交给这个函子,将来在这个函子内部去处理
-
如果出现错误我们是不能不管的,我们也要返回一个值,因为对于纯函数来说,对于相同的输入,始终要有相同的输出
-
那这个时候我们也要返回一个函子,因为我们Either中有Left和Right,我们用right去处理正确的值
-
如果出现异常的时候我们可以返回一个left中的函子,而left这个函子里边,可以帮我们去存储一些错误的信息
-
*/
===============================================================
-
现在我们已经对函子有一个简单的认识,我们可以把它想象为一个盒子,盒子里保存一个值,然后通过调用盒子的map方法,我们可以传入一个函数,通过这个函数,对盒子里的值进行处理,现在我们学习一个新的函子,
-
IO 函子中的 _value 是一个函数,因为函数是一等公民,所以这里是把函数作为值来处理
-
IO 函子可以把不纯的动作存储到 _value中,_value中存储的是函数,我们在函子内部并没有调用这个函数,所以通过IO函数我们其实是延迟执行这个不纯的操作(惰性执行)包装当前的操作是一个纯的操作
-
把不纯的操作交给调用者来处理
-
示例
const fp = require(‘lodash/fp’)
class Io {
static of (value) {
return new Io(function () {
return value
})
}
constructor (fn) {
this._value = fn
}
map (fn) {
return new Io(fp.flowRight(fn,this._value))
}
}
let r = Io.of(process).map( x => x.execPath)
console.log(r._value()) // C:\Program Files\nodejs\node.exe
/**
-
constructor要去接收一个函数,因为IO函子里边保存的是函数,我们将接收到的函数保存在 _value 中
-
of方法与之前有点差异,他要接受一个数据,然后返回一个新的IO函子
-
在这里便要返回一个IO函子,所以要调用IO构造函数,然后传入一个函数
-
因为刚刚我们写过它的构造函数,它的构造函数需要接收的是一个函数
-
所以在这里我们传入一个函数,在这个函数内,我们把刚刚of方法的值返回
-
到这里我们其实可以感受到IO函子,最终想要的还是一个结果,只不过他把取值的过程包装到了函数里边来
-
将来需要值的时候,在来执行这个函数,来取值
-
map方法和之前一样还是需要传递一个fn,这个方法里边我们还是要返回一个IO函子,这里我们要调用IO的构造函数
-
而不是调用of方法,因为外部方法里面,我们把当前函子的value,也就是这个函数和我们传入的这个函数
-
组合成一个新的函数,而不是调用函数去给值 — 这就是和以前不一样的地方
-
*/
-
总结
-
IO函子内部帮我们包装了一些函数,当我们在传递函数的时候,有可能这个函数是一个不纯的操作,但我们不管他是纯还是不纯,我们IO这个函子在执行中,他返回的结果始终是一个纯的操作,IO中有可能包裹不纯的操作,但是当前这个执行始终是一个纯的操作,我们调用map方法时始终会返回一个IO的函子,但是我们IO函子当中的_value属性,它里边保存的这些函数,因为它里边最终要合并很多函数,所以它里边有可能是不纯的,我们把这些不纯的操作,延迟到了调用的时候,我们通过IO函子控制了副作用在可控范围内发生
==================================================================
函子可以帮我们控制副作用,进行异常处理,还可以帮我们处理异步任务,因为在异步操作中,会出现回调地狱而使用 Task 函子,会避免回调地狱。
-
异步任务的实现过于复杂,我们是用 folktale 中的 Task 来演示
-
folktale 一个标准的函数式编程库
-
和lodash、ramda 不同的是,他没有提供很多功能函数
-
只提供了一些函数处理的操作,例如:compose、curry等,一些函子Task、Either、MayBe 等
const { compose, curry } = require(‘folktale/core/lambda’)
const { toUpper, first } = require(‘lodash/fp’)
// 这里的柯里化,与lodash中的柯里化,稍微有一点点的区别
let f = curry(2, (x, y) => x + y)
/**
-
两个参数:
-
第一个:指明我们后边这个函数他有几个参数,目的是为了避免一些错误
-
*/
console.log(f(1, 2))
console.log(f(1)(2))
console.log(f()(1, 2))
// compose — 函数组合 — 和lodash中的函数组合 flowRight 不一样
let f = compose(toUpper, first)
console.log(f([‘one’, ‘two’]))
-
folktale(2.3.2)2.x 中的Task 和 1.0 中的 Task 区别很大, 1.0 中的用法更接近我们现在掩饰的函子
-
这里以2.3.2来演示
-
示例
// 读取package.json,并将其中的version解析出来
const { task } = require(‘folktale/concurrency/task’) // 导入task函子,路径可查官网
const fs = require(‘fs’) // 导入fs模块
const { split, find } = require(‘lodash/fp’) // 导入lodash/fp的方法
// 读取文件的函数,需要一个参数 — 文件的路径,相对路径可以直接写文件名’package.json’
function readFile ( filename ) {
/**
-
返回一个task函子,需要接受一个函数,函数的参数是固定的resolver,可以通过官网来查询
-
resolver是一个对象,里边提供了两个方法:resolve() 和 reject() — 类似于promise
-
*/
return task(resolver => {
// 异步读取文件,需要三个参数:1—读取的路径;2—使用的编码;3—回调函数(在node中是错误优先);
fs.readFile(filename, ‘utf-8’, (err, data) => {
// 判断读取文件时是否出错
if(err){
// 失败时调用
resolver.reject(err)
}
// 成功时调用
resolver.resolve(data)
})
})
}
// 传入路径
readFile(‘package.json’)
/**
-
执行完readFile他不会去读取文件,而是返回了一个Task的函子
-
我们想要它读取文件的话需要调用Task给我们提供的.run方法
-
*/
.run()
/**
-
读取完文件,我们没有传递resolve,我们不知道如何去处理这个数据
-
所以我们需要Task给我们提供的listen来监听当前的执行状态,这里是以时间的方式来给我们提供的
-
*/
.listen({
// 失败时执行的
onRejecter: err => {
console.log(err)
},
// 成功是执行的
onResolved: value =>{
console.log(value)
}
})
// 以换行为切割,分成数组,然后再去其中寻找version
readFile(‘package.json’)
.map(split(‘\n’))
.map(find(x => x.includes(‘version’)))
.run()
.listen({
onRejecter: err => {
console.log(err)
},
onResolved: value =>{
console.log(value)
}
})
===================================================================
-
Pointed函子时实现了of静态方法的函子
-
of方法是为了避免使用new来创建对象,更深层的含义是of方法用来把值放到上下文Context(把值放到容器中,使用Map来处理值)
-
示例
class Content {
static of (value) {
return new Content(value)
}
…
}
let c = Content.of(2)
.map(x => x + 3)
console.log©
=================================================================
- 在使用IO函子的时候,如果我们写出如下代码
const fp = require(‘lodash/fp’)
const fs = require(‘fs’)
class IO {
static of (value) {
return new IO(function () {
return value
})
}
constructor (fn) {
this._value = fn
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
}
let readFile = function (filename) {
return new IO(function () {
return fs.readFileSync(filename, ‘utf-8’)
})
}
let print = function (x) {
return new IO (function () {
console.log(x)
return x
})
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注:前端)
总结
秋招即将开始,校招的朋友普遍是缺少项目经历的,所以底层逻辑,基础知识要掌握好!
而一般的社招,更是神仙打架。特别强调,项目经历不可忽视;几乎简历上提到的项目都会被刨根问底,所以项目应用的技术要熟练,底层原理必须清楚。
这里给大家提供一份汇集各大厂面试高频核心考点前端学习资料。涵盖 HTML,CSS,JavaScript,HTTP,TCP协议,浏览器,Vue框架,算法等高频考点238道(含答案)!
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
资料截图 :
高级前端工程师必备资料包
**
[外链图片转存中…(img-H9PHspxX-1711189855959)]
总结
秋招即将开始,校招的朋友普遍是缺少项目经历的,所以底层逻辑,基础知识要掌握好!
而一般的社招,更是神仙打架。特别强调,项目经历不可忽视;几乎简历上提到的项目都会被刨根问底,所以项目应用的技术要熟练,底层原理必须清楚。
这里给大家提供一份汇集各大厂面试高频核心考点前端学习资料。涵盖 HTML,CSS,JavaScript,HTTP,TCP协议,浏览器,Vue框架,算法等高频考点238道(含答案)!
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】
资料截图 :
高级前端工程师必备资料包