高阶函数
满足以下条件任意一种都可视为高阶函数:
- 若函数的参数是一个函数;
- 若函数返回了一个函数(返回的函数就是高阶函数)。
常见高阶函数的应用
有一个核心功能
AOP 面向切片编程
什么是闭包(作用域产生:根据函数定义的位置来产生作用域。函数执行时,产生的是执行上下文。)
有一个函数,可以不在当前作用域下执行,在函数外执行时,可拿到作用域内的变量。
// AOP 面向切片编程,应用场景:重新原生方法
function say(who) {
// 在说话之前需做。。。。
console.log(who + '说话')
}
// 让每个方法都有 before 方法,则在 Function.prototype 上添加 before 方法
Function.prototype.before = function(beforeFunc) {
return (...args) => { // newFn 箭头函数无 prototype / 无 arguments / 无 this
beforeFunc()
this(...args)
}
}
let newFn = say.before(function() {
console.log('说话前')
})
newFn('chen') // 看调用函数之前的上下文
// Vue2.x 中也会利用 函数劫持 ,也是运用了 AOP 思想
const cachePush = Array.prototype.push
function newPush(...args) {
// 此时 newPush 函数中 this 指向为:[1, 2, 3]
cachePush.call(this, ...args)
}
const arr = [1, 2, 3]
newPush.call(arr, 4, 5, 6) // 改变 newPush 函数中的 this 指向为:arr
console.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 close
newFn()
// after 在。。。之后调用。如在函数调用三次之后调用
// 词法作用域,函数定义时,已经决定了变量的可访问的范围。
function after(times, callback) {
return function() { // fn
if(--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] = value
if(--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 = data
events.emit() // 触发
})
fs.readFile('./b.txt', 'utf-8', (err, data) => {
school.age = data
events.emit()
})
/*
1:通过回调函数来解决 after 函数
2:发布-订阅模式,发布emit,订阅on
3:观察者模式
两者(观察者、被观察者)是否是有关系的
*/
/*
观察者模式
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:Promise
3:async / await
4: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 toString
if ((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 = true
reject(error)
}
} else {
// 若 res 是普通值
resolve(res) // 直接让 promise2 成功即可
}
}
class MyPromise {
constructor(executor) {
this.state = PENDING
// 发布 - 订阅模式,收集 .then 中成功、失败的回调函数onFulfilledCallbacks / onRejectedCallbacks
this.onFulfilledCallbacks = []
this.onRejectedCallbacks = []
this.value = undefined // 成功的值,this 实例上的 value
this.reason = undefined // 失败的原因,this 实例上的 reason
// 成功函数,类中的私有方法
const resolve = (value) => {
if (this.state === PENDING) {
this.value = value
this.state = FULFILLED
this.onFulfilledCallbacks.forEach(fn => fn()) // 发布
}
}
// 失败函数,类中的私有方法
const reject = (reason) => {
if (this.state === PENDING) {
this.reason = reason
this.state = REJECTED
this.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 => data
onrejected = 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 = resolve
dfd.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 -g
module.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
}
// 判断是不是 Promise
function 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')
})