高阶函数

满足以下条件任意一种都可视为高阶函数:

  1. 若函数的参数是一个函数;
  2. 若函数返回了一个函数(返回的函数就是高阶函数)。

常见高阶函数的应用
有一个核心功能
AOP 面向切片编程

什么是闭包(作用域产生:根据函数定义的位置来产生作用域。函数执行时,产生的是执行上下文。)
有一个函数,可以不在当前作用域下执行,在函数外执行时,可拿到作用域内的变量。


  1. // AOP 面向切片编程,应用场景:重新原生方法
  2. function say(who) {
  3. // 在说话之前需做。。。。
  4. console.log(who + '说话')
  5. }
  6. // 让每个方法都有 before 方法,则在 Function.prototype 上添加 before 方法
  7. Function.prototype.before = function(beforeFunc) {
  8. return (...args) => { // newFn 箭头函数无 prototype / 无 arguments / 无 this
  9. beforeFunc()
  10. this(...args)
  11. }
  12. }
  13. let newFn = say.before(function() {
  14. console.log('说话前')
  15. })
  16. newFn('chen') // 看调用函数之前的上下文
  1. // Vue2.x 中也会利用 函数劫持 ,也是运用了 AOP 思想
  2. const cachePush = Array.prototype.push
  3. function newPush(...args) {
  4. // 此时 newPush 函数中 this 指向为:[1, 2, 3]
  5. cachePush.call(this, ...args)
  6. }
  7. const arr = [1, 2, 3]
  8. newPush.call(arr, 4, 5, 6) // 改变 newPush 函数中的 this 指向为:arr
  9. console.log(arr) // [ 1, 2, 3, 4, 5, 6 ]
  1. /*
  2. react setState 事务,此事务非数据库中的事务
  3. */
  4. function perform(anyMethod, wrappers) {
  5. return function() {
  6. wrappers.forEach(wrapper => wrapper.initialize())
  7. anyMethod()
  8. wrappers.forEach(wrapper => wrapper.close())
  9. }
  10. }
  11. const newFn = perform(function() {
  12. console.log('say.....')
  13. }, [
  14. {
  15. initialize() {console.log('wrapper1 beforeSay')},
  16. close() {console.log('wrapper1 close')},
  17. },
  18. {
  19. initialize() {console.log('wrapper2 beforeSay')},
  20. close() {console.log('wrapper2 close')},
  21. }
  22. ])
  23. // wrapper1 beforeSay
  24. // wrapper2 beforeSay
  25. // say.....
  26. // wrapper1 close
  27. // wrapper2 close
  28. newFn()
  1. // after 在。。。之后调用。如在函数调用三次之后调用
  2. // 词法作用域,函数定义时,已经决定了变量的可访问的范围。
  3. function after(times, callback) {
  4. return function() { // fn
  5. if(--times === 0) {
  6. callback()
  7. }
  8. }
  9. }
  10. const fn = after(3, function() {
  11. console.log('really')
  12. })
  13. fn()
  14. fn()
  1. const fs = require('fs');
  2. function after(times, cb) {
  3. const res = {}
  4. return function(key, value) {
  5. res[key] = value
  6. if(--times === 0) { // 若达到次数,则执行 after 方法的回调函数
  7. cb(res) // 并且将结果传入
  8. }
  9. }
  10. }
  11. const out = after(2, function(result) { // 公共处理异步方式
  12. console.log(result)
  13. })
  14. fs.readFile('./a.txt', 'utf-8', (err, data) => {
  15. out('name', data)
  16. })
  17. fs.readFile('./b.txt', 'utf-8', (err, data) => {
  18. out('age', data)
  19. })
  20. // 最终拿到的整体结果:{ age: '10', name: 'this is a.txt' }
  1. /*
  2. 发布 - 订阅模式
  3. 理解:
  4. 发布、订阅之间并没有耦合性。
  5. */
  6. const fs = require('fs');
  7. const events = {
  8. _arr: [],
  9. on(fn) {
  10. this._arr.push(fn)
  11. },
  12. emit() {
  13. this._arr.forEach(func => func())
  14. }
  15. }
  16. const school = {}
  17. events.on(function () { // 函数只是绑定,并不会立即执行
  18. console.log('读取一个')
  19. })
  20. events.on(function() {
  21. if (Object.keys(school).length === 2) {
  22. console.log(school)
  23. }
  24. })
  25. fs.readFile('./a.txt', 'utf-8', (err, data) => {
  26. school.name = data
  27. events.emit() // 触发
  28. })
  29. fs.readFile('./b.txt', 'utf-8', (err, data) => {
  30. school.age = data
  31. events.emit()
  32. })
  33. /*
  34. 1:通过回调函数来解决 after 函数
  35. 2:发布-订阅模式,发布emit,订阅on
  36. 3:观察者模式
  37. 两者(观察者、被观察者)是否是有关系的
  38. */
  1. /*
  2. 观察者模式
  3. Vue 特点,数据变化 => 视图更新。
  4. 监控数据的变化,数据变化需更新视图。
  5. */
  6. // 观察者
  7. class Observer {
  8. constructor(name) {
  9. this.name = name
  10. }
  11. // 更新状态函数
  12. update(newState) {
  13. console.log(`${this.name},状态: ${newState}`)
  14. }
  15. }
  16. // 被观察者
  17. class Subject {
  18. constructor() {
  19. this.state = 'sleep。。。'
  20. this.obList = []
  21. }
  22. attach(ob) {
  23. this.obList.push(ob)
  24. }
  25. setState(newState) {
  26. // 1:更新状态
  27. this.state = newState
  28. // 2:通知各个观察者
  29. this.obList.forEach(ob => ob.update(newState))
  30. }
  31. }
  32. const sub = new Subject() // 被观察者
  33. const ob1 = new Observer('chen') // 观察者
  34. const ob2 = new Observer('liu') // 观察者
  35. // 此时,观察者和被观察者之间就有了联系
  36. sub.attach(ob1)
  37. sub.attach(ob2)
  38. sub.setState('eat....')
  39. sub.setState('溜达。。。。')

