http://www.atguigu.com/mst/html/gp/17380.html

5.1. 如何在 JS 中“深冻结”对象

  1. 如果咱们想要确保对象被深冻结,就必须创建一个递归函数来冻结对象类型的每个属性:
    2. 没有深冻结

    1. let person = {
    2. name: "Leonardo",
    3. profession: {
    4. name: "developer"
    5. }
    6. };
    7. Object.freeze(person);
    8. person.profession.name = "doctor";
    9. console.log(person);
    10. //output { name: 'Leonardo', profession: { name: 'doctor' } }

    3.深冻结

    1. function deepFreeze(object) {
    2. let propNames = Object.getOwnPropertyNames(object);
    3. for (let name of propNames) {
    4. let value = object[name];
    5. object[name] = value && typeof value === "object" ? deepFreeze(value) : value;
    6. }
    7. return Object.freeze(object);
    8. }
    9. let person = {
    10. name: "Leonardo",
    11. profession: {
    12. name: "developer"
    13. }
    14. };
    15. deepFreeze(person);
    16. person.profession.name = "doctor"; // TypeError: Cannot assign to read only property

    5.2. 手写call()

    1. /*
    2. 自定义函数对象的call方法
    3. */
    4. export function call (fn, obj, ...args) {
    5. // 如果传入的是null/undefined, this指定为window
    6. if (obj===null || obj===undefined) {
    7. obj = obj || window
    8. }
    9. // 给obj添加一个方法: 属性名任意, 属性值必须当前调用call的函数对象
    10. obj.tempFn = fn
    11. // 通过obj调用这个方法
    12. const result = obj.tempFn(...args)
    13. // 删除新添加的方法
    14. delete obj.tempFn
    15. // 返回函数调用的结果
    16. return result
    17. }

    5.3. 手写apply()

    1. /*
    2. 自定义函数对象的apply方法
    3. */
    4. export function apply (fn, obj, args) {
    5. // 如果传入的是null/undefined, this指定为window
    6. if (obj===null || obj===undefined) {
    7. obj = obj || window
    8. }
    9. // 给obj添加一个方法: 属性名任意, 属性值必须当前调用call的函数对象
    10. obj.tempFn = fn
    11. // 通过obj调用这个方法
    12. const result = obj.tempFn(...args)
    13. // 删除新添加的方法
    14. delete obj.tempFn
    15. // 返回函数调用的结果
    16. return result
    17. }

    5.4. 手写bind()

    1. import {call} from './call'
    2. /*
    3. 自定义函数对象的bind方法
    4. 重要技术:
    5. 高阶函数
    6. 闭包
    7. call()
    8. 三点运算符
    9. */
    10. export function bind (fn, obj, ...args) {
    11. if (obj===null || obj===undefined) {
    12. obj = obj || window
    13. }
    14. return function (...args2) {
    15. call(fn, obj, ...args, ...args2)
    16. }
    17. }

    5.5. 手写一个防抖函数

    1. /*
    2. 实现函数防抖的函数
    3. */
    4. export function debounce(callback, delay) {
    5. return function () {
    6. // console.log('debounce 事件...')
    7. // 保存this和arguments
    8. const that = this
    9. const args = arguments
    10. // 清除待执行的定时器任务
    11. if (callback.timeoutId) {
    12. clearTimeout(callback.timeoutId)
    13. }
    14. // 每隔delay的时间, 启动一个新的延迟定时器, 去准备调用callback
    15. callback.timeoutId = setTimeout(function () {
    16. callback.apply(that, args)
    17. // 如果定时器回调执行了, 删除标记
    18. delete callback.timeoutId
    19. }, delay)
    20. }
    21. }

    5.6. 手写一个节流函数

    1. /*
    2. 实现函数节流的函数
    3. */
    4. export function throttle(callback, delay) {
    5. let start = 0 // 必须保存第一次点击立即调用
    6. return function () {
    7. // 它的this是谁就得让callback()中的this是谁, 它接收的所有实参都直接交给callback()
    8. console.log('throttle 事件')
    9. const current = Date.now()
    10. if (current - start > delay) { // 从第2次点击开始, 需要间隔时间超过delay
    11. callback.apply(this, arguments)
    12. start = current
    13. }
    14. }
    15. }

    5.7. 手写一个深拷贝函数

    ```javascript / 1). 大众乞丐版 问题1: 函数属性会丢失 问题2: 循环引用会出错 / export function deepClone1(target) { return JSON.parse(JSON.stringify(target)) }

/ 获取数据的类型字符串名 / function getType(data) { return Object.prototype.toString.call(data).slice(8, -1) }

/ 2). 面试基础版本 解决问题1: 函数属性还没丢失 / export function deepClone2(target) { const type = getType(target)

if (type===’Object’ || type===’Array’) { const cloneTarget = type === ‘Array’ ? [] : {} for (const key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = deepClone2(target[key]) } } return cloneTarget } else { return target } }

/ 3). 面试加强版本 解决问题2: 循环引用正常 / export function deepClone3(target, map = new Map()) { const type = getType(target) if (type===’Object’ || type===’Array’) { let cloneTarget = map.get(target) if (cloneTarget) { return cloneTarget } cloneTarget = type===’Array’ ? [] : {} map.set(target, cloneTarget) for (const key in target) { if (target.hasOwnProperty(key)) { cloneTarget[key] = deepClone3(target[key], map) } } return cloneTarget } else { return target } }

/ 4). 面试加强版本2(优化遍历性能) 数组: while | for | forEach() 优于 for-in | keys()&forEach() 对象: for-in 与 keys()&forEach() 差不多 / export function deepClone4(target, map = new Map()) { const type = getType(target) if (type===’Object’ || type===’Array’) { let cloneTarget = map.get(target) if (cloneTarget) { return cloneTarget }

  1. if (type==='Array') {
  2. cloneTarget = []
  3. map.set(target, cloneTarget)
  4. target.forEach((item, index) => {
  5. cloneTarget[index] = deepClone4(item, map)
  6. })
  7. } else {
  8. cloneTarget = {}
  9. map.set(target, cloneTarget)
  10. Object.keys(target).forEach(key => {
  11. cloneTarget[key] = deepClone4(target[key], map)
  12. })
  13. }
  14. return cloneTarget

} else { return target } }

  1. <a name="qZiUX"></a>
  2. ## 5.9. 自定义new工具函数
  3. ```javascript
  4. /*
  5. 自定义new工具函数
  6. 语法: newInstance(Fn, ...args)
  7. 功能: 创建Fn构造函数的实例对象
  8. 实现: 创建空对象obj, 调用Fn指定this为obj, 返回obj
  9. */
  10. export function newInstance(Fn, ...args) {
  11. // 创建一个新的对象
  12. const obj = {}
  13. // 执行构造函数
  14. const result = Fn.apply(obj, args) // 相当于: obj.Fn()
  15. // 如果构造函数执行的结果是对象, 返回这个对象
  16. if (result instanceof Object) {
  17. return result
  18. }
  19. // 如果不是, 返回新创建的对象
  20. obj.__proto__.constructor = Fn // 让原型对象的构造器属性指向Fn
  21. return obj
  22. }

