Promise

这篇文章写得很具体

JS基础 — 5.4

算法

二分法

:::warning 给数组找个标志位
比如我,所有人高于我的 站我右边,矮于我的,站我左边 :::

快速排序

  1. // 快速排序
  2. // O(n*logn) 时间和空间复杂度
  3. function quickSort(arr) {
  4. if(arr.length < 2) { // 递归终止的条件
  5. return arr
  6. }
  7. let flag = arr[0] // 随机抽一个位置
  8. let left = []
  9. let right = []
  10. for(let i = 1; i < arr.length; i++) {
  11. if(arr[i] > flag) {
  12. right.push(arr[i])
  13. } else {
  14. left.push(arr[i])
  15. }
  16. }
  17. return quickSort(left).concat(flag, quickSort(right))
  18. }

原地快排

  1. // O(1)
  2. i -> <- j
  3. [e, a, b, c, d, f, g, h]
  4. // i 找到一个比 e 大的
  5. // j 找到一个比 e 小的
  6. // i 和 j 的 值 交换一下 位置
  7. // 到最后 i 和 j 遇见
  8. // 不需要使用任何额外的变量
  9. function quick(arr, start, end) {
  10. // 双指针
  11. let init = start
  12. let flag = arr[init]
  13. start++
  14. while(start <= end) {
  15. while(arr[end] > flag) {
  16. end--
  17. }
  18. while(arr[start] < flag) {
  19. start++
  20. }
  21. if(start < end) {
  22. [arr[start], arr[end]] = [arr[end], arr[start]]
  23. start++
  24. end--
  25. }
  26. }
  27. [arr[init], arr[start-1]] = [arr[start-1], arr[init]]
  28. return start
  29. }
  30. function quickSort(arr, start, end) {
  31. if(start < end) {
  32. let index = quick(arr, start, end) // 标志位的值
  33. quickSort(arr, start, index-1)
  34. quickSort(arr, index, end)
  35. }
  36. return arr
  37. }

算法图解 — 递归

第 3 章 递归
《第 3 章 递归》

☑️手写 — 数据处理.5 实现数组的扁平化

1 递归实现

递归思路:通过循环递归的方式,一项一项的去遍历,如果每一项还是一个数组,那么就继续往下遍历。
利用递归程序的方法,实现数组的每一项的连接:

  1. let arr = [1,[2, [3, 4, 5]]]
  2. function flatten(arr) {
  3. let result = [];
  4. for(let i = 0; i < arr.length; i++) {
  5. if(Array.isArray(arr[i])) {
  6. result = result.concat(flatten(arr[i]))
  7. } else {
  8. result.push(arr[i])
  9. }
  10. }
  11. return result
  12. }
  13. flatten(arr) // [1, 2, 3, 4, 5]

JS基础 — 5.2

重新了解 js 必会的基础知识图片.png

算法-冒泡排序

:::warning 基础问题:搜索、排序
排序算法解决的是:如何把一个数组变成有序的
除去高级用法,数组的增删改查操作
冒泡排序(较为常用的方式) :::

  1. // arr.sort((a, b) => a-b )
  2. // 时间复杂度--O(n^2)
  3. function bubbleSort(arr) {
  4. // 每个人和右边的比较,如果更高,就交换位置,否则就不动
  5. let len = arr.length - 1
  6. for(let j = 0; j< len; j++) {
  7. for(let i = 0; i < len - j; i++)
  8. // for(let i = 0, i < len; i++) {
  9. for(arr[i] > arr[i+1]) {
  10. // let tmp = arrp[i]
  11. // arr[i] = arr[i+1]
  12. // arr[i+1] = tmp // 交换位置
  13. [arr[i], arr[i+1]] = [arr[i+1], arr[i]] // 使用解构赋值实现变量交换
  14. }
  15. }
  16. }
  17. }