Promise

  1. /*
  2. Promise 特点:
  3. 1:三个状态:pending(进行中)- 默认、fulfilled(已成功)和rejected(已失败)。状态一旦改变,就不能再变。
  4. 2:每个 Promise 实例都有一个 then 方法;
  5. 3:若 new Promise 时报错了,状态就会变成 rejected,执行 then 中的 rejected 方法。
  6. Promise 优缺点:
  7. 优点:
  8. 1:可解决异步嵌套问题;
  9. 2:可解决多个异步并发问题;
  10. 缺点:
  11. 1:Promise 基于回调;
  12. 2:Promise 无法终止异步。
  13. 异步方案:
  14. 1:callback;
  15. 2:Promise
  16. 3:async / await
  17. 4:Generate
  18. */
  19. const PENDING = 'PENDING'
  20. const FULFILLED = 'FULFILLED'
  21. const REJECTED = 'REJECTED'
  22. // 判断 promise2 和 res 的关系
  23. const resolvePromise = (promise2, res, resolve, reject) => {
  24. // 判断 promise2 和 res 是不是同一个,若是同一个,就不需要等待了,直接出错即可。
  25. if (promise2 === res) {
  26. return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  27. }
  28. // 判断数据类型 typeof constructor instanceof toString
  29. if ((typeof res === 'object' && res !== null) || typeof res === 'function') {
  30. let called; // 内部测试时,会成功和失败都调用
  31. try {
  32. const then = res.then // 获取 then 属性可能会报错,有可能 then 属性是通过 Object.defineProperty 来定义的。
  33. if (typeof then === 'function') {
  34. then.call(res, y => { // y 可能还是一个 promise,直到解析出来的结果是一个普通值为止。
  35. if (called) {
  36. return
  37. }
  38. called = true // 防止多次调用成功或失败
  39. resolvePromise(promise2, y, resolve, reject) // 采用 promise 的成功结果将值向下传递
  40. }, r => {
  41. reject(r) // 采用失败结果向下传递
  42. }) // 能保证不用再次取 then 的值,再次取值可能出错
  43. } else {
  44. if (called) {
  45. return
  46. }
  47. called = true
  48. // {then: 1}
  49. resolve(res) // 说明 res 是一个普通对象,直接成功即可
  50. }
  51. } catch (error) {
  52. // promise 失败了,有可能还能调用成功
  53. if (called) {
  54. return
  55. }
  56. called = true
  57. reject(error)
  58. }
  59. } else {
  60. // 若 res 是普通值
  61. resolve(res) // 直接让 promise2 成功即可
  62. }
  63. }
  64. class MyPromise {
  65. constructor(executor) {
  66. this.state = PENDING
  67. // 发布 - 订阅模式,收集 .then 中成功、失败的回调函数onFulfilledCallbacks / onRejectedCallbacks
  68. this.onFulfilledCallbacks = []
  69. this.onRejectedCallbacks = []
  70. this.value = undefined // 成功的值,this 实例上的 value
  71. this.reason = undefined // 失败的原因,this 实例上的 reason
  72. // 成功函数,类中的私有方法
  73. const resolve = (value) => {
  74. if (this.state === PENDING) {
  75. this.value = value
  76. this.state = FULFILLED
  77. this.onFulfilledCallbacks.forEach(fn => fn()) // 发布
  78. }
  79. }
  80. // 失败函数,类中的私有方法
  81. const reject = (reason) => {
  82. if (this.state === PENDING) {
  83. this.reason = reason
  84. this.state = REJECTED
  85. this.onRejectedCallbacks.forEach(fn => fn()) // 发布
  86. }
  87. }
  88. // 若 executor 在执行时报错,等价于调用了 reject 方法。
  89. // !重点:try...catch 只能捕获同步异常
  90. try {
  91. executor(resolve, reject) // 执行器,默认执行器会立即执行
  92. } catch (error) {
  93. reject(error)
  94. }
  95. }
  96. // then 方法有两个可选参数,类中的实例方法
  97. then(onfulfilled, onrejected) {
  98. // 判断可选参数
  99. onfulfilled = typeof onfulfilled === 'function' ? onfulfilled : data => data
  100. onrejected = typeof onrejected === 'function' ? onrejected : err => {
  101. throw err
  102. }
  103. const promise2 = new MyPromise((resolve, reject) => { // executor 立即执行函数
  104. // 处理 executor 中是同步代码
  105. if (this.state === FULFILLED) {
  106. setTimeout(() => { // 宏任务,为了保证 promise2 已经 new 完成了
  107. try {
  108. const res = onfulfilled(this.value)
  109. // res 可能是普通值,也可能是 promise
  110. // 判断 res 的值,进而推导出 promise2 的状态
  111. // !重点:promise2 通过宏任务,来延迟取值
  112. resolvePromise(promise2, res, resolve, reject)
  113. } catch (error) {
  114. reject(error)
  115. }
  116. }, 0);
  117. }
  118. if (this.state === REJECTED) {
  119. setTimeout(() => {
  120. try {
  121. const res = onrejected(this.reason)
  122. resolvePromise(promise2, res, resolve, reject)
  123. } catch (error) {
  124. reject(error)
  125. }
  126. }, 0);
  127. }
  128. // 处理 executor 中是异步代码
  129. if (this.state === PENDING) {
  130. this.onFulfilledCallbacks.push(() => {
  131. // others todo....
  132. setTimeout(() => {
  133. try {
  134. const res = onfulfilled(this.value)
  135. resolvePromise(promise2, res, resolve, reject)
  136. } catch (error) {
  137. reject(error)
  138. }
  139. }, 0);
  140. })
  141. this.onRejectedCallbacks.push(() => {
  142. // others todo....
  143. setTimeout(() => {
  144. try {
  145. const res = onrejected(this.reason)
  146. resolvePromise(promise2, res, resolve, reject)
  147. } catch (error) {
  148. reject(error)
  149. }
  150. }, 0);
  151. })
  152. }
  153. })
  154. return promise2
  155. }
  156. }
  157. MyPromise.defer = MyPromise.deferred = function() {
  158. let dfd = {}
  159. dfd.promise = new MyPromise((resolve, reject) => {
  160. dfd.resolve = resolve
  161. dfd.reject = reject
  162. })
  163. return dfd
  164. }
  165. MyPromise.resolve = function(value){
  166. return new MyPromise((resolve,reject)=>{
  167. resolve(value);
  168. })
  169. }
  170. MyPromise.reject = function(value){
  171. return new MyPromise((resolve,reject)=>{
  172. reject(value);
  173. })
  174. }
  175. MyPromise.prototype.finally = function(callback){
  176. return this.then((data)=>{
  177. return MyPromise.resolve(callback()).then(()=>data);
  178. // return new MyPromise((resolve,reject)=>{
  179. // resolve(callback()); // 如果callback是一个函数返回promise 就等待这个promise执行完毕
  180. // }).then(()=>data);
  181. // callback();
  182. // return data;
  183. },(err)=>{
  184. return MyPromise.resolve(callback()).then(()=>{throw err}); // koa 原理
  185. // throw err;
  186. });
  187. };
  188. // npm i promises-aplus-tests -g
  189. module.exports = MyPromise
  1. const MyPromise = require('./MyPromise')
  2. const fs = require('fs')
  3. /*
  4. Promise.all 全部 可实现等待所有异步执行完成后,拿到统一结果
  5. 解决异步并发 同步处理结果问题
  6. */
  7. // 读取文件内容
  8. function read(url) {
  9. // 方法一:
  10. /* return new MyPromise((resolve, reject) => {
  11. fs.readFile(url, 'utf-8', (err, data) => {
  12. if (err) {
  13. reject(err)
  14. }
  15. resolve(data)
  16. })
  17. }) */
  18. // 方法二:利用 dfd 来写,解决封装嵌套问题
  19. const dfd = MyPromise.defer()
  20. fs.readFile(url, 'utf-8', (err, data) => {
  21. if (err) {
  22. dfd.reject(err)
  23. }
  24. dfd.resolve(data)
  25. })
  26. return dfd.promise
  27. }
  28. // 判断是不是 Promise
  29. function isPromise(value) {
  30. if ((typeof value === 'object' && value !== null) || typeof value === 'function') {
  31. if(typeof value.then === 'function') {
  32. return true
  33. }
  34. } else {
  35. return false
  36. }
  37. }
  38. /*
  39. MyPromise.all 的实现,类上的静态属性。
  40. 思考:什么时候才算成功。
  41. 只有当 arr.length === values.length 时,才算全部成功,但是这样有个问题:
  42. [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,但是异步代码结果还没返回,所以此种判断方法不对。
  43. 所以,需用 count 计数器,来解决多个异步并发问题。
  44. */
  45. MyPromise.all = function (values) {
  46. return new MyPromise((resolve, reject) => {
  47. const arr = []
  48. let count = 0 // 重点:解决多个异步并发问题,需使用计数器
  49. function processData (key, value) {
  50. arr[key] = value
  51. // if (arr.length === values.length) { resolve(arr) } // !:这样判断不对
  52. if (++count === values.length) {
  53. resolve(arr)
  54. }
  55. }
  56. for (let i = 0; i < values.length; i++) {
  57. const cur = values[i];
  58. if(isPromise(cur)) {
  59. cur.then(data => {
  60. processData(i, data)
  61. }, reject)
  62. } else {
  63. processData(i, cur)
  64. }
  65. }
  66. })
  67. }
  68. /* MyPromise.all([1, 2, 3, read('./a.txt'), read('./b.txt'), 4, 5]).then(data => {
  69. console.log(data)
  70. }) */
  71. // 场景一
  72. /* const promise = new MyPromise((resolve, reject) => {
  73. resolve(123)
  74. }).then((data) => {
  75. console.log('data', data)
  76. }, error => {
  77. console.log('error', error)
  78. }) */
  79. // 场景二
  80. // 可多次调用 promise 实例上的 then 方法。
  81. // 若 executor 里面是一个 setTimeout,则需在 MyPromise 中添加成功、失败回调数组(onFulfilledCallbacks / onRejectedCallbacks)
  82. /* const promise = new MyPromise((resolve, reject) => {
  83. setTimeout(() => {
  84. resolve(123)
  85. }, 2000);
  86. })
  87. promise.then((data) => {
  88. console.log('data', data)
  89. }, error => {
  90. console.log('error', error)
  91. })
  92. promise.then((data) => {
  93. console.log('data', data)
  94. }, error => {
  95. console.log('error', error)
  96. })
  97. promise.then((data) => {
  98. console.log('data', data)
  99. }, error => {
  100. console.log('error', error)
  101. }) */
  102. // 场景三
  103. /*
  104. 若一个 promise 的 then 方法中的函数(成功、失败)返回的结果是一个 promise 的话,会自动将这个 promise 执行,并且采用它的状态,若成功会将成功的结果向外层的下一个 then 传递
  105. */
  106. read('./a.txt').then(data => {
  107. console.log(read('./b.txt'))
  108. return read('./b.txt')
  109. }, err => {
  110. console.log(err)
  111. }).then(data => {
  112. console.log(data)
  113. }, err => {
  114. console.log(err)
  115. return false // 返回一个普通值,会将此普通值作为下一次成功的结果
  116. }).then(data => {
  117. console.log(data)
  118. // !重点:若不希望继续向下再走 then ?
  119. return new MyPromise(() => {}) // 终止 promise ,可返回一个 pending 状态的 promise
  120. }).then(() => {
  121. console.log('success')
  122. },() => {
  123. console.log('err')
  124. })

参考地址

  1. 【2022最新】珠峰-手写Promise A+ 完整版(附源码)
  2. 201905架构课
  3. Babel 官网