5.10. 手写axios函数

  1. /*
  2. 1. 函数的返回值为promise, 成功的结果为response, 失败的结果为error
  3. 2. 能处理多种类型的请求: GET/POST/PUT/DELETE
  4. 3. 函数的参数为一个配置对象
  5. {
  6. url: '', // 请求地址
  7. method: '', // 请求方式GET/POST/PUT/DELETE
  8. params: {}, // GET/DELETE请求的query参数
  9. data: {}, // POST或DELETE请求的请求体参数
  10. }
  11. 4. 响应json数据自动解析为js的对象/数组
  12. */
  13. /* 发送任意类型请求的函数 */
  14. function axios({
  15. url,
  16. method='GET',
  17. params={},
  18. data={}
  19. }) {
  20. // 返回一个promise对象
  21. return new Promise((resolve, reject) => {
  22. // 处理method(转大写)
  23. method = method.toUpperCase()
  24. // 处理query参数(拼接到url上) id=1&xxx=abc
  25. /*
  26. {
  27. id: 1,
  28. xxx: 'abc'
  29. }
  30. */
  31. let queryString = ''
  32. Object.keys(params).forEach(key => {
  33. queryString += `${key}=${params[key]}&`
  34. })
  35. if (queryString) { // id=1&xxx=abc&
  36. // 去除最后的&
  37. queryString = queryString.substring(0, queryString.length-1)
  38. // 接到url
  39. url += '?' + queryString
  40. }
  41. // 1. 执行异步ajax请求
  42. // 创建xhr对象
  43. const request = new XMLHttpRequest()
  44. // 打开连接(初始化请求, 没有请求)
  45. request.open(method, url, true)
  46. // 发送请求
  47. if (method==='GET') {
  48. request.send()
  49. } else if (method==='POST' || method==='PUT' || method==='DELETE'){
  50. request.setRequestHeader('Content-Type', 'application/json;charset=utf-8') // 告诉服务器请求体的格式是json
  51. request.send(JSON.stringify(data)) // 发送json格式请求体参数
  52. }
  53. // 绑定状态改变的监听
  54. request.onreadystatechange = function () {
  55. // 如果请求没有完成, 直接结束
  56. if (request.readyState!==4) {
  57. return
  58. }
  59. // 如果响应状态码在[200, 300)之间代表成功, 否则失败
  60. const {status, statusText} = request
  61. // 2.1. 如果请求成功了, 调用resolve()
  62. if (status>=200 && status<=299) {
  63. // 准备结果数据对象response
  64. const response = {
  65. data: JSON.parse(request.response),
  66. status,
  67. statusText
  68. }
  69. resolve(response)
  70. } else { // 2.2. 如果请求失败了, 调用reject()
  71. reject(new Error('request error status is ' + status))
  72. }
  73. }
  74. })
  75. }
  76. /* 发送特定请求的静态方法 */
  77. axios.get = function (url, options) {
  78. return axios(Object.assign(options, {url, method: 'GET'}))
  79. }
  80. axios.delete = function (url, options) {
  81. return axios(Object.assign(options, {url, method: 'DELETE'}))
  82. }
  83. axios.post = function (url, data, options) {
  84. return axios(Object.assign(options, {url, data, method: 'POST'}))
  85. }
  86. axios.put = function (url, data, options) {
  87. return axios(Object.assign(options, {url, data, method: 'PUT'}))
  88. }
  89. export default axios