☑️手写 — 数据处理.4 实现数组元素求和

  1. let arr = [1, 2, 3, 4, 5]
  2. let sum = arr.reduce((total, i) => total += i, 0)
  3. console.log(sum)
  4. let arr = [1, 2, 3, [[4, 5], 6], 7, 8, 9]
  5. let arr1 = arr.toString().split(',').reduce((total, i) => total += Number(i), 0)
  6. console.log(arr)
  7. // 递归实现
  8. let arr = [1, 2, 3, 4, 5]
  9. function add(arr) {
  10. if(arr.lenght === 1) return arr[0]
  11. return arr[0] + add(arr.slice(1))
  12. }
  13. console.log(add(arr))

:::warning 5·1 卷卷计划

1 敲完项目 * 2

  • 🐟的开源项目 * 1
  • b站 cms_blog 项目(5.1)

2 JS 基础

  • 1节

3 手写题

  • 手写题*1

4 算法

  • 《算法图解》* 1章 & 笔记
  • 算法视频*1
  • 算法题*1

5 React 视频课

:::

JS 基础 — 箭头函数 4.30

☑️手写 — 数据处理.3 实现数组的乱序输出

主要的实现思路:
取出数组的第一个元素,随机产生一个索引值,将该第一个元素和这个索引对应的元素进行交换
第二次取出数组第二个元素,随机产生一个除了索引为1的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
按照上面的规律执行,直到遍历完成

  1. let arr = [1,2,3,4,5,6,7,8,9,10]
  2. for(let i = 0; i < arr.length; i++) {
  3. const randomIndex = Math.round(Math.random()*(arr.length - 1 - i))+i;
  4. [arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]
  5. }

倒序遍历

  1. let length = arr.length
  2. randomIndex
  3. temp
  4. while(length) {
  5. randomIndex = Math.floor(Math.random()*length --)
  6. temp = arr[length]
  7. arr[length] = arr[randomIndex]
  8. arr[randomIndex] = temp
  9. }
  10. console.log(arr)

JS 基础 — 函数表达式 4.29


what 函数声明 & 函数表达式 区别

☑️手写 — 数据处理.1 实现日期格式化函数

输入

  1. dateFormat(new Date('2022-04-29'), 'yyyy/MM/dd') // 2022/04/29
  2. dateFormat(new Date('2022-04-29'), 'yyyy年MM月dd日') // 2022/04/29
  1. const dateFormat = (dateInput, format) => {
  2. var day = dateInput.getDate()
  3. var month = dateInput.getMonth() + 1
  4. var year = dateInput.getFullYear()
  5. format = format.replace(/yyyy/, year)
  6. format = format.replace(/MM/, month)
  7. format = format.replace(/dd/, day)
  8. return format
  9. }

☑️手写 — 2 交换 a, b 的值,不能用临时变量

how
利用两个数的和、差

  1. a = a + b
  2. b = a - b
  3. a = a - b

JS基础 — 函数

感悟:重新认识函数 & 对工作的函数命名有了新认知

☑️手写 — 18 实现深拷贝 4.28

what
深拷贝
如果遇到属性值为引用类型的时候,新建一个引用类型并将对应的值复制给它,因此对象获得的一个新的引用类型而不是一个原有类型的引用。
深拷贝对于一些对象可以使用 JSON 的两个函数来实现,但是由于 JSON 的对象格式比 js 的对象格式更加严格,所以如果属性值里出现函数或者 Symbol 类型的值时,会转换失败。
how
1 JSON.stringify()
JSON.parse(JSON.stringify(obj))
原理:利用 JSON.stringify 将 js 对象序列化(JSON 字符串)
再使用 JSON.parse 来反序列化(还原) js 对象

  1. let obj1 = {
  2. a: 0,
  3. b: {
  4. c: 0
  5. }
  6. }
  7. let obj2 = JSON.parse(JSON.stringify(obj1))
  8. obj1.a = 1
  9. obj1.b.c = 1
  10. console.log(obj1) // {a: 1, b: {c: 1}}
  11. console.log(obj2) // {a: 0, b: {c: 0}}

2 lodash 的 _.cloneDeep 方法

