文章目录
前言
今天是学习大前端的第三天,也是人生第一次写IT笔记的一天。4-16是一个值得纪念的日子。
今天我将对我学习的Part1-task1模块进行一个总结
以下是本篇文章正文内容
一、函数式编程是什么?
- 函数式编程(Functional Programming)简写FP
- 是编程范式之一,其他还有面向对象编程(new),面向过程编程(感觉自己目前是这样的思维),并列关系
- 思维方式:把现实世界的事物和事物之间的联系抽象到程序世界(对运算过程进行抽象)
- 程序本质:根据输入通过计算得到相应的输出,程序开发中会有很多这种输入输出的函数(就是要对这些函数进行抽象!)
- 函数式编程中的函数指的不是程序中的函数,而是数学中的函数即映射关系,例:y=sin(x),x和y的关系(x确定了y的值也确定了!)
- 相同的输入始终要得到相同的输出(纯函数)
- 函数式编程用来描述数据之间的映射(y=fn(x),fn()来描述x和y的关系)
举个例子
//非函数式 面向过程编程
let a=1
let b=2
let c=a+b
console.log(c)
//函数式 细粒度 可以重用 组合成高阶函数
function add(n1,n2)=>{
return n1+n2
}
let sum=add(1,2)
二、为什么要学习函数式编程?
- 函数式编程是因为react流行起来
- Vue3也开始拥抱函数式编程
- 函数式编程可以抛弃this、
- 打包过程中可以更好利用tree shaking过滤掉无用代码
- 方便测试,方便并行处理、
- 有很多库可以帮助我们函数式开发:lodash,underscore,ramda
- 以上目前因个人原因都没有使用到 !!!(以后会的)
三、函数是一等公民
- 在JS中函数是一个普通对象,它可以存储到变量/数组中,可以作为参数和返回值,我们还可以通过new Function(‘alert(1)’)来构造一个函数
四、高阶函数(Higher-order function)
- 函数可以作为参数和返回值
模拟2个高阶函数 作为参数
//forEach
let forEach=(arr,fn)=>{
for(let v of arr){
fn(v)
}
}
let arr1=[0,1,2,3,4,5]
forEach(arr1,(item)=>{
console.log(item)
})
//filter
let filter=(arr,fn)=>{
let array=[]
for(let v of arr){
if(fn(v)){
array.push(v)
}
}
return array
}
let arr2=[13,15,18,20,22]
filter(arr2,(item)=>{
return item>=18
})
模拟JQ的once 作为返回值
//once 应用场景 支付
let once=(fn)=>{
let first=true
//这里我用 箭头函数 会发生意外 因为箭头函数没有arguments和 箭头函数没有this指向,this的值要向上一层作用域链this是个空对象
return function(){
if(first){
first=false
fn.apply(this,arguments)
}
}
}
let pay=once((money)=>{
console.log(`多少${money}`)
})
pay(5)
pay(5)
pay(5)
pay(5)
pay(5)
- 意义:代码更简洁,帮我们屏蔽细节只需要关注目标,用来抽象通用的问题
五、闭包(Closure)
- 在另一个作用域中调用一个函数的内部函数并且访问到该函数的作用域中的成员
六、Promise
- promise3个状态 peeding,成功onfulfilled,失败onrejected。一旦执行完成,结果不可逆
使用案例-封装ajax
const { reject } = require("lodash");
function ajax(url){
return new Promise((resolve,reject)=>{
let xhr=new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType='JSON'
xhr.onload=()=>{
if(this.status===200){
resolve(xhr.response)
}else{
reject(new Error(xhr.responseText))
}
}
xhr.send()
})
}
ajax('testURL').then(res=>{
console.log(res)
},err=>{
console.log(err);
})
//误区 回调地狱
ajax('testURL').then(res => {
ajax(res.url).then(res2 => {
ajax(res2.url).then(res3 => {
ajax(res3.url).then(res4 => {
ajax(res4.url).then(res5 => {
})
})
})
})
}, err => {
})
//正确写法
ajax('testURL').then(res=>{
console.log(res)
},err=>{
console.log(err);
}).then(res=>{
console.log(res)
}).then(res=>{
console.log(res)
})
- Promise对象的then方法会返回一个Promise全新对象
- 后面的then是为上一个then返回的Promise对象注册回调
- 前面then回调函数的返回值会作为后面then方法中的回调函数的参数
- 如果回调函数返回的是Promise,那后面的then就是为这个Promise注册回调
promise.all&promise.race的使用区别
//all 等待所有的任务结束
Promise.all([promise对象1,promise对象2,Promise对象3])
.then(res=>{
console.log(res) //res是一个array 返回3个Promise的返回结果
})
.catch(err=>{
console.log(err) //err是只要有一个Promise返回失败就会执行catch
})
//race 只会等待第一个执行结束的任务
Promise.race([promise对象1,promise对象2,Promise对象3])
.then(res=>{
console.log(res) //res是传参的多个Promise对象中第一个执行完毕的Promise回调
})
- 宏任务和微任务
console.log("start")
setTimeout(()=>{
console.log('setTimeOut')
},0)
Promise.resolve("promise").then(res=>{
console.log(res)
})
console.log("end")
//这里打印顺序是 start-end-promise-setTimeout
- 一般我们把回调队列中的任务称为宏任务,宏任务执行过程中可以临时新增一些新的额外需求,这些需求可以选择作为一个新的宏任务进入到回调队列中去排队,这时这些任务在回调队列的末尾排队去执行(setTimeOut和大部分异步调用API)就是这种宏任务方式,这些需求还可以选择微任务方式,微任务指的是直接在当前任务结束后立即执行,不需要在当前回调队列末尾去排队执行,Promise&MutationObserver&Node中的process.nextTick都是作为微任务去执行
- 微任务的目的是为了提高整个应用的响应能力
- 打个比方,去银行排队办事,你开始是想要存钱,存完钱你想要办张信用卡,这时候你可以直接和柜台人员说你想要办张信用卡,不需要再去队伍后面排队。这时排队的人都是想要存钱的,这些是宏任务,你想要办信用卡这个需求是这个队列中的微任务
七、Generator异步方案
代码示例
function* foo() {
console.log('start')
try {
const res= yield 'foo'
console.log(res)
} catch (error) {
console.log(error)
}
}
const generator = foo()
const result = generator.next()
console.log(result)
// const result2 = generator.next('bar')
// console.log(result2)
generator.throw(new Error("is error"))
//与promise结合使用
function ajax(url) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'JSON'
xhr.onload = () => {
if (this.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.responseText))
}
}
xhr.send()
})
}
function * main() {
try {
const data = yield ajax('json1.json')
console.log(data)
const data2 = yield ajax('json2.json')
console.log(data2)
} catch (error) {
console.log(error)
}
}
function co(generator) {
const g = generator()
function handleResult(result) {
if (result.done) return
result.value.then(res => {
handleResult(g.next(res))
}, err => {
g.throw(err)
})
}
handleResult(g.next())
}
co(main)
- es2017 出现的Async和Await语法糖可替代Generator
代码示例
async function main() {
try {
const data = await ajax('json1.json')
console.log(data)
const data2 = await ajax('json2.json')
console.log(data2)
} catch (error) {
console.log(error)
}
}
const promise= main()
promise.then(res=>{
console.log(res)
})
八、手写Promise类源码
const PEEDING = "peeding" //等待
const FULFILLED = "fulfilled" //成功
const REJECTED = "rejected" //失败
class Mypromise {
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
//promise的状态
status = PEEDING
//成功值
value = undefined
//失败原因
reason = undefined
//成功回调
successCallBack = []
//失败回调
failCallBack = []
resolve = value => {
//状态改为成功
if (this.status !== PEEDING) return
this.status = FULFILLED
//保存成功值
this.value = value
// this.successCallBack && this.successCallBack(this.value)
//数组依次执行里面的函数
while (this.successCallBack.length) this.successCallBack.shift()()
}
reject = reason => {
//状态改为失败
if (this.status !== REJECTED) return
this.status = REJECTED
//保存失败原因
this.reason = reason
// this.failCallBack && this.failCallBack(this.reason)
//数组依次执行里面的函数
while (this.successCallBack.length) this.successCallBack.shift()()
}
then(successCallBack, failCallBack) {
//使then可以传空参
successCallBack = successCallBack ? successCallBack : value => value
failCallBack = failCallBack ? failCallBack : reason => {
throw reason
}
//链式调用
let promise2 = new Mypromise((resolve, reject) => {
if (this.status === FULFILLED) {
//异步调用 得到promise2
setTimeout(() => {
//添加异常报错情况
try {
let x = successCallBack(this.value)
//判断回调是否是Promise函数,不是话执行resolve,是的话根据自身promise返回的结果,来调用resolve或reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
//异步调用 得到promise2
setTimeout(() => {
try {
let x = failCallBack(this.reason)
//判断回调是否是Promise函数,不是话执行resolve,是的话根据自身promise返回的结果,来调用resolve或reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
//异步调用 得到promise2
setTimeout(() => {
//添加异常报错情况
try {
this.successCallBack.push(() => {
try {
let x = successCallBack(this.value)
//判断回调是否是Promise函数,不是话执行resolve,是的话根据自身promise返回的结果,来调用resolve或reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
this.failCallBack.push(() => {
//添加异常报错情况
try {
let x = failCallBack(this.reason)
//判断回调是否是Promise函数,不是话执行resolve,是的话根据自身promise返回的结果,来调用resolve或reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} catch (error) {
reject(error)
}
}, 0)
}
})
return promise2
}
catch (failCallBack) {
return this.then(undefined, failCallBack)
} finally(callBack) {
return this.then(value => {
Mypromise.resolve(callBack()).then(() => value)
}, reason => {
Mypromise.resolve(callBack()).then(() => {
throw reason
})
})
}
//all方法是个静态方法
static all(array) {
let arr2 = []
let index = 0
return new Mypromise((resolve, reject) => {
function addData(index, value) {
arr[index] = value
index++
if (index === array.length) {
resolve(arr2)
}
}
array.forEach((item, index) => {
if (item instanceof Mypromise) {
item.then(value => addData(index, value), err => reject(err))
} else {
addData(item, index)
}
});
})
}
//resolve静态方法
static resolve(value) {
if (value instanceof Mypromise) return value
return new Mypromise(resolve => {
resolve(value)
})
}
}
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new Error('promise重复调用'))
}
if (x instanceof Mypromise) {
x.then(resolve, reject)
} else {
resolve(x)
}
}
总结
以上就是我part1模块的笔记,上面还有些task1未记录,后续有时间我会尽快补上
to be continued…