5.11. 自定义事件总线

  1. /*
  2. * 自定义事件总线
  3. */
  4. const eventBus = {}
  5. /*
  6. {
  7. add: [callback1, callback2]
  8. delete: [callback3]
  9. }
  10. */
  11. let callbacksObj = {}
  12. /*
  13. 绑定事件监听
  14. */
  15. eventBus.on = function (eventName, callback) {
  16. const callbacks = callbacksObj[eventName]
  17. if (callbacks) {
  18. callbacks.push(callback)
  19. } else {
  20. callbacksObj[eventName] = [callback]
  21. }
  22. }
  23. /*
  24. 分发事件
  25. */
  26. eventBus.emit = function (eventName, data) {
  27. const callbacks = callbacksObj[eventName]
  28. if (callbacks && callbacks.length > 0) {
  29. callbacks.forEach(callback => {
  30. callback(data)
  31. })
  32. }
  33. }
  34. /*
  35. 移除事件监听
  36. */
  37. eventBus.off = function (eventName) {
  38. if (eventName) {
  39. delete callbacksObj[eventName]
  40. } else {
  41. callbacksObj = {}
  42. }
  43. }
  44. export default eventBus

5.12. 自定义消息订阅与发布

  1. /*
  2. 自定义消息订阅与发布
  3. */
  4. const PubSub = {}
  5. /*
  6. {
  7. add: {
  8. token1: callback1,
  9. token2: callback2
  10. },
  11. update: {
  12. token3: callback3
  13. }
  14. }
  15. */
  16. let callbacksObj = {} // 保存所有回调的容器
  17. let id = 0 // 用于生成token的标记
  18. // 1. 订阅消息
  19. PubSub.subscribe = function (msgName, callback) {
  20. // 确定token
  21. const token = 'token_' + ++id
  22. // 取出当前消息对应的callbacks
  23. const callbacks = callbacksObj[msgName]
  24. if (!callbacks) {
  25. callbacksObj[msgName] = {
  26. [token]: callback
  27. }
  28. } else {
  29. callbacks[token] = callback
  30. }
  31. // 返回token
  32. return token
  33. }
  34. // 2. 发布异步的消息
  35. PubSub.publish = function (msgName, data) {
  36. // 取出当前消息对应的callbacks
  37. let callbacks = callbacksObj[msgName]
  38. // 如果有值
  39. if (callbacks) {
  40. // callbacks = Object.assign({}, callbacks)
  41. // 启动定时器, 异步执行所有的回调函数
  42. setTimeout(() => {
  43. Object.values(callbacks).forEach(callback => {
  44. callback(data)
  45. })
  46. }, 0)
  47. }
  48. }
  49. // 3. 发布同步的消息
  50. PubSub.publishSync = function (msgName, data) {
  51. // 取出当前消息对应的callbacks
  52. const callbacks = callbacksObj[msgName]
  53. // 如果有值
  54. if (callbacks) {
  55. // 立即同步执行所有的回调函数
  56. Object.values(callbacks).forEach(callback => {
  57. callback(data)
  58. })
  59. }
  60. }
  61. /*
  62. 4. 取消消息订阅
  63. 1). 没有传值, flag为undefined
  64. 2). 传入token字符串
  65. 3). msgName字符串
  66. */
  67. PubSub.unsubscribe = function (flag) {
  68. // 如果flag没有指定或者为null, 取消所有
  69. if (flag === undefined) {
  70. callbacksObj = {}
  71. } else if (typeof flag === 'string') {
  72. if (flag.indexOf('token_') === 0) { // flag是token
  73. // 找到flag对应的callbacks
  74. const callbacks = Object.values(callbacksObj).find(callbacks => callbacks.hasOwnProperty(flag))
  75. // 如果存在, 删除对应的属性
  76. if (callbacks) {
  77. delete callbacks[flag]
  78. }
  79. } else { // flag是msgName
  80. delete callbacksObj[flag]
  81. }
  82. } else {
  83. throw new Error('如果传入参数, 必须是字符串类型')
  84. }
  85. }
  86. export default PubSub