why

  1. function deepClone(object) {
  2. if(!object || typeof object !== 'object') return
  3. let newObject = Array.isArray(object) ? [] : {}
  4. for(let key in object) {
  5. if(object.hasOwnProperty(key)) {
  6. newObject[key] =
  7. typeof object[key] === 'object' ? deepClone(object[key]) : object[key]
  8. }
  9. }
  10. return newObject
  11. }

☑️手写 — 17 实现浅拷贝 4.27

what
浅拷贝:
一个新的对象对原始对象的属性值进行精确的拷贝
如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值。
如果是引用数据类型,拷贝的就是内存地址。
如果其中一个对象的引用内存地址发生改变,另一个对象也会发生改变。
how
1 Object.assign
2 扩展运算符 ...
3 数组方法实现数组浅拷贝
1)Array.prototype.slice()
2) Array.prototype.concat()
why

  1. function shallowCopy(object) {
  2. // 只拷贝对象
  3. if(!object || typeof object !== 'object') return
  4. // 根据 object 的类型判断是新建一个数组还是对象
  5. let newObject = Array.isArray(object) ? [] : {}
  6. // 遍历 object, 并且判断是 object 的属性才拷贝
  7. for(let key in object) {
  8. if(object.hasOwnProperty(key)) {
  9. newObject[key] = object[key]
  10. }
  11. }
  12. return newObject
  13. }

JS基础 — 变量对象

https://github.com/mqyqingfeng/Blog/issues/5

☑️手写 — 16 使用 Promise 封装 AJAX 请求

  1. function getJSON(url) {
  2. // 创建一个 promise 对象
  3. let promise = new Promise(function(resolve, reject){
  4. let xhr = new XMLHttpRequest()
  5. // 新建一个 http 请求
  6. xhr.open('GET', url, true)
  7. // 设置状态的监听函数
  8. xhr.onreadystatechange = function() {
  9. if(this.readyState !== 4) return
  10. // 当请求成功或失败时,改变 promise 的状态
  11. if(this.status === 200) {
  12. resolve(this.response)
  13. } else {
  14. reject(new Error(this.statusText))
  15. }
  16. }
  17. // 设置错误监听函数
  18. xhr.onerror = function () {
  19. reject(new Error(this.statusText))
  20. }
  21. // 设置响应的数据类型
  22. xhr.responseType = 'json'
  23. // 设置请求头信息
  24. xhr.setRequestHeader('Accept', 'application/json')
  25. // 发送请求
  26. xhr.send(null)
  27. })
  28. return promise
  29. }

JS 基础 — 闭包 4.25

☑️手写 — 15 实现 AJAX 请求

  1. const SERVER_URL = '/server'
  2. let xhr = new XMLHttpRequest()
  3. // 创建 Http 请求
  4. xhr.open('GET', SERVER_URL, true)
  5. // 设置状态监听函数
  6. xhr.onreadystatechange = function() {
  7. if(this.readyState !== 4) return
  8. // 当请求成功时
  9. if(this.status === 200) {
  10. handle(this.response)
  11. } else {
  12. console.error(this.statusText)
  13. }
  14. };
  15. // 设置请求失败时的监听函数
  16. xhr.onerror = function() {
  17. console.error(this.statusText)
  18. }
  19. // 设置请求头信息
  20. xhr.responseType = 'json'
  21. xhr.setRequestHeader('Accept', 'application/json')
  22. // 发送 Http 请求
  23. xhr.send(null)

ES6 运算符的扩展 4.24 | 252

Learn record - 图2

☑️手写 — 14 函数柯里化的实现

what — 函数柯里化: 将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术

  1. function curry(fn, args) {
  2. // 获取函数需要的参数长度
  3. let length = fn.length
  4. args = args || []
  5. return function() {
  6. let subArgs = args.slice(0)
  7. // 拼接到现有的所有参数
  8. for(let i = 0; i < arguments.length; i++) {
  9. subArgs.push(arguments[i])
  10. }
  11. // 判断参数的长度是否已经满足函数所需参数的长度
  12. if(subArgs.length >= length) {
  13. // 如果满足,执行函数
  14. return fn.apply(this, subArgs)
  15. } else {
  16. // 如果不满足,递归返回柯里化的函数,等待参数的传入
  17. return curry.call(this, fn, subArgs)
  18. }
  19. }
  20. }

