高阶函数
满足以下条件任意一种都可视为高阶函数:
- 若函数的参数是一个函数;
- 若函数返回了一个函数(返回的函数就是高阶函数)。
常见高阶函数的应用
有一个核心功能
AOP 面向切片编程
什么是闭包(作用域产生:根据函数定义的位置来产生作用域。函数执行时,产生的是执行上下文。)
有一个函数,可以不在当前作用域下执行,在函数外执行时,可拿到作用域内的变量。
// AOP 面向切片编程,应用场景:重新原生方法function say(who) {// 在说话之前需做。。。。console.log(who + '说话')}// 让每个方法都有 before 方法,则在 Function.prototype 上添加 before 方法Function.prototype.before = function(beforeFunc) {return (...args) => { // newFn 箭头函数无 prototype / 无 arguments / 无 thisbeforeFunc()this(...args)}}let newFn = say.before(function() {console.log('说话前')})newFn('chen') // 看调用函数之前的上下文
// Vue2.x 中也会利用 函数劫持 ,也是运用了 AOP 思想const cachePush = Array.prototype.pushfunction newPush(...args) {// 此时 newPush 函数中 this 指向为:[1, 2, 3]cachePush.call(this, ...args)}const arr = [1, 2, 3]newPush.call(arr, 4, 5, 6) // 改变 newPush 函数中的 this 指向为:arrconsole.log(arr) // [ 1, 2, 3, 4, 5, 6 ]
/*react setState 事务,此事务非数据库中的事务*/function perform(anyMethod, wrappers) {return function() {wrappers.forEach(wrapper => wrapper.initialize())anyMethod()wrappers.forEach(wrapper => wrapper.close())}}const newFn = perform(function() {console.log('say.....')}, [{initialize() {console.log('wrapper1 beforeSay')},close() {console.log('wrapper1 close')},},{initialize() {console.log('wrapper2 beforeSay')},close() {console.log('wrapper2 close')},}])// wrapper1 beforeSay// wrapper2 beforeSay// say.....// wrapper1 close// wrapper2 closenewFn()
// after 在。。。之后调用。如在函数调用三次之后调用// 词法作用域,函数定义时,已经决定了变量的可访问的范围。function after(times, callback) {return function() { // fnif(--times === 0) {callback()}}}const fn = after(3, function() {console.log('really')})fn()fn()
const fs = require('fs');function after(times, cb) {const res = {}return function(key, value) {res[key] = valueif(--times === 0) { // 若达到次数,则执行 after 方法的回调函数cb(res) // 并且将结果传入}}}const out = after(2, function(result) { // 公共处理异步方式console.log(result)})fs.readFile('./a.txt', 'utf-8', (err, data) => {out('name', data)})fs.readFile('./b.txt', 'utf-8', (err, data) => {out('age', data)})// 最终拿到的整体结果:{ age: '10', name: 'this is a.txt' }
/*发布 - 订阅模式理解:发布、订阅之间并没有耦合性。*/const fs = require('fs');const events = {_arr: [],on(fn) {this._arr.push(fn)},emit() {this._arr.forEach(func => func())}}const school = {}events.on(function () { // 函数只是绑定,并不会立即执行console.log('读取一个')})events.on(function() {if (Object.keys(school).length === 2) {console.log(school)}})fs.readFile('./a.txt', 'utf-8', (err, data) => {school.name = dataevents.emit() // 触发})fs.readFile('./b.txt', 'utf-8', (err, data) => {school.age = dataevents.emit()})/*1:通过回调函数来解决 after 函数2:发布-订阅模式,发布emit,订阅on3:观察者模式两者(观察者、被观察者)是否是有关系的*/
/*观察者模式Vue 特点,数据变化 => 视图更新。监控数据的变化,数据变化需更新视图。*/// 观察者class Observer {constructor(name) {this.name = name}// 更新状态函数update(newState) {console.log(`${this.name},状态: ${newState}`)}}// 被观察者class Subject {constructor() {this.state = 'sleep。。。'this.obList = []}attach(ob) {this.obList.push(ob)}setState(newState) {// 1:更新状态this.state = newState// 2:通知各个观察者this.obList.forEach(ob => ob.update(newState))}}const sub = new Subject() // 被观察者const ob1 = new Observer('chen') // 观察者const ob2 = new Observer('liu') // 观察者// 此时,观察者和被观察者之间就有了联系sub.attach(ob1)sub.attach(ob2)sub.setState('eat....')sub.setState('溜达。。。。')
Promise
/*Promise 特点:1:三个状态:pending(进行中)- 默认、fulfilled(已成功)和rejected(已失败)。状态一旦改变,就不能再变。2:每个 Promise 实例都有一个 then 方法;3:若 new Promise 时报错了,状态就会变成 rejected,执行 then 中的 rejected 方法。Promise 优缺点:优点:1:可解决异步嵌套问题;2:可解决多个异步并发问题;缺点:1:Promise 基于回调;2:Promise 无法终止异步。异步方案:1:callback;2:Promise3:async / await4:Generate*/const PENDING = 'PENDING'const FULFILLED = 'FULFILLED'const REJECTED = 'REJECTED'// 判断 promise2 和 res 的关系const resolvePromise = (promise2, res, resolve, reject) => {// 判断 promise2 和 res 是不是同一个,若是同一个,就不需要等待了,直接出错即可。if (promise2 === res) {return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))}// 判断数据类型 typeof constructor instanceof toStringif ((typeof res === 'object' && res !== null) || typeof res === 'function') {let called; // 内部测试时,会成功和失败都调用try {const then = res.then // 获取 then 属性可能会报错,有可能 then 属性是通过 Object.defineProperty 来定义的。if (typeof then === 'function') {then.call(res, y => { // y 可能还是一个 promise,直到解析出来的结果是一个普通值为止。if (called) {return}called = true // 防止多次调用成功或失败resolvePromise(promise2, y, resolve, reject) // 采用 promise 的成功结果将值向下传递}, r => {reject(r) // 采用失败结果向下传递}) // 能保证不用再次取 then 的值,再次取值可能出错} else {if (called) {return}called = true// {then: 1}resolve(res) // 说明 res 是一个普通对象,直接成功即可}} catch (error) {// promise 失败了,有可能还能调用成功if (called) {return}called = truereject(error)}} else {// 若 res 是普通值resolve(res) // 直接让 promise2 成功即可}}class MyPromise {constructor(executor) {this.state = PENDING// 发布 - 订阅模式,收集 .then 中成功、失败的回调函数onFulfilledCallbacks / onRejectedCallbacksthis.onFulfilledCallbacks = []this.onRejectedCallbacks = []this.value = undefined // 成功的值,this 实例上的 valuethis.reason = undefined // 失败的原因,this 实例上的 reason// 成功函数,类中的私有方法const resolve = (value) => {if (this.state === PENDING) {this.value = valuethis.state = FULFILLEDthis.onFulfilledCallbacks.forEach(fn => fn()) // 发布}}// 失败函数,类中的私有方法const reject = (reason) => {if (this.state === PENDING) {this.reason = reasonthis.state = REJECTEDthis.onRejectedCallbacks.forEach(fn => fn()) // 发布}}// 若 executor 在执行时报错,等价于调用了 reject 方法。// !重点:try...catch 只能捕获同步异常try {executor(resolve, reject) // 执行器,默认执行器会立即执行} catch (error) {reject(error)}}// then 方法有两个可选参数,类中的实例方法then(onfulfilled, onrejected) {// 判断可选参数onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => dataonrejected = typeof onrejected === 'function' ? onrejected : err => {throw err}const promise2 = new MyPromise((resolve, reject) => { // executor 立即执行函数// 处理 executor 中是同步代码if (this.state === FULFILLED) {setTimeout(() => { // 宏任务,为了保证 promise2 已经 new 完成了try {const res = onfulfilled(this.value)// res 可能是普通值,也可能是 promise// 判断 res 的值,进而推导出 promise2 的状态// !重点:promise2 通过宏任务,来延迟取值resolvePromise(promise2, res, resolve, reject)} catch (error) {reject(error)}}, 0);}if (this.state === REJECTED) {setTimeout(() => {try {const res = onrejected(this.reason)resolvePromise(promise2, res, resolve, reject)} catch (error) {reject(error)}}, 0);}// 处理 executor 中是异步代码if (this.state === PENDING) {this.onFulfilledCallbacks.push(() => {// others todo....setTimeout(() => {try {const res = onfulfilled(this.value)resolvePromise(promise2, res, resolve, reject)} catch (error) {reject(error)}}, 0);})this.onRejectedCallbacks.push(() => {// others todo....setTimeout(() => {try {const res = onrejected(this.reason)resolvePromise(promise2, res, resolve, reject)} catch (error) {reject(error)}}, 0);})}})return promise2}}MyPromise.defer = MyPromise.deferred = function() {let dfd = {}dfd.promise = new MyPromise((resolve, reject) => {dfd.resolve = resolvedfd.reject = reject})return dfd}MyPromise.resolve = function(value){return new MyPromise((resolve,reject)=>{resolve(value);})}MyPromise.reject = function(value){return new MyPromise((resolve,reject)=>{reject(value);})}MyPromise.prototype.finally = function(callback){return this.then((data)=>{return MyPromise.resolve(callback()).then(()=>data);// return new MyPromise((resolve,reject)=>{// resolve(callback()); // 如果callback是一个函数返回promise 就等待这个promise执行完毕// }).then(()=>data);// callback();// return data;},(err)=>{return MyPromise.resolve(callback()).then(()=>{throw err}); // koa 原理// throw err;});};// npm i promises-aplus-tests -gmodule.exports = MyPromise
const MyPromise = require('./MyPromise')const fs = require('fs')/*Promise.all 全部 可实现等待所有异步执行完成后,拿到统一结果解决异步并发 同步处理结果问题*/// 读取文件内容function read(url) {// 方法一:/* return new MyPromise((resolve, reject) => {fs.readFile(url, 'utf-8', (err, data) => {if (err) {reject(err)}resolve(data)})}) */// 方法二:利用 dfd 来写,解决封装嵌套问题const dfd = MyPromise.defer()fs.readFile(url, 'utf-8', (err, data) => {if (err) {dfd.reject(err)}dfd.resolve(data)})return dfd.promise}// 判断是不是 Promisefunction isPromise(value) {if ((typeof value === 'object' && value !== null) || typeof value === 'function') {if(typeof value.then === 'function') {return true}} else {return false}}/*MyPromise.all 的实现,类上的静态属性。思考:什么时候才算成功。只有当 arr.length === values.length 时,才算全部成功,但是这样有个问题:[1, 2, 3, read('./a.txt'), read('./b.txt'), 4, 5] 有两个异步项,当执行到 arr[6] = 5 时,异步还没有返回,此时输出结果是:[1, 2, 3, empty, empty, 4, 5]。理解:同步代码先执行,如 arr[6] = 5,那 arr.length = 6,此时arr.length === values.length,但是异步代码结果还没返回,所以此种判断方法不对。所以,需用 count 计数器,来解决多个异步并发问题。*/MyPromise.all = function (values) {return new MyPromise((resolve, reject) => {const arr = []let count = 0 // 重点:解决多个异步并发问题,需使用计数器function processData (key, value) {arr[key] = value// if (arr.length === values.length) { resolve(arr) } // !:这样判断不对if (++count === values.length) {resolve(arr)}}for (let i = 0; i < values.length; i++) {const cur = values[i];if(isPromise(cur)) {cur.then(data => {processData(i, data)}, reject)} else {processData(i, cur)}}})}/* MyPromise.all([1, 2, 3, read('./a.txt'), read('./b.txt'), 4, 5]).then(data => {console.log(data)}) */// 场景一/* const promise = new MyPromise((resolve, reject) => {resolve(123)}).then((data) => {console.log('data', data)}, error => {console.log('error', error)}) */// 场景二// 可多次调用 promise 实例上的 then 方法。// 若 executor 里面是一个 setTimeout,则需在 MyPromise 中添加成功、失败回调数组(onFulfilledCallbacks / onRejectedCallbacks)/* const promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(123)}, 2000);})promise.then((data) => {console.log('data', data)}, error => {console.log('error', error)})promise.then((data) => {console.log('data', data)}, error => {console.log('error', error)})promise.then((data) => {console.log('data', data)}, error => {console.log('error', error)}) */// 场景三/*若一个 promise 的 then 方法中的函数(成功、失败)返回的结果是一个 promise 的话,会自动将这个 promise 执行,并且采用它的状态,若成功会将成功的结果向外层的下一个 then 传递*/read('./a.txt').then(data => {console.log(read('./b.txt'))return read('./b.txt')}, err => {console.log(err)}).then(data => {console.log(data)}, err => {console.log(err)return false // 返回一个普通值,会将此普通值作为下一次成功的结果}).then(data => {console.log(data)// !重点:若不希望继续向下再走 then ?return new MyPromise(() => {}) // 终止 promise ,可返回一个 pending 状态的 promise}).then(() => {console.log('success')},() => {console.log('err')})