5.13. 自定义数组声明式系列方法

  1. /*
  2. 实现数组声明式处理系列工具函数
  3. */
  4. /*
  5. 实现map()
  6. */
  7. export function map (array, callback) {
  8. const arr = []
  9. for (let index = 0; index < array.length; index++) {
  10. arr.push(callback(array[index], index))
  11. }
  12. return arr
  13. }
  14. /*
  15. 实现reduce()
  16. */
  17. export function reduce (array, callback, initValue) {
  18. let result = initValue
  19. for (let index = 0; index < array.length; index++) {
  20. // 调用回调函数将返回的结果赋值给result
  21. result = callback(result, array[index], index)
  22. }
  23. return result
  24. }
  25. /*
  26. 实现filter()
  27. */
  28. export function filter(array, callback) {
  29. const arr = []
  30. for (let index = 0; index < array.length; index++) {
  31. if (callback(array[index], index)) {
  32. arr.push(array[index])
  33. }
  34. }
  35. return arr
  36. }
  37. /*
  38. 实现find()
  39. */
  40. export function find (array, callback) {
  41. for (let index = 0; index < array.length; index++) {
  42. if (callback(array[index], index)) {
  43. return array[index]
  44. }
  45. }
  46. return undefined
  47. }
  48. /*
  49. 实现findIndex()
  50. */
  51. export function findIndex (array, callback) {
  52. for (let index = 0; index < array.length; index++) {
  53. if (callback(array[index], index)) {
  54. return index
  55. }
  56. }
  57. return -1
  58. }
  59. /*
  60. 实现every()
  61. */
  62. export function every (array, callback) {
  63. for (let index = 0; index < array.length; index++) {
  64. if (!callback(array[index], index)) { // 只有一个结果为false, 直接返回false
  65. return false
  66. }
  67. }
  68. return true
  69. }
  70. /*
  71. 实现some()
  72. */
  73. export function some (array, callback) {
  74. for (let index = 0; index < array.length; index++) {
  75. if (callback(array[index], index)) { // 只有一个结果为true, 直接返回true
  76. return true
  77. }
  78. }
  79. return false
  80. }
  81. export function test() {
  82. console.log('test()222')
  83. }