ES6 对象新增方法

Learn record - 图3

☑️手写 — 13 手写 bind 函数

  1. Function.prototype.myBind = function(context) {
  2. // 判断调用对象是否为函数
  3. if(typeof this !== 'function') {
  4. throw new TypeError('Error')
  5. }
  6. // 获取参数
  7. var args = [...arguments].slice(1)
  8. fn = this
  9. return function Fn() {
  10. // 根据调用方式,传入不同绑定值
  11. return fn.apply(
  12. this instanceof Fn ? this : context,
  13. args.concat(...arguments)
  14. )
  15. }
  16. }

ES6 对象的扩展 4.21

Learn record - 图4

☑️手写 — 12 手写 apply 函数

  1. Function.prototype.myApp = function(context) {
  2. // 判断调用函数是否为函数
  3. if(typeof this !== 'function') {
  4. throw new TypeError('Error')
  5. }
  6. let result = null
  7. // 判断 context 是否存在,如果未传入则为 window
  8. context = context || window
  9. // 将函数设为对象的方法
  10. context.fn = this
  11. // 调用方法
  12. if(arguments[1]) {
  13. result = context.fn(...arguments[1])
  14. } else {
  15. result = context.fn()
  16. }
  17. // 将属性删除
  18. delete context.fn
  19. return result
  20. }

基础-js 4.20

JavaScript 《JavaScript》

ES6 数组的扩展 4.20

Learn record - 图5

☑️手写 — 11 手写 call 函数

  1. Function.prototype.myCall = function(context) {
  2. // 判断调用对象
  3. if(typeof this !== 'function') {
  4. console.error('type error')
  5. }
  6. // 获取参数
  7. let args = [...arguments].slice(1)
  8. result = null
  9. // 判断 context 是否传入,如果未传入则设置为 window
  10. context = context || window
  11. // 将调用函数设为对象的方法
  12. context.fn = this
  13. // 调用函数
  14. result = context.fn(...args)
  15. // 将属性删除
  16. delete context.fn
  17. return result
  18. }

基础-js 4.19

JavaScript
《JavaScript》

☑️手写 — 10 手写类型判断函数

  1. function getType(value) {
  2. // 判断数据是 null 的情况
  3. if(value === null) {
  4. return value + ''
  5. }
  6. // 判断数据是引用类型的情况
  7. if(typeof value === 'object') {
  8. let valueClass = Object.prototype.toString.call(value) // [object string]
  9. type = valueClass.split('')[1].split('') // Array['o']
  10. type.pop() // 'o'
  11. return type.join('').toLowerCase() // 'o'
  12. } else {
  13. // 判断数据是基本数据类型的情况和函数的情况
  14. return typeof value
  15. }
  16. }

pop

ES6 函数的扩展(4.18 && 4.19)

Learn record - 图6

  1. // 阶乘函数
  2. // O(n)
  3. function factorial(n) {
  4. if(n === 1) return 1
  5. return n * factorial(n - 1)
  6. }
  7. factorial(5)
  8. // 尾递归
  9. // 只保持一个调用记录,O(1)
  10. function factorial(n, total) {
  11. if(n === 1) return total
  12. return factorial(n-1, n * total)
  13. }
  14. factorial(5, 1)

☑️手写 — 9 手写节流函数

what
函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行。
如果在同一个单位时间内某件事被触发多次,只有一次能生效。

how
节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

why

  1. // 函数节流的实现
  2. function throttle(fn, delay) {
  3. let curTime = Date.now()
  4. return function () {
  5. let context = this,
  6. args = arguments,
  7. nowTime = Date.now();
  8. // 如果两次时间间隔超过了指定时间,则执行函数
  9. if(nowTime - curTime >= delay) {
  10. curTime = Date.now()
  11. return fn.apply(context, args)
  12. }
  13. }
  14. }

(4.17)躺平的一天

