- Promise
- JS基础 — 5.4
- 算法
- ☑️手写 — 数据处理.5 实现数组的扁平化
- JS基础 — 5.2
- 算法-冒泡排序
- ☑️手写 — 数据处理.4 实现数组元素求和
- JS 基础 — 箭头函数 4.30
- ☑️手写 — 数据处理.3 实现数组的乱序输出
- JS 基础 — 函数表达式 4.29
- ☑️手写 — 数据处理.1 实现日期格式化函数
- ☑️手写 — 2 交换 a, b 的值,不能用临时变量
- JS基础 — 函数
- ☑️手写 — 18 实现深拷贝 4.28
- ☑️手写 — 17 实现浅拷贝 4.27
- JS基础 — 变量对象
- ☑️手写 — 16 使用 Promise 封装 AJAX 请求
- JS 基础 — 闭包 4.25
- ☑️手写 — 15 实现 AJAX 请求
- ES6 运算符的扩展 4.24 | 252
- ☑️手写 — 14 函数柯里化的实现
- ES6 对象新增方法
- ☑️手写 — 13 手写 bind 函数
- ES6 对象的扩展 4.21
- ☑️手写 — 12 手写 apply 函数
- 基础-js 4.20
- ES6 数组的扩展 4.20
- ☑️手写 — 11 手写 call 函数
- 基础-js 4.19
- ☑️手写 — 10 手写类型判断函数
- ES6 函数的扩展(4.18 && 4.19)
- ☑️手写 — 9 手写节流函数
- (4.17)躺平的一天
- ES6 数值的扩展(4.16)
- ☑️手写 — 8 手写防抖函数
- ES6 正则的扩展(4.15)
- ☑️手写 — 3 手写 new 操作符(疑问:new 是用来干啥的)
- ES6 字符串的新增方法(4.14)
- ☑️手写 — 1 手写 Object.create
- ☑️手写 — 2 手写 intanceof 方法
- ES6 字符串的扩展(4.13)
- ☑️手写 — 18实现深拷贝
- 变量的解构赋值(4.12)
- ☑️手写 — 17 实现浅拷贝
- JSON(4.11)
Promise
这篇文章写得很具体
JS基础 — 5.4
算法
二分法
:::warning
给数组找个标志位
比如我,所有人高于我的 站我右边,矮于我的,站我左边
:::
快速排序
// 快速排序// O(n*logn) 时间和空间复杂度function quickSort(arr) {if(arr.length < 2) { // 递归终止的条件return arr}let flag = arr[0] // 随机抽一个位置let left = []let right = []for(let i = 1; i < arr.length; i++) {if(arr[i] > flag) {right.push(arr[i])} else {left.push(arr[i])}}return quickSort(left).concat(flag, quickSort(right))}
原地快排
// O(1)i -> <- j[e, a, b, c, d, f, g, h]// i 找到一个比 e 大的// j 找到一个比 e 小的// i 和 j 的 值 交换一下 位置// 到最后 i 和 j 遇见// 不需要使用任何额外的变量function quick(arr, start, end) {// 双指针let init = startlet flag = arr[init]start++while(start <= end) {while(arr[end] > flag) {end--}while(arr[start] < flag) {start++}if(start < end) {[arr[start], arr[end]] = [arr[end], arr[start]]start++end--}}[arr[init], arr[start-1]] = [arr[start-1], arr[init]]return start}function quickSort(arr, start, end) {if(start < end) {let index = quick(arr, start, end) // 标志位的值quickSort(arr, start, index-1)quickSort(arr, index, end)}return arr}
算法图解 — 递归
第 3 章 递归
《第 3 章 递归》
☑️手写 — 数据处理.5 实现数组的扁平化
1 递归实现
递归思路:通过循环递归的方式,一项一项的去遍历,如果每一项还是一个数组,那么就继续往下遍历。
利用递归程序的方法,实现数组的每一项的连接:
let arr = [1,[2, [3, 4, 5]]]function flatten(arr) {let result = [];for(let i = 0; i < arr.length; i++) {if(Array.isArray(arr[i])) {result = result.concat(flatten(arr[i]))} else {result.push(arr[i])}}return result}flatten(arr) // [1, 2, 3, 4, 5]
JS基础 — 5.2
算法-冒泡排序
:::warning
基础问题:搜索、排序
排序算法解决的是:如何把一个数组变成有序的
除去高级用法,数组的增删改查操作
冒泡排序(较为常用的方式)
:::
// arr.sort((a, b) => a-b )// 时间复杂度--O(n^2)function bubbleSort(arr) {// 每个人和右边的比较,如果更高,就交换位置,否则就不动let len = arr.length - 1for(let j = 0; j< len; j++) {for(let i = 0; i < len - j; i++)// for(let i = 0, i < len; i++) {for(arr[i] > arr[i+1]) {// let tmp = arrp[i]// arr[i] = arr[i+1]// arr[i+1] = tmp // 交换位置[arr[i], arr[i+1]] = [arr[i+1], arr[i]] // 使用解构赋值实现变量交换}}}}
☑️手写 — 数据处理.4 实现数组元素求和
let arr = [1, 2, 3, 4, 5]let sum = arr.reduce((total, i) => total += i, 0)console.log(sum)let arr = [1, 2, 3, [[4, 5], 6], 7, 8, 9]let arr1 = arr.toString().split(',').reduce((total, i) => total += Number(i), 0)console.log(arr)// 递归实现let arr = [1, 2, 3, 4, 5]function add(arr) {if(arr.lenght === 1) return arr[0]return arr[0] + add(arr.slice(1))}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的之外的索引值,并将第二个元素与该索引值对应的元素进行交换
按照上面的规律执行,直到遍历完成
let arr = [1,2,3,4,5,6,7,8,9,10]for(let i = 0; i < arr.length; i++) {const randomIndex = Math.round(Math.random()*(arr.length - 1 - i))+i;[arr[i], arr[randomIndex]] = [arr[randomIndex], arr[i]]}
倒序遍历
let length = arr.lengthrandomIndextempwhile(length) {randomIndex = Math.floor(Math.random()*length --)temp = arr[length]arr[length] = arr[randomIndex]arr[randomIndex] = temp}console.log(arr)
JS 基础 — 函数表达式 4.29
what 函数声明 & 函数表达式 区别
☑️手写 — 数据处理.1 实现日期格式化函数
输入
dateFormat(new Date('2022-04-29'), 'yyyy/MM/dd') // 2022/04/29dateFormat(new Date('2022-04-29'), 'yyyy年MM月dd日') // 2022/04/29
const dateFormat = (dateInput, format) => {var day = dateInput.getDate()var month = dateInput.getMonth() + 1var year = dateInput.getFullYear()format = format.replace(/yyyy/, year)format = format.replace(/MM/, month)format = format.replace(/dd/, day)return format}
☑️手写 — 2 交换 a, b 的值,不能用临时变量
how
利用两个数的和、差
a = a + bb = a - ba = 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 对象
let obj1 = {a: 0,b: {c: 0}}let obj2 = JSON.parse(JSON.stringify(obj1))obj1.a = 1obj1.b.c = 1console.log(obj1) // {a: 1, b: {c: 1}}console.log(obj2) // {a: 0, b: {c: 0}}
2 lodash 的 _.cloneDeep 方法
why
function deepClone(object) {if(!object || typeof object !== 'object') returnlet newObject = Array.isArray(object) ? [] : {}for(let key in object) {if(object.hasOwnProperty(key)) {newObject[key] =typeof object[key] === 'object' ? deepClone(object[key]) : object[key]}}return newObject}
☑️手写 — 17 实现浅拷贝 4.27
what
浅拷贝:
一个新的对象对原始对象的属性值进行精确的拷贝
如果拷贝的是基本数据类型,拷贝的就是基本数据类型的值。
如果是引用数据类型,拷贝的就是内存地址。
如果其中一个对象的引用内存地址发生改变,另一个对象也会发生改变。
how
1 Object.assign
2 扩展运算符 ...
3 数组方法实现数组浅拷贝
1)Array.prototype.slice()
2) Array.prototype.concat()
why
function shallowCopy(object) {// 只拷贝对象if(!object || typeof object !== 'object') return// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {}// 遍历 object, 并且判断是 object 的属性才拷贝for(let key in object) {if(object.hasOwnProperty(key)) {newObject[key] = object[key]}}return newObject}
JS基础 — 变量对象
https://github.com/mqyqingfeng/Blog/issues/5
☑️手写 — 16 使用 Promise 封装 AJAX 请求
function getJSON(url) {// 创建一个 promise 对象let promise = new Promise(function(resolve, reject){let xhr = new XMLHttpRequest()// 新建一个 http 请求xhr.open('GET', url, true)// 设置状态的监听函数xhr.onreadystatechange = function() {if(this.readyState !== 4) return// 当请求成功或失败时,改变 promise 的状态if(this.status === 200) {resolve(this.response)} else {reject(new Error(this.statusText))}}// 设置错误监听函数xhr.onerror = function () {reject(new Error(this.statusText))}// 设置响应的数据类型xhr.responseType = 'json'// 设置请求头信息xhr.setRequestHeader('Accept', 'application/json')// 发送请求xhr.send(null)})return promise}
JS 基础 — 闭包 4.25
☑️手写 — 15 实现 AJAX 请求
const SERVER_URL = '/server'let xhr = new XMLHttpRequest()// 创建 Http 请求xhr.open('GET', SERVER_URL, true)// 设置状态监听函数xhr.onreadystatechange = function() {if(this.readyState !== 4) return// 当请求成功时if(this.status === 200) {handle(this.response)} else {console.error(this.statusText)}};// 设置请求失败时的监听函数xhr.onerror = function() {console.error(this.statusText)}// 设置请求头信息xhr.responseType = 'json'xhr.setRequestHeader('Accept', 'application/json')// 发送 Http 请求xhr.send(null)
ES6 运算符的扩展 4.24 | 252

☑️手写 — 14 函数柯里化的实现
what — 函数柯里化: 将使用多个参数的一个函数转换成一系列使用一个参数的函数的技术
function curry(fn, args) {// 获取函数需要的参数长度let length = fn.lengthargs = args || []return function() {let subArgs = args.slice(0)// 拼接到现有的所有参数for(let i = 0; i < arguments.length; i++) {subArgs.push(arguments[i])}// 判断参数的长度是否已经满足函数所需参数的长度if(subArgs.length >= length) {// 如果满足,执行函数return fn.apply(this, subArgs)} else {// 如果不满足,递归返回柯里化的函数,等待参数的传入return curry.call(this, fn, subArgs)}}}
ES6 对象新增方法

☑️手写 — 13 手写 bind 函数
Function.prototype.myBind = function(context) {// 判断调用对象是否为函数if(typeof this !== 'function') {throw new TypeError('Error')}// 获取参数var args = [...arguments].slice(1)fn = thisreturn function Fn() {// 根据调用方式,传入不同绑定值return fn.apply(this instanceof Fn ? this : context,args.concat(...arguments))}}
ES6 对象的扩展 4.21

☑️手写 — 12 手写 apply 函数
Function.prototype.myApp = function(context) {// 判断调用函数是否为函数if(typeof this !== 'function') {throw new TypeError('Error')}let result = null// 判断 context 是否存在,如果未传入则为 windowcontext = context || window// 将函数设为对象的方法context.fn = this// 调用方法if(arguments[1]) {result = context.fn(...arguments[1])} else {result = context.fn()}// 将属性删除delete context.fnreturn result}
基础-js 4.20
JavaScript 《JavaScript》
ES6 数组的扩展 4.20

☑️手写 — 11 手写 call 函数
Function.prototype.myCall = function(context) {// 判断调用对象if(typeof this !== 'function') {console.error('type error')}// 获取参数let args = [...arguments].slice(1)result = null// 判断 context 是否传入,如果未传入则设置为 windowcontext = context || window// 将调用函数设为对象的方法context.fn = this// 调用函数result = context.fn(...args)// 将属性删除delete context.fnreturn result}
基础-js 4.19
JavaScript
《JavaScript》
☑️手写 — 10 手写类型判断函数
function getType(value) {// 判断数据是 null 的情况if(value === null) {return value + ''}// 判断数据是引用类型的情况if(typeof value === 'object') {let valueClass = Object.prototype.toString.call(value) // [object string]type = valueClass.split('')[1].split('') // Array['o']type.pop() // 'o'return type.join('').toLowerCase() // 'o'} else {// 判断数据是基本数据类型的情况和函数的情况return typeof value}}
ES6 函数的扩展(4.18 && 4.19)

// 阶乘函数// O(n)function factorial(n) {if(n === 1) return 1return n * factorial(n - 1)}factorial(5)// 尾递归// 只保持一个调用记录,O(1)function factorial(n, total) {if(n === 1) return totalreturn factorial(n-1, n * total)}factorial(5, 1)
☑️手写 — 9 手写节流函数
what
函数节流是指规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行。
如果在同一个单位时间内某件事被触发多次,只有一次能生效。
how
节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。
why
// 函数节流的实现function throttle(fn, delay) {let curTime = Date.now()return function () {let context = this,args = arguments,nowTime = Date.now();// 如果两次时间间隔超过了指定时间,则执行函数if(nowTime - curTime >= delay) {curTime = Date.now()return fn.apply(context, args)}}}
(4.17)躺平的一天
ES6 数值的扩展(4.16)

☑️手写 — 8 手写防抖函数
what:
函数防抖 是指 在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。
使用场景:点击请求事件,避免因为用户的多次点击向后端发送多次请求。
why:
// 函数防抖的实现function debounce(fn, wait) {let timer = nullreturn function() {let context = thisargs = arguments// 如果此时存在定时器,则取消之前的定时器重新计时if(timer) {clearTimeout(timer)timer = null}// 设置定时器,使事件间隔指定事件后执行timer = setTimeout(() => {fn.apply(context, args)}, wait)}}
ES6 正则的扩展(4.15)

☑️手写 — 3 手写 new 操作符(疑问:new 是用来干啥的)
在调用 new 的过程中会发生以下 4 件事:
1 创建了一个新的空对象
2 设置原型,将对象的原型设置为函数的 prototype 对象
3 让函数的 this 指向这个对象,执行构造函数的代码(为这个新对象添加属性)
4 判断函数的返回值类型:
如果是值类型,返回创建的对象。
如果是引用类型,就返回这个引用类型的对象。
function objectFactory() {let newObject = nulllet constructor = Array.prototype.shift.call(arguments)let result = nullif(typeof constructor !== 'function') {console.error('type error')return}// 1 新建一个空对象,对象的原型为构造函数的 prototype 对象newObject = Object.create(constructor.prototype)// 2 将 this 指向空对象,并执行函数result = constructor.apply(newObject, arguments)// 3 判断返回对象let flag = result && (typeof result === 'object' || typeof result === 'function')// 4 判断返回结果return flag ? result : newObject}// 使用方法objectFactory(构造函数,初始化参数)
ES6 字符串的新增方法(4.14)

☑️手写 — 1 手写 Object.create
思路将传入的对象作为原型
function create(obj) {function F() {}F.prototype = objreturn new F()}
☑️手写 — 2 手写 intanceof 方法
instanceof 用于判断构造函数的 prototype 属性是否出现在对象的原型链中的任何位置
实现:
1 获取类型的原型
2 获得对象的原型
3 一直循环判断对象的原型是否等于类型的原型,直到对象原型为 null, 因为原型链最终为 null
function myInstanceof(left, right) {let proto = Object.getPrototypeOf(left) // 获取对象的原型prototype = right.prototype // 获取构造函数的 prototype 对象// 判断构造函数的 prototype 对象是否在对象的原型链上while(true) {if(!proto) return falseif(proto === prototype) return trueproto = Object.getPrototypeOf(proto)}}
ES6 字符串的扩展(4.13)
☑️手写 — 18实现深拷贝

function deepCopy(object) {if(!object || typeof object !== 'object') returnlet newObject = Array.isArray(object) ? [] : {}for(let key in object) {if(object.hasOwnProperty(key)) {newObject[key] = typeof object[key] === 'object' ? deepCopy(object[key]) : object[key]}}return newObject}
变量的解构赋值(4.12)

☑️手写 — 17 实现浅拷贝

function shallowCopy(object) {// 只拷贝对象if(!object || typeof object !== 'object') return;// 根据 object 的类型判断是新建一个数组还是对象let newObject = Array.isArray(object) ? [] : {}// 遍历 object 并且判断是 object 的属性才拷贝for(let key in object) {if(object.hasOwnProperty(key)) {newObject[key] = object[key]}}return newObject}
JSON(4.11)