5.14. 手写Promise

  1. const PENDING = 'pending' // 初始未确定的状态
  2. const RESOLVED = 'resolved' // 成功的状态
  3. const REJECTED = 'rejected' // 失败的状态
  4. /*
  5. Promise构造函数
  6. */
  7. function Promise(excutor) {
  8. const self = this // Promise的实例对象
  9. self.status = PENDING // 状态属性, 初始值为pending, 代表初始未确定的状态
  10. self.data = undefined // 用来存储结果数据的属性, 初始值为undefined
  11. self.callbacks = [] // {onResolved(){}, onRejected(){}}
  12. /*
  13. 将promise的状态改为成功, 指定成功的value
  14. */
  15. function resolve(value) {
  16. // 如果当前不是pending, 直接结束
  17. if (self.status !== PENDING) return
  18. self.status = RESOLVED // 将状态改为成功
  19. self.data = value // 保存成功的value
  20. // 异步调用所有缓存的待执行成功的回调函数
  21. if (self.callbacks.length > 0) {
  22. // 启动一个延迟时间为0的定时器, 在定时器的回调中执行所有成功的回调
  23. setTimeout(() => {
  24. self.callbacks.forEach(cbsObj => {
  25. cbsObj.onResolved(value)
  26. })
  27. })
  28. }
  29. }
  30. /*
  31. 将promise的状态改为失败, 指定失败的reason
  32. */
  33. function reject(reason) {
  34. // 如果当前不是pending, 直接结束
  35. if (self.status !== PENDING) return
  36. self.status = REJECTED // 将状态改为失败
  37. self.data = reason // 保存reason数据
  38. // 异步调用所有缓存的待执行失败的回调函数
  39. if (self.callbacks.length > 0) {
  40. // 启动一个延迟时间为0的定时器, 在定时器的回调中执行所有失败的回调
  41. setTimeout(() => {
  42. self.callbacks.forEach(cbsObj => {
  43. cbsObj.onRejected(reason)
  44. })
  45. })
  46. }
  47. }
  48. // 调用excutor来启动异步任务
  49. try {
  50. excutor(resolve, reject)
  51. } catch (error) { // 执行器执行出错, 当前promise变为失败
  52. console.log('-----')
  53. reject(error)
  54. }
  55. }
  56. /*
  57. 用来指定成功/失败回调函数的方法
  58. 1). 如果当前promise是resolved, 异步执行成功的回调函数onResolved
  59. 2). 如果当前promise是rejected, 异步执行成功的回调函数onRejected
  60. 3). 如果当前promise是pending, 保存回调函数
  61. 返回一个新的promise对象
  62. 它的结果状态由onResolved或者onRejected执行的结果决定
  63. 2.1). 抛出error ==> 变为rejected, 结果值为error
  64. 2.2). 返回值不是promise ==> 变为resolved, 结果值为返回值
  65. 2.3). 返回值是promise ===> 由这个promise的决定新的promise的结果(成功/失败)
  66. */
  67. Promise.prototype.then = function (onResolved, onRejected) {
  68. const self = this
  69. onResolved = typeof onResolved === 'function' ? onResolved : value => value // 将value向下传递
  70. onRejected = typeof onRejected === 'function' ? onRejected : reason => {
  71. throw reason
  72. } // 将reason向下传递
  73. return new Promise((resolve, reject) => { // 什么时候改变它的状态
  74. /*
  75. 1. 调用指定的回调函数
  76. 2. 根据回调执行结果来更新返回promise的状态
  77. */
  78. function handle(callback) {
  79. try {
  80. const result = callback(self.data)
  81. if (!(result instanceof Promise)) { // 2.2). 返回值不是promise ==> 变为resolved, 结果值为返回值
  82. resolve(result)
  83. } else { // 2.3). 返回值是promise ===> 由这个promise的决定新的promise的结果(成功/失败)
  84. result.then(
  85. value => resolve(value),
  86. reason => reject(reason)
  87. )
  88. // result.then(resolve, reject)
  89. }
  90. } catch (error) { // 2.1). 抛出error ==> 变为rejected, 结果值为error
  91. reject(error)
  92. }
  93. }
  94. if (self.status === RESOLVED) {
  95. setTimeout(() => {
  96. handle(onResolved)
  97. })
  98. } else if (self.status === REJECTED) {
  99. setTimeout(() => {
  100. handle(onRejected)
  101. })
  102. } else { // PENDING
  103. self.callbacks.push({
  104. onResolved(value) {
  105. handle(onResolved)
  106. },
  107. onRejected(reason) {
  108. handle(onRejected)
  109. }
  110. })
  111. }
  112. })
  113. }
  114. /*
  115. 用来指定失败回调函数的方法
  116. catch是then的语法糖
  117. */
  118. Promise.prototype.catch = function (onRejected) {
  119. return this.then(undefined, onRejected)
  120. }
  121. /*
  122. 用来返回一个指定vlaue的成功的promise
  123. value可能是一个一般的值, 也可能是promise对象
  124. */
  125. Promise.resolve = function (value) {
  126. return new Promise((resolve, reject) => {
  127. // 如果value是一个promise, 最终返回的promise的结果由value决定
  128. if (value instanceof Promise) {
  129. value.then(resolve, reject)
  130. } else { // value不是promise, 返回的是成功的promise, 成功的值就是value
  131. resolve(value)
  132. }
  133. })
  134. }
  135. /*
  136. 用来返回一个指定reason的失败的promise
  137. */
  138. Promise.reject = function (reason) {
  139. return new Promise((resolve, reject) => {
  140. reject(reason)
  141. })
  142. }
  143. /*
  144. 返回一个promise, 只有当数组中所有promise都成功才成功, 否则失败
  145. */
  146. Promise.all = function (promises) {
  147. return new Promise((resolve, reject) => {
  148. let resolvedCount = 0 // 已经成功的数量
  149. const values = new Array(promises.length) // 用来保存成功promise的value值
  150. // 遍历所有promise, 取其对应的结果
  151. promises.forEach((p, index) => {
  152. p.then(
  153. value => {
  154. resolvedCount++
  155. values[index] = value
  156. if (resolvedCount === promises.length) { // 都成功了
  157. resolve(values)
  158. }
  159. },
  160. reason => reject(reason)
  161. )
  162. })
  163. })
  164. }
  165. /*
  166. 返回一个promise, 由第一个完成promise决定
  167. */
  168. Promise.race = function (promises) {
  169. return new Promise((resolve, reject) => {
  170. // 遍历所有promise, 取其对应的结果
  171. promises.forEach(p => {
  172. // 返回的promise由第一个完成p来决定其结果
  173. p.then(resolve, reject)
  174. })
  175. })
  176. }
  177. /*
  178. 返回一个延迟指定时间才成功(也可能失败)的promise
  179. */
  180. Promise.resolveDelay = function (value, time) {
  181. return new Promise((resolve, reject) => {
  182. setTimeout(() => {
  183. // 如果value是一个promise, 最终返回的promise的结果由value决定
  184. if (value instanceof Promise) {
  185. value.then(resolve, reject)
  186. } else { // value不是promise, 返回的是成功的promise, 成功的值就是value
  187. resolve(value)
  188. }
  189. }, time)
  190. })
  191. }
  192. /*
  193. 返回一个延迟指定时间才失败的promise
  194. */
  195. Promise.rejectDelay = function (reason, time) {
  196. return new Promise((resolve, reject) => {
  197. setTimeout(() => {
  198. reject(reason)
  199. }, time)
  200. })
  201. }
  202. export default Promise