ES6 数值的扩展(4.16)

Learn record - 图7

☑️手写 — 8 手写防抖函数

what:
函数防抖 是指 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
使用场景:点击请求事件,避免因为用户的多次点击向后端发送多次请求。

why:

  1. // 函数防抖的实现
  2. function debounce(fn, wait) {
  3. let timer = null
  4. return function() {
  5. let context = this
  6. args = arguments
  7. // 如果此时存在定时器,则取消之前的定时器重新计时
  8. if(timer) {
  9. clearTimeout(timer)
  10. timer = null
  11. }
  12. // 设置定时器,使事件间隔指定事件后执行
  13. timer = setTimeout(() => {
  14. fn.apply(context, args)
  15. }, wait)
  16. }
  17. }

ES6 正则的扩展(4.15)

Learn record - 图8

☑️手写 — 3 手写 new 操作符(疑问:new 是用来干啥的)

在调用 new 的过程中会发生以下 4 件事:
1 创建了一个新的空对象
2 设置原型,将对象的原型设置为函数的 prototype 对象
3 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
4 判断函数的返回值类型:
如果是值类型,返回创建的对象。
如果是引用类型,就返回这个引用类型的对象。

  1. function objectFactory() {
  2. let newObject = null
  3. let constructor = Array.prototype.shift.call(arguments)
  4. let result = null
  5. if(typeof constructor !== 'function') {
  6. console.error('type error')
  7. return
  8. }
  9. // 1 新建一个空对象,对象的原型为构造函数的 prototype 对象
  10. newObject = Object.create(constructor.prototype)
  11. // 2 将 this 指向空对象,并执行函数
  12. result = constructor.apply(newObject, arguments)
  13. // 3 判断返回对象
  14. let flag = result && (typeof result === 'object' || typeof result === 'function')
  15. // 4 判断返回结果
  16. return flag ? result : newObject
  17. }
  18. // 使用方法
  19. objectFactory(构造函数,初始化参数)

ES6 字符串的新增方法(4.14)

Learn record - 图9

☑️手写 — 1 手写 Object.create

思路将传入的对象作为原型

  1. function create(obj) {
  2. function F() {}
  3. F.prototype = obj
  4. return new F()
  5. }

☑️手写 — 2 手写 intanceof 方法

instanceof 用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置

实现:
1 获取类型的原型
2 获得对象的原型
3 一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null, 因为原型链最终为 null

  1. function myInstanceof(left, right) {
  2. let proto = Object.getPrototypeOf(left) // 获取对象的原型
  3. prototype = right.prototype // 获取构造函数的 prototype 对象
  4. // 判断构造函数的 prototype 对象是否在对象的原型链上
  5. while(true) {
  6. if(!proto) return false
  7. if(proto === prototype) return true
  8. proto = Object.getPrototypeOf(proto)
  9. }
  10. }

ES6 字符串的扩展(4.13)

Learn record - 图10

☑️手写 — 18实现深拷贝

Learn record - 图11

  1. function deepCopy(object) {
  2. if(!object || typeof object !== 'object') return
  3. let newObject = Array.isArray(object) ? [] : {}
  4. for(let key in object) {
  5. if(object.hasOwnProperty(key)) {
  6. newObject[key] = typeof object[key] === 'object' ? deepCopy(object[key]) : object[key]
  7. }
  8. }
  9. return newObject
  10. }

变量的解构赋值(4.12)

Learn record - 图12

☑️手写 — 17 实现浅拷贝

Learn record - 图13

  1. function shallowCopy(object) {
  2. // 只拷贝对象
  3. if(!object || typeof object !== 'object') return;
  4. // 根据 object 的类型判断是新建一个数组还是对象
  5. let newObject = Array.isArray(object) ? [] : {}
  6. // 遍历 object 并且判断是 object 的属性才拷贝
  7. for(let key in object) {
  8. if(object.hasOwnProperty(key)) {
  9. newObject[key] = object[key]
  10. }
  11. }
  12. return newObject
  13. }

JSON(4.11)

Learn record - 图14
图片.png