LTN做题

Posted by CodingWithAlice on October 1, 2024

LTN做题

LTN ①③ - ①⑨记录

工具推荐周期:3.17-3.28 35错题/96题 10天 LTN1-39题,LTN2-21题,LTN3-19题,LTN4-11题,LTN5-0题

LTN1 【推荐做题时间 03-17 - 10题 实际做题时间: 3.17】 ✅作业36:多分支项目的大致情况 ✅作业37:大盘的大致情况 ✅2021-04 01 浏览器进程组成 + 复用渲染进程 + 进程/线程 ✅作业38:部署两个项目的大致情况 ❌2025.02 TS - dayjs 引入但失效问题 ❌2025.02 docker-compose 的配置管理 context ❌2022-10 TS 泛型函数/泛型接口/泛型约束、特性、type/keyof/enum/as/Partial ❌2025-03 浏览器前端性能优化RAIL ❌2025-03 js执行优先级 + 测试题 ❌2025-03 TCP 滑动窗口 VS TCP 排队

LTN1 【推荐做题时间 03-19 - 10题 实际做题时间: 3.18 + 3.19】 ✅作业20:浏览器打开一个 URL 发生了什么 ✅2021.09 Object属性排序、与Map的区别 ✅2021.07 防抖节流 ✅作业80:对DOM 树的理解 - 定义和作用 ❌作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ✅作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别? ❌作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更? -✅2021.06 flex布局 ❌2025-03 服务器 不明文密码处理 ❌2023-09 二叉树:前中后序、层序遍历-递归/非递归版本、层序遍历、路径和

LTN2 【推荐做题时间 03-19 - 3题 实际做题时间: 3.19】 ✅2021.07 babel 作用+原理+补丁 ✅2021.05 27 HTTP/3 改进的点 ❌2021.04 12 垃圾回收机制

LTN4 【推荐做题时间 03-19 - 3题 实际做题时间: 3.19 + 3.20】 ✅2021-06 CSS性能优化 -✅2019-06 第一章-第三章 简介、基本概念 小结 -✅2024-11 第十八章 动画与Canvas图形

LTN1 【推荐做题时间 03-20 - 6题 实际做题时间: 3.20】 ✅作业23:写出js 代码的执行顺序。词法作用域的特点是什么?

function bar(){
    console.log(myName);
}
function foo(){
    var myName = 'hi 坑';
    bar();
}
var myName = 'bye 坑';
foo();

var name = "global name";
var obj = {
    name: 'Alice',
    getName: function() { console.log(this.name) }
}
var getName = obj.getName;
getName();


(function(){
    try{
        console.log(1, a);
        var a="a";
        console.log(a);
        b();
        c();
        function b(){
            console.log("b");
        }
        var c=function(){
            console.log("c");
        }
        console.log("d") // 没有被执行
    }catch(e){
        console.log('err')
    }
})()

✅作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 -✅2024.12 模拟实现promise -✅2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程 -✅2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码 ❌2025.02 Express VS KOA2

LTN4 【推荐做题时间 03-20 - 3题 实际做题时间: 3.20】 ❌2021-07 给DOM元素绑定事件 ✅2021-04 实现给定时间切换状态 ❌2024-11 数组乱序 洗牌算法 从最后一个元素开始,从数组中随机选出一个位置,交换,直到第一个元素。

LTN1 【推荐做题时间 03-21 - 3题 实际做题时间: 3.21】 ❌2025-3 NextJS 全局中间件-校验权限 ❌2025-3 NestJS 守卫-校验权限 -❌2025-03 p-limit 使用+源码 、100个请求并发请求、分时操作

LTN3 【推荐做题时间 03-21 - 1题 实际做题时间: 3.21】 ❌2021.07 数组扁平化(一层、全部展开、指定深度)

LTN1 【推荐做题时间 03-22 - 2题 实际做题时间: 3.22】 -✅作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ✅作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法。写个POST调用流程

LTN3 【推荐做题时间 03-22 - 6题 实际做题时间: 3.22】 ✅作业6:new操作符实例化一个对象的过程 ✅作业25:简单写下 请求创建过程 ❌作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? ✅作业55:flex 常见缩写 -✅作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ✅作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅作业76:显卡的作用?

LTN1 【推荐做题时间 03-23 - 8题 实际做题时间: 3.23】 ✅作业17:对斐波那切数列进行尾调用优化 ✅作业70:Vue 和 React 的 Diff 算法比较 ✅2024.10 怎么实现跨域 ✅2019.10 两栏,左边固定,右边自适应的布局 ✅作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置 ✅作业51: 如何停止过度思考?如何停止焦虑 ✅作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ✅69 x 的平方根

LTN4 【推荐做题时间 03-23 - 5题 实际做题时间: 3.23】 ✅作业49:牛顿三大定律、热力学两大定律 ✅作业54:举杯邀明月,对影成三人。的上一句 ✅作业77:重绘和重排是什么?有什么区别? ❌作业79:地球四季的成因是什么? ✅2021.04 25 HTTP/2 特性

LTN2 【推荐做题时间 03-24 - 10题 实际做题时间: 3.24】 -❌作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅作业61:常见的数据结构有哪些 ✅2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝? ❌作业93:闭包的作用和原理 ✅2021-07 前端路由的两种模式 -❌作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ❌2025.02 Mysql 8.0 以上,Sequel报插件错 ❌2025.02 Nextjs 获取链接中的参数 ❌2025.02 sequelize 在 Nestjs 中的 env 配置

LTN2 【推荐做题时间 03-26 - 4题 实际做题时间: 3.26】 ❌作业66:React如何处理错误 ✅2021.06 跨页面通信 ✅2019.07 数组去重(算法整理) ✅2021.07 柯里化函数 / 反柯里化函数实现

LTN3 【推荐做题时间 03-26 - 6题 实际做题时间: 3.26】 ✅作业58:React和Vue是怎么描述UI的 ✅作业90:金玉满堂是什么意思?在花卉中的说法是什么? ✅作业91:三次握手的过程和具体包的作用 ✅2021.07 事件流 + 事件模型 ✅2021.06 链表 141. 环形链表 ✅2021.09 get和post有什么区别

LTN2 【推荐做题时间 03-27 - 4题 实际做题时间: 3.27】 -✅作业64:Vue渲染怎么实现的? ❌2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现? ✅2024.10 第九章 代理与反射 小结 ❌2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序

LTN3 【推荐做题时间 03-27 - 6题 实际做题时间: 3.27】 ❌作业62:fiber架构是什么?优点和实现方式? -✅作业67:React 怎么做的性能优化? ❌作业68:React hooks 的作用和原理是什么?useEffect useState模拟实现 -❌2020.07 对象分类、Promise按序执行、实现map ✅2024.11 第十七章 事件 ❌2021-07 常见Webpack问题

LTN5 【推荐做题时间 03-27 - 6题 实际做题时间: 3.27】 ✅作业16(1):箭头函数和普通函数的区别是什么?(3点) ✅2019.07 盒模型有哪些?有什么区别? ✅作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器 ✅作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接? ❌作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义? ✅2019.07 隐式转换:{}+{}=?


做题记录

// 2019.07 隐式转换:{}+{}=?
- 第三遍 2025.3.27 -
'[object Object][object Object]'
- 第二遍 2025.2.20 -
- 第一遍 2025.1.8  -
// 作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义?
- 第三遍 2025.3.27 -
Expires: deadline时间
Cache-Control: max-age=2000; 优先级高
HttpOnly 在响应头中设置 cookie 的属性值只有 Http 协议能够访问该属性用于解决 XSS 攻击 // ❌ 1、浏览器禁用 js 访问 cookie,例如 document.cookie
- 第二遍 2025.2.20 -
- 第一遍 2025.1.8  -
// 作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接?
- 第三遍 2025.3.27 -
TCP 建立连接后不会马上关闭 // ⭐️ 一个连接可以处理多个响应
Connection: keep-alive
HTTP/1.1 开始使用长连接
- 第二遍 2025.2.20 -
- 第一遍 2025.1.8  -
// 作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器
- 第三遍 2025.3.27 -
Proxy 的原型为 undefined所以使用 instanceOf 查询报错
const { proxy, revoke } = Proxy.revocable(target, handler);
rovoke() 不可撤销
const handler = {
    get(...arg) { Reflect.get(...arg) },
    set/has - 对应in/apply - 执行/constructor - new创建
}
- 第二遍 2025.2.20 -
- 第一遍 2025.1.8  -
// 2019.07 盒模型有哪些?有什么区别?
- 第三遍 2025.3.27 -
W3C标准盒内容盒子width 表示 内容宽度不包含 borderpadding
box-sizing: content-box;
width=内容宽度+border+padding // ⭐️ IE 盒模型
box-sizing: border-box;
- 第二遍 2025.2.20 -
- 第一遍 2025.1.20 -
// 作业16(1):箭头函数和普通函数的区别是什么?(3点)
- 第三遍 2025.3.27 -
箭头函数内部不能使用 arguments new.target super 不能用作构造函数
没有 prototype
this 在定义时确定不在运行时
// ⭐️ 设计初衷是简化函数表达式并固化 this,因此牺牲了这些特性
- 第二遍 2025.2.20 -
- 第一遍 2025.1.20 -
// 2021-07 常见Webpack问题
- 第六遍 2025.3.27 -
1webpack的几个核心概念理解
Chunk - 根据 entry 入口配置由多个模块组成Module - 万物皆模块plugin - 插件构建过程中在指定时机注入代码
2常见配置项
entry: 'main.js'/{main:'main.js', sub:'sub.js'} // ⭐️  entry 不是 [] 是 {}
output: {filename: '[name]-[hash].js', path: path.resolve(_dirname, 'dist')}
module: { // ⭐️ 配置模块解析、转换规则
	 rules: [{ test:/.\js/, use: ['my-loader'] }, 
            { test:/.\css/, use: ['style-loader', 'sass-loader', 'css-loader'] }] // ❌ 1、应该是先 sass-loader 将 sass 解析成原生 css,再 css-loader 进行处理模块和依赖,例如解析import\url 路径,转换为 js 模块
}
resolve: {
   	alias:{'@c': '/src/my/component'},
	extension: ['json', 'ts', 'js'], // ❌ 2、文件扩展名 extensions
	modules: ['node_modules', path.resolve(_dirname, 'src/myC')]
}
自定义 loader 怎么配置
- resolve.modules 配置解析模块的导入路径添加路径 +  rules  use 中直接使用
-  rules  use 直接使用 use:[path.resolve(_dirname, 'src/myC/my-loader')]
3Code Splitting  Tree Shaking 的区别怎么开启配置懒加载怎么实现
Code Splitting 代码分割根据 entry 入口加载时只需要加载当前页面需要的模块按需加载提高加载效率splitChunk 插件配置开启
Tree Shaking 按需打包减小包体积根据 entry 入口打包时只打包相关代码默认开启
懒加载利用 Code Splitting 在首次加载页面的时候只加载需要的模块提高加载效率
4html-webpack-plugin 作用
 webpack 打包结束后的 js/css 等静态文件嵌入 html 文件中可以直接渲染
5sourceMap不同环境的区别怎么开启配置
devtool:eval-cheap-source-map; 
devtool:hidden-source-map; 线上默认关闭
6热更新怎么实现
 启动 webpack在内存中启动一个服务 dev-server并注入 HMR 代码和浏览器使用 Websocket 双向实时通讯
 有代码更新后监听 done 事件将打包后的结果 mainfest.json  chunkId.js将配置文件 json 主动推送给浏览器浏览器拿到配置文件通过 ajax 获取模块代码
 调用 webpackHotUpdate 更新对应模块 -> hotApply 替换模块代码最后 webpack.__require__ 执行
7webpack原理/执行过程
整合配置 shell 脚本的配置参数和配置文件整合 // ⭐️ 初始化参数
利用配置初始化 Webpack  Compiler引入所有插件执行 .run 开始构建流程
根据 entry 依赖关系遍历所有文件依次调用 loader 得到转换后的文件和依赖关系 // ⭐️ 串行调用
得到文件和依赖关系输出到下载文件列表 // ❌ 3、先再按照 entry 组合成一个个 Chunk
按照 output 配置文件输出到指定位置
开发插件的桥梁
Compiler 代表 webpack 的生命周期唯一实例
Complication 代表一次编译修改一次代码就出发一次创建实例
- 第五遍 2025.3.6 -
- 第四遍 2025.2.21 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.14 -
 // 1、entry: 配置方式记错 - 不识别 path 这个属性
 // 2、resolve 的作用不是解析,是「依赖查找配置规则」
 // 3、devtool:cheap-eval-source-map; (不提供列信息)方便代码调试
 // 4、 监听到 compiler 的 done 事件 得到的文件是 chunk.js 文件,浏览器从 mainfest.json 文件中得到了 ChunkId,获取 chunk.js
 // 5、window.webpackHotUpdate -> hotApply 热更新函数,替换原有模块代码
- 第一遍 2024.12.26 -
// 2020.07 对象分类、Promise按序执行、实现map
- 第八遍 2025.3.27 -
对象分类 
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]) {
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre
    }, {})
}
Promise按序执行
function list(promises, init){
    return promises.reduce((pre, cur) => pre.then(cur), Promise.resolve(init))
}
实现map
Function.prototype.fakeMap = function(cb, thisArg) { // ❌ 1、不是 Function,是 Array
    // ❌ 2、数组 arr 其实应该是 this,调用这个 fakeMap 的对象
    return arr.reduce((pre, cur, index, array) => {
        const res = cb.call(thisArg, cur, index, array);
        pre.push(res); // ❌ 3、使用 pre[index] = res -> reduce 会默认跳过 empty,index 就对不上了
        return pre
    }, [])
}
- 第七遍 2025.3.6 -
- 第六遍 2025.2.21 -
- 第五遍 2025.2.18 -
 // 1、classify分类函数:reduce 的初始值应该是 {} 而不是 []
 // 2、map 是 Array 上的高阶函数,之前写错成 Function(想成bind了),重写了
- 第四遍 2025.2.14 -
 // 1、again,map 接收两个参数,一个 callback 函数,第二个是 this 指向
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
// 作业68:React hooks 的作用和原理是什么?useEffect useState模拟实现
- 第十遍 2025.3.27 -
原理闭包保存之前的值 + 每次组件更新都会遍历执行整个 hooks 链表
// ⭐️ 作用:在不编写类组件的情况下,使用 state 及 React 的状态管理能力、生命周期替代能力等
const memorizedState = {};
let cursor = 0;
--
function render(){ cursor = 0; return ReactDOM.createElement(<App/>); };
// ❌ 1、render 使用的函数错误 ReactDOM.render(<App />, document.getElementById('root'))
function fakeUseEffect(cb, arr) {
    const lastDep = memorizedState[cursor];
    const hasChanged = lastDep && lastDep.some((it, index) => !Object.is(it, arr[index]));
    if(!lastDep or hasChanged) {
        cb();
        render();
        memorizedState[cursor] = arr;
    }
    cursor++;
}
--
function render(){ return ReactDOM.createElement(<App/>); };
// ❌ 2、render 函数用法错误,内部函数是 render ,参数有第二项 ReactDOM.render(<App />, document.getElementById('root'))
function fakeUseState(init) {
    const current = cursor;
    [memorizedState[current] = [memorizedState[current] ?? init;
    const setValue = (v) => {
        [memorizedState[current] = v;
	    render();
    }
 	cursor++;	
    return [memorizedState[current], setValue]
}
- 第九遍 2025.3.6 -
- 第八遍 2025.2.21 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.12 -
 // 1、原理:没有事件委托,主要是 闭包和链表
 // 2、useEffect:使用 find 得到的是值,应该使用 some + 判断条件应该是 !Object.is
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 2025.1.13 -
 // 1、UseEffect:少写了「更新依赖数组」的逻辑:memorized[count] = array;
 // 2、UseState:使用 value 会导致返回后,value 不会自动更新,和 memorized[currentCount] 失去了联系
 // 3、UseState:调用 setValue 必须触发 render
 // 4、UseEffect:cursor应该使用其他变量保存,和下文中 setValue 形成闭包 - 在异步情况下,cursor 可能会变化,导致索引错误
// 作业67:React 怎么做的性能优化?
- 第九遍 2025.3.27 -
1fiber 架构时间分片渐进式渲染增量式 Diff优先级调度
2是否重新渲染的渲染策略
eagerState策略如果state新值并不依赖旧值渲染则直接渲染 state 新值不查询旧值
bailout策略如果 stateprops 没有变化则跳过重新渲染
	shouldComponentUpdate(nextProps, nextState) 类组件
	React.memo(props => <p>{props.text}</p>) 函数组件,对 props 浅比较,不变则跳过重新渲    useCallback(a=>a*2, [a])/useMemo(b=>b*3, [b]) 依赖项不变则不重新渲染
- 第八遍 2025.3.6 -
- 第七遍 2025.2.21 -
- 第六遍 2025.2.17 -                                
- 第五遍 2025.2.12 -
 // 1、少写了 函数组件 React.memo - 浅比较props,不变则直接复用 
 ⭐️ // 记忆法:shouldComponentUpdate(类组件)/React.memo(函数组件)+2个hook:useMemo/useCallback
- 第四遍 2025.1.23 -
- 第三遍 2025.1.18 -
 ⭐️ // useCallback:每次渲染,函数都是重新生成的,用了这个就缓存住了
- 第二遍 2025.1.12 -
// 作业62:fiber架构是什么?优点和实现方式?
- 第十遍 2025.3.27 -
三个主模块组成 // ⭐️ 主流程
	Reconciler调度器主要用于在渲染阶段管理优先级调度统一管理 // ❌ 1、流程名称错了:Scheduler + 作用少写了:宏观调控
    ?:对应 render 阶段遍历生成 VDOM  收集副作用列表 // ❌ 2、流程名称错了:Reconciler 协调器,作用不是 render 阶段,是时间分片的处理,所以叫协调器:每次循环都会调用 shouldYield 判断当前 Time Slice 是否有剩余时间(没有则暂停更新,将主线程交给渲染流水线,等待下一个宏任务
    Rendercommit 阶段更新真实 DOM同步执行不能被打断 // ⭐️ 渲染器
前两个阶段都是异步的在内存中执行随时可能被打断:①报错 优先级更高的任务 时间切片不够
优点
	1 时间分片 + 渐进式渲染 fiber 之前都是同步对fiber 树遍历对比更新阻塞渲染进程fiber 将渲染过程拆分成一个个渲染任务在每次绘制空闲检查是否时间充足进行渲染任务执行 // ⭐️ 由调度器分配执行
	2 优先级调度优先处理用户的交互延迟一切非实时页面渲染提高交互体验 // ⭐️ 为不同任务配置不同优先级
	3 增量式 Diff根据渲染任务的执行增量式进行 Diff 计算提高算法效率 // ❌ 3、要描述到 逐步比较和更新部分节点,减小了每次比较的范围
- 第九遍 2025.3.6 -
三大主流程 
- Scheduler 调度器管理优先级处理在整个渲染阶段起到宏观调控
- Reconciler  对应 render 阶段在主线程空闲重绘之前计算是否可以执行时间分片 shouldYield // ⭐️ 协调器
- Render 渲染器对应 commit 阶段更新到真实 DOM同步执行无法被中断
Scheduler + Reconciler在内存中执行异步可被打断:①错误时间分片不够有更高优先级的任务 // ⭐️ 在内存中执行,不会更新宿主环境的 UI
优点
1时间分片 + 渐进式渲染之前 React 需要遍历更新整个应用生成 VDOM会长时间占用主线程阻塞重绘使用 fiber 架构实现时间分片后将渲染任务划分为一个一个小渲染任务单元在空闲重绘前执行计算有充足时间则执行
2优先级调度优先处理用户交互的渲染任务提高用户体验
3Diff 增量式更新根据渲染任务的执行情况计算最小更新UI提高 Diff 算法效率
- 第八遍 2025.2.20 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.10 -
 // 1、时间切片:之前 react 会对整棵树 对比+更新,同步执行,会长时间占用主线程,阻塞重绘;描述优化:小的单元任务,在空闲 + 重绘判断执行
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
 // 1、忘记中文:Scheduelor 是调度器
 // 2、增量式 Diff:不是一次性比较整棵树,而是根据任务执行进度,逐步比较、更新部分节点 - 减少了每次比较的范围,提高了效率 
// 2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序
- 第三遍 2025.3.27 -
核心特性:
	依赖注入DI模块化结构基于Express // ❌ 1、模块化架构 + 多种协议 HTTP/websocket
基本概念:
	服务*.service.ts控制器*.controller.ts模块*.module.ts应用入口 main.ts
    在模块中注入服务可以在控制器中使用服务
    令牌在查找实例时的唯一标识 // ⭐️ 依赖注入时用于标识依赖项的唯一标识
    装饰器一个表达式 // ❌ 2、返回一个函数,参数为目标/名称/属性描述符
生命周期:
	根据配置信息实例化 Nest // ❌ 3、① 应用初始化:Nestjs框架启动,解析模块、控制器、provider等配置信息,构建依赖注入容器
    将服务依赖注入容器// ❌ 4、②依赖解析和实例化:根据配置,解析各组件的依赖关系,依次实例化服务、控制器
    提供服务接收请求处理顺序中间件-守卫-请求拦截器-管道-响应拦截器// ❌ 5、③应用启动:所有组件实例化后,应用监听指定端口,正式提供服务	④请求处理 ⑤应用关闭:执行关闭操作,销毁各组件实例
获取请求和响应
	请求// ❌ 6、若是对象或者数组,则自动序列化为 JSON;若基础类型,直接发送,不序列化
    响应 .json .send// ❌ 7、@Req @Body @Query 开箱即用 @Param 动态路由
不同组件的实现和执行顺序
	中间件
    	时机在进入路由系统之前 // ❌ 8、少写了:全局请求的预处理,可以访问原生请求、响应,例如日志记录、请求验证、请求解析、跨域处理
        实现...implements NestMiddleware{ use(ctx, req, res){} } // ❌ 9、use 的参数顺序是 use(req, res, next) + 少写了第二种写法:函数中间件 logger(req, res, next)
		配置main.ts配置app.use(Mid)
            + app.moudle.ts 配置 ...implements NestModule{ 
                config(consumer) {consummer.apply(Mid).forRoutes('cats')} } // ⭐️ 函数名称是 configure
    守卫
    	时机处理权限问题返回布尔类型
        实现...implements CanActivate{ canActivate(ctx){} }
		配置main.ts 配置 app.useGlobalGuards(/实例) // ❌ 10、少写了:全局 useClass 配置
			+ app.controller.ts 控制器中配置 @useGuards(/实例)
    请求拦截器
    	时机请求到达路由处理函数前处理请求头
        实现...implements NestInterceptor{ interceptor(ctx){} }
		配置main.ts 配置 app.useGlobalInterceptors(/实例) // ❌ 11、少写了:全局 useClass
			+ app.contoller.ts 控制器中配置 @useInterceptors(/实例)
    管道
    	时机将路由处理函数的参数进行处理返回新的函数参数覆盖原参数
        实现...implements PipeTransform(){ transform(value, metadata){} } // ❌ 12、少写了第二种内置实现方式:内置管道9个
		配置main.ts 配置 app.useGlobalPipes(/实例)
			+ app.controller.ts 控制器中配置 @usePips(/实例) // ❌ 12、少写了在路由处理函数的配置:@Query('property', 类/实例)/@Param('property', 类/实例)/@Body(类/实例)
    响应拦截器
- 第二遍 2025.3.13 -
- 第一遍 2025.3.5 -
 // 1、更改描述为“模块化架构” + 多种协议 HTTP Websocket
 // 2、少写了 令牌:类是代码的具体实现,令牌是依赖注入的 标识依赖项的唯一标识,指向相应实例
 // 3、生命周期:描述不清楚 应用初始化:解析模块、控制器、provider等配置信息,收集、整理组件间的依赖关系注入容器(容器是个抽象概念,用于管理组件、依赖,启动时创建)
 // 4、依赖解析和实例化:根据配置,深度解析各组件依赖关系,根据关系依次实例化 provider、实例化控制器
 // 5、少写了响应方式:2种 对象/数组-自动序列化为JSON;基础类型-直接返回,不序列化
// 2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现?
- 第四遍 2025.3.27 -
js 动画 加载后的执行会阻塞页面主线程的执行需要写兼容代码因为加载顺序可能无法修改DOM样式
css动画 可以预加载 js 加载完成前就可以渲染动画自然降级可以GPU加速js可操作 DOM 样式
// ❌ 1、CSS 动画可以在合成线程中完成,transform opacity 等只需在合成层操作,避免回流重绘带来的性能损耗
- 第三遍 2025.3.13 -
- 第二遍 2025.3.6 -
- 第一遍 2025.1.30 -
// 作业64:Vue渲染怎么实现的?
- 第十一遍 2025.3.27 -
1 模版编译 AOT编译
	parse 将模版编译为 AST // ⭐️ 词法分析 + 语法分析
    optimize 遍历 AST 标记静态节点
    generate 根据 AST 生成 render 函数
2 初始化组件+首次渲染 - 数据双向绑定
	初始化的时候通过数据劫持+订阅发布进行双向数据绑定 Object.defineProperty(obj, property, {get, set})/Proxy 
	在渲染组件 Watcher 利用 getter 将相关的 Watcher 收集到组件的 Dep 中存储 // ⭐️ 初次渲染
    在更新时setter 触发所有相关 Watcher 的执行 dep.notify()
3 执行 render 函数生成 VDOM
4 Diff 计算得到最小更新 UI应用到真实 DOM
- 第十遍 2025.3.13 -
- 第九遍 2025.3.6 -
 // 1、首次渲染时 getter 触发 Watcher 收集;更新时 setter 触发通知
- 第八遍 2025.2.20 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.11 -
 // 1、模版编译的阶段名称错误:parse - optimize - generate
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
// 2021.09 get和post有什么区别
- 第五遍 2025.3.26 -
get  参数在请求行中大小限制 2kb浏览器默认缓存  回退无害参数在历史记录中被保存了
post 线束在请求体中大小无限制   浏览器默认不缓存回退有害参数没在历史记录中
- 第四遍 2025.3.5 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、从四个方面描述:数据参数 + 参数长度 + 回退安全性 + 缓存
// 2021.06 链表 141. 环形链表
- 第五遍 2025.3.26 -
function isCircle(head) {
    try{
        JSON.stringify(head);
        return false
    }catch(e){
        return true
    }
}
function isCircle(head) {
    while(head) {
        if(!head.isCircle) {
            head.isCircle = true;
        } else {
            return true
        }
        head = head.next;
    }
    return false
}
- 第四遍 2025.3.5 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、判断条件是 while(head) 
// 2021.07 事件流 + 事件模型
- 第五遍 2025.3.26 -
事件流捕获 从最外层 window-document-html-body-div...冒泡 再从里层到最外层
事件模型现代事件模型 - 利用事件委托在组件渲染时绑定在 document  
    // ⭐️ 事件委托基于冒泡 dom.addEventListener('click', ()=>{})
- 第四遍 2025.3.5 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业91:三次握手的过程和具体包的作用
- 第五遍 2025.3.26 -
SYN 序列编号包 ACK 确认包 // ⭐️ SYN 同步序列编号包
客户端发起 SYN
服务器返回 ACK + SYN
客户端返回 ACK
- 第四遍 2025.3.5 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业90:金玉满堂是什么意思?在花卉中的说法是什么?
- 第五遍 2025.3.26 -
指财富很多形容学识充足学富五车
花卉玉兰 海棠 迎春 牡丹 桂花 - 玉堂春富贵
- 第四遍 2025.3.5 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业58:React和Vue是怎么描述UI的
- 第九遍 2025.3.26 -
React JSX = HTML+JS {} 类组件函数组件 stateprops
Vue 模版指令  v-for/v-if/v-if-else/v-show/v-model
- 第八遍 2025.3.5 -
- 第七遍 2025.2.20 -
- 第六遍 2025.2.17 -
- 第五遍 2025.2.10 -
 // 1、React 少写了 :类组件+函数组件
 // 2、Vue 少写了:模版语法 + v-show
- 第四遍 2025.1.22 -
- 第三遍 2025.1.18 -
// 2021.07 柯里化函数 / 反柯里化函数实现
- 第五遍 2025.3.26 -
function curry(fn, ...arg1) {
    if(fn.length > arg1.length) {
        return (...arg2) => curry(fn, ...arg1, ...arg2)
    } else {
        return fn(...arg1)
    }
}
function uncurry(fn) {
    return (...args) => args.reduce((pre, cur) => pre(cur), fn)
}
- 第四遍 2025.3.11 -
- 第三遍 2025.3.2 -
// ❌ 1、不是直接返回回调,而是根据参数数量判断返回
// ❌ 2、参数不够时,返回一个包裹的函数
// ❌ 3、执行应该是 return fn(...args)
- 第二遍 2025.2.8 -
// 2019.07 数组去重(算法整理)
- 第六遍 2025.3.26 -
[...new Set(arr)]
Array.from(new Set(arr))
arr.filter((it, index) => index === arr.indexOf(it))
function unique(arr) {
    const hash = {};
    return arr.filter(it => hash[it] ? false : hash[it] = true)
}
- 第五遍 2025.3.11 -
- 第四遍 2025.3.2 -
- 第三遍 2025.2.8 -
// 2021.06 跨页面通信
- 第六遍 2025.3.26 -
同源页面
localStorage只能处理非当前页面的通信localStorage.setItem(key,v) + window.onstorage
BroadCastChannel同名广播 const b1 = new BroadCastChannel('bbb');
一个页面传输另一个页面可以接收 b1.postMessage('str') + b1.ommessage
非同源页面
postMessage打开新的页面 const w = window.open('url'); 
w.postMessage('str') + window.onmessage
iframeorigin 设置成同源域名
- 第五遍 2025.3.11 -
 // 1、window.onstorage 监听函数名称错了
- 第四遍 2025.3.2 -
- 第三遍 2025.2.8 -
 // 1、构造函数的名称记错:BroadCastChannel
// 作业66:React如何处理错误
- 第十三遍 2025.3.26 -
使用 static getDerivedStateFromError 返回 state 覆盖之前的状态 + componentDidCatch 捕获 render + commit 阶段的错误保证渲染过程中不溢出错误影响整个APP应用的UI展示
class BoundariesError extends React.Component{
    constructor(props) {
        // ❌ 1、少写了 super(props); - 有组件状态的时候,必写
        this.state = { hasErr: false }
    }
    static getDerivedStateFromError() {
        return { hasErr: true }
    }
    componentDidCatch(err, info) {
        console.log(info)
    }
    render() {
        if(this.hasErr) { // ❌ 2、要去 state 中获取 this.state.hasErr
            return <div>There are errs.</div>
        } else {
            return this.props.children;
        }
    }
}
- 第十二遍 2025.3.11 -
- 第十一遍 2025.3.2 -
// ❌ 1、少写了 super(props)
- 第十遍 2025.2.27 -
- 第九遍 2025.2.24 -
 // 1、静态函数名称错了 getDerivedStateFromError 
- 第八遍 2025.2.10 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 2025.02 sequelize 在 Nestjs 中的 env 配置
- 第三遍 2025.3.24 -
// ❌ 1、使用 cross-env 在指令中传入 环境变量 NODE_ENV - 在 package.json 中配置 script
 app.module.ts 引入 Sequelize 模块
import { ConfigModal } from 'nest/config'; ⭐️ // @nest/config
import type ConfigService from 'nest/config';
@Module() // ❌ 2、配置在装饰器 @Module({}) 中
export default class AppModule implements NestModule{
    imports:[
        RoutineModal,
        TimeModal,
        ConfigModal.forRoot({
        	env: path.resolve(_dirname, path.resolve(process.env.type || 'development.env')), // ❌ 3、配置名称是 envFilePath 配置文件路径 `.env.${process.env.NODE_ENV || 'development'}`
        	isGlobal: true
        }),
        SequelizeModal.forRootAsync((configService: ConfigService) => {
            // ❌ 4、Modal 是配置,而不是传入回调函数 {imports:[ ConfigModal ],useFactory:()=>{},inject:[ ConfigService ]}
        	return {
                user: 'root', ⭐️ // username 属性名错了
                passWord: configService.get('psw'),
                host: configService.get('host'),
                database: 'daily',
                dialect: 'mysql',
                inject: [Ltn, Time] // ❌ 5、导入数据路模块定义 应该使用models,而不是inject
                // ❌ 6、少写了 port:3306
            }
        })
    ]
}
// ❌ 7、导出默认的 APPModule 类
export class AppModule {}
- 第二遍 2025.3.10 -
- 第一遍 2025.2.28 -
// 2025.02 Nextjs 获取链接中的参数
- 第五遍 2025.3.24 -
import { useSearchParams } from 'next/navigation';
const params = useSearchParams();
const date = params.get('date');

import { useSearchParams } from 'react-dom-router'; // ❌ 1、包名不对 react-router-dom
const [params, setParams] = useSearchParams();
const date = params.get('date');
// ❌ 2、少写了 因为预渲染,路由是异步传入的,需要 <Suspense> 处理 layout.tsx
export default function RootLayout({children}:Readonly<{children: React.ReactNode}>) {
    return (<html lang='en'>
            	<body>
            		<Suspense fallback={<div>loading</div>}>
            			{children}
            		</Suspense>
            	</body>
            </html>)
}
- 第四遍 2025.3.10 -
- 第三遍 2025.3.1 -
 // 1、依赖包是 react-router-dom,是 router 不是 route
- 第二遍 2025.2.26 -
- 第一遍 2025.2.22 -
 // 1、核心函数的名称记错:useSearchParams
// 2025.02 Mysql 8.0 以上,Sequel报插件错
- 第五遍 2025.3.24 -
本地改为安装包安装安装的倒数第二步可以切换认证插件 mysql_login_plugin ⭐️ //Use Legacy Password
线上进入 root 账户切换 mysql 文件的配置文件 // ❌ 1、不是配置文件,是指令,将用户的认证插件修改为 mysql_native_password
- 第四遍 2025.3.10 -
- 第三遍 2025.3.1 -
 // 1、线上改变的是 认证插件 mysql_native_password
- 第二遍 2025.2.26 -
- 第一遍 2025.2.22 -
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第七遍 2025.3.24 -
A.hasMany(B, {
	foreignField: B_id, // ❌ 1、foreignKey、sourceKey、targetKey
    sourceField: A_id
})
B.belongsTo(A, {
    targetField: B_id,
    sourceField: A_id
})
const res = await A.findAll({
    where,
	include: [{modal: B}] // ❌ 2、包含数据表的属性是 model + SQL 实现是使用 LEFT JOIN
})
以及必须配合 SQL // ❌ 3、记岔劈了,必须配合的是 唯一索引 - CTEARE UNIQUE INDEX+ADD UNIQUE KEY
CREATE INDEX VIEW `idx_date_sort` ON `daily` (`sort`, `date`)
ADD INDEX View `idx_date_sort` (`sort`, `date`)
- 第六遍 2025.3.10 -
- 第五遍 2025.3.1 -
// ❌ 1、include 关键词记错了
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 1、数据库查询函数需要 await
- 第二遍 2025.2.17 -
- 第一遍 2025.2.13 -
 // 1、关键词记错了:belongsTo
// 2021-07 前端路由的两种模式
- 第五遍 2025.3.24 -
hash模式 - 前端切换 #hash不会触发向浏览器发起请求前端可以监听来更新/切换前端页面
	window.onhashchange=()=>{} + location.hash
history模式 - 需要服务器配合通过 pushState/replaceState 修改 history 虽然不会马上触发浏览器向服务端发起请求但是通过 history.go(n)/forward()/back()操作前进后退按钮就会触发 popState 使用 url 发起请求
history.pushState({state}, url, ''); ⭐️ // pushState 参数顺序 url 是第三个 (state,'',url)
window.addEventListener('popState', (e) => {
    console.log(e.state)
})
- 第四遍 2025.3.10 -
- 第三遍 2025.2.28 -
// ❌ 1、 window.addEventListener 是在 window 上而不是在 dom 上监听
- 第二遍 2025.2.26 -
- 第一遍 2025.2.20 -
// 作业93:闭包的作用和原理
- 第七遍 2025.3.24 -
原理内部函数调用外部函数中的活动变量由于被引用会导致活动变量没有被垃圾回收机制清理
// ❌ 1、少写了 核心是词法作用域,在定义时确定作用域,而不是执行时
作用回调函数保存变量封装变量-私有变量 // ❌ 2、少写了定制化函数-函数工厂
- 第六遍 2025.3.10 -
- 第五遍 2025.2.28 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?
- 第六遍 2025.3.24 -
深拷贝和浅拷贝的区别
基础类型深浅拷贝都是将对象拷贝新值的修改不会影响旧值
[...arr] arr.concat() arr.slice() Array.from(arr) {...obj} Object.assign({}, obj)
引用类型浅拷贝是直接拷贝对象地址修改新值会直接影响旧值深拷贝是拷贝对象地址指向的内存空间修改新值不会影响旧值
JOSN.parse(JSON.stringify(obj)) - 无法处理 function和循环
function isObj(t) {
    const type = typeof t;
    return  t !== null && (type === 'function' || type === 'object')
}
function normalType(t, type) {
    switch(type) {
        case '[object Date]':
            return new Date(t.getTime());
            break;
        case '[object ExpReg]':
            return new ExpReg(t.source, t.flags);
            break;
        case '[object Symbol]':
            return Symbol.for(t.description);
            break;
        default:
            const ctor = t.constructor;
            return new ctor(t);

    }
}
function deepClone(target, m = new WeakMap()) {
    if(!isObj(target)) {
        return target;
    }
    const detailType = Object.prototype.toString.call(target);
    const specialTypes = ['Map', 'Set', 'Array', 'Object'].map(it => `[object ${it}]`);
    if(!specialTypes.includes(detailType)) {
        return normalType(target, detailType);
    }
    
    const ctor = t.constructor;
    const res = new ctor();
    if(m.has(target)) {
        return m.get(target);
    }
    m.set(target, res);
    
    if(detailType === '[object Map]') {
        target.forEach((value, key) => {
            res.set(key, deepClone(value, m))
        })
    } else if(detailType === '[object Set]') {
        target.forEach((value) => {
            res.add(deepClone(value, m))
        })
    } else if(Array.isArray(target)) {
        target.forEach((it, index) => {
            res[index] = deepClone(it, m);
        })
    } else {
        for(let i in target) {
            res[i] = deepClone(target[i], m);
        }
    }
    return res
}
- 第五遍 2025.3.10 -
- 第四遍 2025.3.1 -
// ❌ 1、浅拷贝中 Object.assign(obj) 其实是直接返回了 obj 原对象,就是原对象,不算是浅复制,应该是 Object.assign({}, obj)
// ❌ 2、深拷贝中,需要初始化 res 为基础类型,方便后续存储、修改操作:const ctor = target.constructor; let res = new ctor();
// ❌ 3、Map 的 forEach 的回调函数的参数是 (value, key, map)
- 第三遍 2025.2.8 -
 // 1、属性名写错了:正则类型的 t.flags
 // 2、方法错了:object 不是可迭代对象,不能使用 for...of,这里应该改为 for...in
// 作业61:常见的数据结构有哪些
- 第八遍 2025.3.24 -
按照逻辑结构分
线性结构 队列数组链表
非次线性结构  
- 第七遍 2025.3.10 -
- 第六遍 2025.3.1 -
// ❌ 1、线性结构 + 非线性结构
- 第五遍 2025.2.15 -
- 第四遍 2025.2.9 -
- 第三遍 2025.2.2 -
 // 1、少写了一个类型:数组
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第十一遍 2025.3.24 -
mysql2 是专门为 node 环境设计的 数据库驱动可以直连数据库主要写的是 SQL 来查询数据库
	- 支持 async/await  mysql2/promise同时管理多个数据库连接
Sequelize ORM 工具将数据库表和行映射为对象模型适合于函数式编程
import { createPool } from 'mysql2'; // ❌ 1、依赖包是 'mysql2/promise'
const pool = createPool({user, password, host, database});
// ❌ 2、创建 pool 连接池 之后,再获取连接 const con = await pool.getConnection()
const rows = await pool.execute('SELECT * FROM table');
// ❌ 3、这里的 rows 是数组 且不是 pool 直接调用执行 SQL 函数,应该是连接执行 SQL 指令,可以直接 [rows] 来数组解构,真正赋值给 rows

import { Sequelize, DataType, Modal } from 'sequelize';
const se = new Sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql'
})
⭐️ // 创建连接后,一直保持打开状态,并对所有查询使用相同状态,关闭需要单独调用实例关闭 se.close()
// 法1 ⭐️ // 底层是调用 Modal 实现的
const User = se.define('table', {
    name: DataType.STRING
}, {
    timestamps: true,
    underscored: true
});
// 法2 ⭐️ // 模型是 Sequelize 的本质
class User2 extends Modal{}
User2.init({
    name: DataType.STRING
}, {
    modalName: 'table', // ❌ 4、modelName 模块名称
    sequelize: se
})
- 第十遍 2025.3.10 -
- 第九遍 2025.3.1 -
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
 // 1、mysql2:获取连接的函数错了 await getConnection()
 // 2、mysql2:执行 SQL 需要 await
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、sequelize:实例化参数错误 + init 参数错误
 // 2、sequelize:define 第三个参数应该是一些配置
- 第四遍 2025.2.9 -
- 第三遍 2025.1.31 -
 // 1、mysql2:导入依赖包错了 mysql2/promise + createPool的参数 少写了 database
- 第二遍 2025.1.26 -
 // 1、mysql2:创建连接池 createPool 不需要 await
- 第一遍 2025.1.22 -
// 作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情?
- 第十一遍 2025.3.24 -
类组件 - 生命周期函数
挂载前 shouldComponentUpdate(nextProps, nextState)是否需要重新渲染 getDerivedStateFromProps 
挂载阶段:componentDidMount 挂载到真实 DOM  
// ❌ 1、挂载前的说法 不对,挂载阶段分为挂载前、挂载中、挂载后 - 是创建组件【重点注意】+首次挂载到DOM - 顺序是先①constructor 组件的构造函数,用于初始化state和事件处理函数的this、②static getDerivedStateFromProps 再到执行③render() 最后挂载后执行的 ④componentDidMount
更新阶段: componentDidUpdate 更新到真实 DOM  // ❌ 2、shouldComponentUpdate 是在更新阶段进行性能优化的,更新阶段的主要行为是 重新执行 render 构建出新的 VDOM,顺序是先 ①static getDerivedStateFromProps 再 ②shouldComponentUpdate(nextProps, nextState) 判断是否重新渲染,再 ③render 重新渲染,更新后 ④componentDidUpdate
卸载阶段: componentWillUnMount 卸载前清理
函数组件 - 模拟生命周期
useEffect 模拟生命周期函数
useLayoutEffect 在更新到真实 DOM 
- 第十遍 2025.3.10 -
- 第九遍 2025.2.28 -
- 第八遍 2025.2.12 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.31 -
 // 1、挂载阶段 少写了 constructor - 初始化 state 和绑定事件处理函数的 this
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
 // 1、更新阶段的 shouldComponentUpdate 写错在挂载阶段,实际应该归属于更新阶段
 // 2、阶段划分是:挂载-更新-卸载,其中更新阶段记错了 - 应该是当组件的 state/props 变化,进入更新阶段,重新执行 render 函数来更新 DOM
- 第三遍 2025.1.19 -
// 2021.04 25 HTTP/2 特性
- 第五遍 2025.3.23 -
语义层面不变语法层面大力改造
1 压缩头部算法 ⭐️ // HPACK 算法
2 改为二进制帧 - 双向传输序列形成虚拟的流多路复用 - 一个连接上可以处理多个请求响应没有顺序限制的时候也在应用层解决了队头阻塞
3 服务端可以主动发起请求
4 可以设置优先级
5 升级为 HTTPS
- 第四遍 2025.2.23 -
- 第三遍 2025.2.2 -
 // 1、少写一个特性:服务器推送
// 作业79:地球四季的成因是什么?
- 第四遍 2025.3.23 -
和太阳照射时长无关是太阳直射点的位置移动和地面夹角的改变改变经过大气层的路径长度
// ❌ 1、和太阳照射时长 + 太阳直射点移动 - 路径长度的改变会改变衰减的辐射能量强度;和太阳公转造成的地球和太阳距离没啥关系
- 第三遍 2025.2.23 -
- 第二遍 2025.2.2 -
// 作业77:重绘和重排是什么?有什么区别?
- 第四遍 2025.3.23 -
DOM树- CSS树 - 布局 - 分层 - 合成
重排需要重新计算样式布局再分层合成 - 性能消耗高
	- css 属性增删style属性样式变更伪元素激活内容大小位置改变访问节点位置 ⭐️ // 页面初始渲染、dom 元素增删、resize 窗口、伪类激活、节点布局属性查询
重绘不需要重新计算布局直接绘制到合成
	- 字体背景颜色
- 第三遍 2025.2.23 -
- 第二遍 2025.2.2 -
// 作业54:举杯邀明月,对影成三人。的上一句
- 第五遍 2025.3.23 -
花间一壶酒独酌无相亲
- 第四遍 2025.2.23 -
- 第三遍 2025.2.2 -
// 作业49:牛顿三大定律、热力学两大定律
- 第五遍 2025.3.23 -
惯性定律加速度定律作用力与反作用力定律
能量守恒定律熵增定律
- 第四遍 2025.2.23 -
- 第三遍 2025.2.2 -
 // 1、少写了惯性定律 - 公交车记忆:刹车-加速度、乘客-惯性、地上痕迹-摩擦
// 69 x 的平方根
- 第五遍 2025.3.23 -
function sqrt(n){
    const mid = Math.floor(n/2) + 1;
    let res = 0;
    for(;res < mid; res++){
        if(res * res <= n && (res + 1)*(res+1) > n){
            return res
        }
    }
    return res
}
function sqrt(n) {
    let left = 0;
    let right = n + 1;
    while(left + 1 < right) {
        const mid = Math.floor((left + right) /2);
        if(mid * mid > n){
            right = mid
        } else {
            left = mid
        }
    }
    return left
}
- 第四遍 2025.3.16 -
 // 1、for(;res <=mid; res++) 不需要等于号,完全平方数时,导致多余的循环
 // 1、right 取 x + 1,使下面的循环成立
 // 2、while(left <= right) 会导致死循环 while(left + 1 < right)
- 第三遍 2025.3.1 -
- 第二遍 2025.2.26 -
- 第一遍 2025.2.21 -
 // 1、res 应该从 0 开始数,否则 n 为 0 时会返回 1
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第七遍 2025.3.23 -
async getData(data) {
    const [issue, created] = await modal.findOrCreate({where, defaults: data});
    if(!created){
        issue.set(data);
        await issue.save();
    }
}
- 第六遍 2025.3.16 -
 // 1、data 的接收属性是 defaults
- 第五遍 2025.3.1 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 2、issue.set 不需要 await
- 第二遍 2025.2.17 -
 // 2、更新时:先set数据,再保存
- 第一遍 2025.2.13 -
// 作业51: 如何停止过度思考?如何停止焦虑
- 第四遍 2025.3.23 -
思考令人上瘾让自己 get it然后 let it go回到当下
停止焦虑其实是害怕失败那就写下所有可能成功的路径去做
- 第三遍 2025.3.16 -
 // 1、思考令人上瘾,get it,回到当下 + 焦虑失败则写下所有可以成功的方式,去做
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置
- 第四遍 2025.3.23 -
input 单行size 设置宽度单位为字符maxLength 设置最大长度 value 设置初始值
textarea 多行cols 设置宽度rows 设置高度标签间的值作为初始值
option  value 属性时取 value 属性没有时取标签中间值作为初始值
- 第三遍 2025.3.16 -
 // 1、textarea 的初始值取标间中间值,而不是 value,该标签没有 value 属性,即设置了也无效
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 2019.10 两栏,左边固定,右边自适应的布局
- 第七遍 2025.3.23 -
.left { width: 100px }
1 .left{ float: left } .right{ margin-left: 100px }
2 .left{ position: absolute } .right{ margin-left: 100px }
3 BFC .left{ float: left } .right{ overflow: hidden }
4 flex .outer{ display: flex } .right{ flex:1 }
5 table/grid .outer{ display: table; width: 100% } .left, .right{ display: table-cell }
.outer{ display:grid; grid-template-columns: 100px auto }
6 calc .left,.right{ float: left } .right{ width: cacl(100% - 100px) }
- 第六遍 2025.3.16 -
 // 1、少写了 BFC .left{float:left} .right{overflow:hidden}
- 第五遍 2025.3.8 -
 // 1、grid-template-columns
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
 // 2、父盒子变成弹性盒子后, right 要变成可拉伸 .right { flex: 1 }
// 2024.10 怎么实现跨域
- 第七遍 2025.3.23 -
1 jsonp 只能处理 GET利用 script 标签 src 不受限
2 CORS Access-Control-Allow-Origin 响应头/meta设置 配置服务端信任的域名
3 nginx 代理服务端之间没有同源协议限制
4 websocket 全双工-实时通讯协议不受同源协议的限制
- 第六遍 2025.3.16 -
 // 1、jsonp 只支持 GET 请求
 // 2、属性名错误 Access-Control-Allow-Origin、配置方式是响应头
- 第五遍 2025.3.8 -
 // 2、CORS 服务端允许的哪些域名可以访问 - 是为了让浏览器知道哪些域名可以访问,而不是从客户端角度描述
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
// 作业70:Vue 和 React 的 Diff 算法比较
- 第十三遍 2025.3.23 -
Vue diff - 静态节点跳过 双端指针算法-同标签同key 索引比较 - 同标签同key复用
React diff - 分层比较 + 同标签优先比较不同标签-直接重新渲染同标签-修改属性同标签同key 移动复用
- 第十二遍 2025.3.16 -
 // 1、React 同类型优先比较
- 第十一遍 2025.3.2 -
- 第十遍 2025.2.27 -
 // 1、Vue 是组件级别的更新 + React 是节点属性级别的更新
- 第九遍 2025.2.23 -
 // 1、Vue 少写:Diff 跳过静态节点
 // 2、React 少写了:分层比较 + DFS 深度优先遍历子节点,逐个比较
- 第八遍 2025.2.18 - 
- 第七遍 2025.2.16 -
 // 1、Vue 少写了:双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
- 第三遍 2025.1.18 -
- 第二遍 2025.1.13 -
// 作业17:对斐波那切数列进行尾调用优化
- 第五遍 2025.3.23 -
function fib(n){
    if(n<2) return n;
    return fib(n-1)+fib(n-2)
}
优化
function fib(a, b, n) {
    if(n===0) return a;
    return fib(a+b, a, n -1)
}
- 第四遍 2025.3.16 -
 // 1、n为0时,return a
 // 2、第二个参数 应该是 a,否则 b 一直是 1
- 第三遍 2025.2.16 -
 // 1、参数传递可以传 b,a+b,n-1 或者 a+b,a,n-1
- 第二遍 2025.1.20 -
// 作业76:显卡的作用?
- 第六遍 2025.3.22 -
将位图合成图像渲染帧存储入后缓存区 ⭐️ // 后缓冲区
⭐️ // 一旦合成的图像写到后缓冲区,系统就会让前后缓冲区互换
- 第五遍 2025.3.1 -
- 第四遍 2025.2.15 -
- 第三遍 2025.2.10 -
- 第二遍 2025.2.2 -
// 作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次?
- 第七遍 2025.3.22 -
在合成事件+生命周期函数中setState  批量更新机制是异步的 - 在事件处理函数中遇到 setState 收集到一个队列在执行完事件处理函数的逻辑后批量执行 setState 然后触发一次更新渲染
在原生事件/setTimeout等函数中 setState 是同步的调用一次执行一次触发一次更新渲染
- 第六遍 2025.3.1 -
- 第五遍 2025.2.15 -
- 第四遍 2025.2.10 -
- 第三遍 2025.2.2 -
 // 1、除了合成事件,还有「生命周期函数」中都是异步
// 作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例
- 第八遍 2025.3.22 -
高阶函数参数或者返回值是函数的函数 map sort
高阶组件参数是组件的组件
副作用函数式编程的说法除了返回值还会影响其他值例如修改全局变量参数IO下载交互等
const a = function(MyC){
    return class extends React.Component{
        constructor(props) {
            super(props);
        }
        render() {
            return <MyC {...this.props} />
        }
    }
}
- 第七遍 2025.3.1 -
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
- 第四遍 2025.2.2 -
⭐️ // 副作用,少写了:除了返回值
// 作业55:flex 常见缩写
- 第八遍 2025.3.22 -
flex: none; 0 0 auto
flex: auto; 1 1 auto
flex: initial; 0 1 auto
flex: 1; 1 1 0 
- 第七遍 2025.3.1 -
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
- 第四遍 2025.2.2 -
 // 1、少写了一个 flex: initial; // 0 1 auto
// 作业74:起舞弄清影 是那首诗?谁写的?下一句是什么?
- 第六遍 2025.3.22 -
苏轼 水调歌头 明月几时有
明月几时有把酒问青天不知天上宫阙今夕是何年
我欲乘风归去又恐琼楼玉宇高处不胜寒
转朱阁低绮户照无眠// ❌ 1、顺序反了,先起舞,后转朱阁
起舞弄清影何似在人间
不应有恨此时长向别时圆
⭐️ // 少写了:人有悲欢离合,月有阴晴圆缺,此事古难全。
但愿人长久千里共婵娟
- 第五遍 2025.3.1 -
// ⭐️ 顺序反了,先不应有很,何事长向别时圆?再人有
// ⭐️ 少写了 但愿人长久,千里共婵娟
- 第四遍 2025.2.15 -
- 第三遍 2025.2.9 -
- 第二遍 2025.2.2 -
// 作业25:简单写下 请求创建过程
- 第八遍 2025.3.22 -
function XMLRequest(url){
    const xhr = new XMLHttpRequest();
    xhr.ontimeout = ()=>{};
    xhr.onerror = ()=>{};
    xhr.onreadystatechange = (res) => {
        switch(res.readyState) {
            case 0:
                break;
            case 4:
                if(this.status === 200 || this.status === 304) {
                    return console.log(this.responseText);
                }
        }
    }
    xhr.open('GET', url, true);
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader(key, value);
    xhr.send();
}
- 第七遍 2025.3.1 -
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
⭐️ // status 在 this 上
- 第四遍 2025.2.2 -
 // 1、函数名称错误:onreadystatechange
 // 2、readyState 状态属性不在 this 上,在 res 上:res.readyState
 // 3、switch 中 0 表示请求还没开始,要使用 break 跳出循环
 // 4、open 的参数顺序记错了:xhr.open('GET', url, true)
// 作业6:new操作符实例化一个对象的过程
- 第七遍 2025.3.22 -
function fakeNew() {
    const ctor = [].shift.call(arguments);
	const obj = new Object();
    obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
    return typeof res === 'object' ? res : obj;
}
- 第六遍 2025.3.16 -
- 第五遍 2025.2.24 -
- 第四遍 2025.2.10 -
- 第三遍 2025.2.9 -
- 第二遍 2025.1.28 -
 // 1、实例的 __proto__ 指向的不是构造函数,是构造函数的原型对象 ctor.prototype;
- 第一遍 2025.1.20 -
 // 1、不是调整 constructor 的指向,是设置实例的原型 __proto__
// 作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法。写个POST调用流程
- 第七遍 2025.3.22 -
序列化将对象结构转换成方便存储传输的格式
	JSON.stringify(data) 转换成 json 格式 ⭐️ // 无法处理 function 和 循环引用
	new FormData(form) 转换成键值对组成的 json
反序列化响应返回的都是 json 格式需要转换成 js 格式
JSON.parse(data) response.json()
const options = {
    methods: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify(data)/new FormData(form)
}
const data = await fetch('xx', options);
data.then(res => res.json()).then(res => res);
- 第六遍 2025.3.15 -
 // 1、POST 的数据在 body 中
 // 2、少写了 headers: { 'Content-Type': 'application/json' }
- 第五遍 2025.2.28 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
 // 1、将「数据结构、对象」转换为可以「存储、传输」的格式的过程
- 第二遍 2025.2.10 -
 // 1、new FormData 收集表单数据并序列化,转换成键值对格式
- 第一遍 2025.2.8 -
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第十一遍 2025.3.22 -
预渲染主要由 SSR 服务端渲染 getServerSideProps + SSG 静态生成 generateStaticParams + ISR 增量
⭐️ // NextJS 中所有组件默认为服务端组件,默认实现服务端渲染
静态生成 revalidate
服务端渲染 - 在客户端发起请求时服务端将 data 获取后生成 HTML客户端直接渲染客户端js 不需要执行
增量静态生成 - 在运行时根据 revalidate 定时在服务端更新 HTML方便客户端直接调用 ⭐️ // 增量更新
SSR
import getServerSideProps from 'next/server';
export async function getServerSideProps(){
    const res = await fetch('xxx', options);
    const data = await res.json();
    return {props: {data}}
}
export function Blog({data}){return <div>{data.text}</div>}
SSG
export async function generateStaticParams() {
    return [{slug: 'first'}, {slug: 'second'}]
}
export const revalidate = 60;
export function Blog({slug}){return <div>{slug}</div>}
- 第十遍 2025.3.15 -
 // 1、SSG 少写了 generateStaticParams
 // 2、SSR 依赖包错了 next/server
 // 3、response.json 少写了 await
- 第九遍 2025.3.1 -
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、SSG + SSR:函数 都少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR + 客户端、服务端生成的结果都是 HTML
- 第三遍 2025.1.31 -       
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 2021.07 数组扁平化(一层、全部展开、指定深度)
- 第九遍 2025.3.21 -
一层 arr.flat()  [].cancat(...arr) [].cancat.apply([], arr) [].concat.call([], ...arr)
全部展开 arr.toString().split(',') // ❌ 1、少写了 arr.flat(Infinity)
指定深度
function deepArr(arr, deep = 1){
    return arr.reduce(it => it.concat(Array.isArray(it) && deep > 1 ? deepArr(it) : it ))
    // ❌ 2、reduce 的使用方法错了 return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) && deep > 1 ? deepArr(cur, deep - 1) : cur ), [])
}
- 第八遍 2025.2.28 -
- 第七遍 2025.2.12 -
- 第六遍 2025.2.9 -
- 第五遍 2025.1.31 -
 // 1、JSON.parse(JSON.stringify(arr)) 是用于深拷贝的,不能展开
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、少写了 arr.flat(Infinity)
 // 2、全部展开递归函数中,pre 的值不要使用 push 来更新 - concat 既能处理数组,也能处理非数组
- 第二遍 2025.1.19 -
 // 1、deep 传递的时候要减一
// 2025-03 p-limit 使用+源码 、100个请求并发请求
- 第二遍 2025.3.21 -
import pLimit, { limitFunction } from 'p-limit';
// 方法1
const limit = new pLimit(4);  // ❌ 1、创建限制器不需要 new:limit = pLimit(6)
const arr1 = Array.from({ length: 30 }, (it, index) =>
	limit(() => new Promise((resolve) => setTimeout(resolve, index * 88, index)))
)
Promise.all(arr).then(res => console.log(res))
// 方法2
const arr2 = Array.from({ length: 30 }, (it, index) =>
	limitFunction(() => new Promise((resolve) => setTimeout(resolve, index * 88, index)),
	{ concurrency: 4 })
)
//  源码
function validate(limit) {
    if (!Number.isInteger(limit) || limit < 0) {
        throw new Error('concurrency 无效') ⭐️ // TypeError
    }
}
function pLimit(concurrency) {
    validate(concurrency);
    const queue = [];
    let running = 0;
    const next = () => {
        running--;
        if (queue.length) { ⭐️ // queue.length > 0
            queue.pop()();
        }
    }
    const run = async (resolve, fn, arg) => {
        running++;
        resolve(fn(...arg));
         /** 2、直接执行不对,需要 await + 应该使用 try-catch
        try{ 
        	const result = await fn(...arg);
        	resolve(result);
        }catch(e){
        	resolve(Promise.reject(e))
        }*/
        next()
    }
    return (fn, ...arg) => {
        return new Promise((resole) => {
            if (running < concurrency) {
                run(resolve, fn, arg)
            } else {
                queue.push(() => run(resolve, fn, arg))
                ⭐️ // 更优的写法:queue.push(run.bind(null, resolve, fn, arg))
            }
        })
    }
}
function limitFunction(fn, options) {
    const limit = new pLimit(options.concurrency);
    return limit(() => fn)
     // 3、return 的应该是函数 return (...arg) => limit(() => fn(...arg))
}
// 100个并发请求
const arr = Array.from({ length: 100 }, (it, index) => {
    return () => new Promise((resolve) => setTimeout(resolve, index * 99, index))
     // 4、return 的不应该是函数,而是直接的 promise
})
async function Co(arr, concurrency) {
    const indexedArr = arr.map((it, index) => ({
        index,
        promise: () => Promise.resolve(it).then(value => ({ key: index, value }))  // 5、不应该是函数,应该是 promise
    }))
    const result = [];
    const queue = indexedArr.splice(0, concurrency);
    while (queue.length) {  // 6、queue.length > 0
        const { key, value } = await Promise.race(queue.map(it => it.promise))
        result[key] = value;
		 // 7、解决的 promise 在 queue 里面的 index 需要重新查找:const resolvedIndex = queue.findIndex(it => it.index === key)
        queue.splice(key, 1);  // 8、queue.splice(resolvedIndex, 1)
        queue.push(indexedArr.pop())  // 9、需要判断 indexedArr 是否还有待处理函数 if(indexedArr.length > 0){ queue.push(indexedArr.pop()) }
    }
    return result
}
- 第一遍 2025.3.14 -
 // 1、p-limit 还可以导出一个函数 { limitFunction } 处理
 // 2、创建限制器不需要 new:limit = pLimit(6)
 /** p-limit 函数实现,而不是类
需要校验传入的 concurrency 是否为正整数
next 执行队列中下一个任务 count-- 一个任务完成则减一
run  执行任务封装 count++ 表示有个新任务开始开始执行*/
// 2025-3 NestJS 守卫-校验权限 + 2025-3 NextJS 全局中间件-校验权限
- 第一遍 2025.3.20 -
权限校验
Nest - 守卫
function MyAuth(ctx) {  // 1、不拆分处理 且 不等于的时候需要 throw new UnauthorizedException
    const header = ctx.request.header;
    const auth = header.get('auth')
    if(auth !== 'xxx'){
        return false;
    }
    return true;
}
@Injectable()  // 2、少写了():装饰器需要执行
export class MyAuth implements CanActivate {  // 3、少写了 class 声明
    canactivate(config){  // 4、canactivate 的参数是 ctx;config 这边应该是 Nest 中间件 - 配置时,控制器中 ...implements NestModule{ configure(consumer){consumer.apply().forRoutes()} }
        config.apply(/实例).forRoutes('cats') ;
		 // 5、从 context 中获取 request:const request = ctx.switchToHttp().getRequest()
         // 6、再从 request 中获取 headers:const headers = request.headers;
         // 7、if(headers.auth !== 'xxx'){ throw new UnauthorizedException('x') }
    }
}
使用 @useGuard(MyAuth)  // 8、useGuards 少了个 s

Next - 全局中间件  // 1、middleware.ts 放在 app 同层级下面,src/middleware.ts
 // 2、路由配置器 export const config = { matcher: '/api/:path*' }
export function MyAuth(request: NextRequest, next) {  // 3、少写了 export 且参数没有 next
    const header = request.switchHttp().getRequest().header;
    const auth = header.get('auth');
	 // 4、headers 获取的方式:NextRequest 已经封装好了 const headers = request.headers
     // 5、变量的获取方式 const auth = headers.get('Auth')
    if(auth !== 'xxx'){
        return NextResponse.josn({status: 500, error: 'wrong auth'}); ⭐️ // 401
    }
    next();  // 6、next 调用 NextResponse.next()
}
// 2024-11 数组乱序 洗牌算法 从最后一个元素开始,从数组中随机选出一个位置,交换,直到第一个元素。
- 第三遍 2025.3.20 -
function shuffle(arr) {
    let lastIndex = arr.length - 1;
    while(lastIndex >= 0) {
        const random = Math.random() * arr.length;  // 1、少处理成整数 Math.floor
        [arr[random], arr[lastIndex]] = [arr[lastIndex], arr[random]];
        lastIndex--;
    }
    return arr
}
- 第二遍 2025.2.20 -
- 第一遍 2024.12.25 -
// 2021-04 实现给定时间切换状态
- 第二遍 2025.3.20 -
async function time(promise, delay) {
    return await Promise.race([promise, 
                               new Promise((resolve, reject) => setTimeout(reject, dealy, new Error('超时')))])
}
- 第一遍 2025.2.20 -
// 2021-07 给DOM元素绑定事件
- 第二遍 2025.3.20 - 
HTML 标签 onlick  // 1、少写了 onclick 属性值为可执行代码而不是函数 onlick='init()'
事件委托 dom.addEventlistener('click', (e) => {console.log(e)})
 // 2、少写了两者差异:事件委托可以给同一个元素添加多个事件;onclick 一个元素只能添加一个事件
- 第一遍 2025.2.20 -
// 2025.02 Express VS KOA2
- 第三遍 2025.3.20 -
Express 
	设计理念主要是回调函数实现异步后已支持异步promise async/await;框架功能完整,开箱即    中间件线性模型通过 next 线性执行中间件
    错误处理res.json()/res.send()
    app.use((err, req, res, next)=>{ 
    	res.end({status: 500, message: err})
         // 1、res.status(500).send(err)
    })
    上下文req res  // 2、res.json/res.send 都是基于底层的 Node 的 Response.end 封装的
KOA2
	设计理念基于 async/await 实现,适合复杂异步场景;代码简单、灵活、性能    中间件洋葱模型先入栈依次执行再出栈依次执行
    错误处理app.use(async(ctx, next) => { 
    	try{await next()}catch(e){ ctx.status = 500; ctx.body = {message: e} }
    })
    上下文context 封装了 req res  // 3、ctx.body 来设置响应体
- 第二遍 2025.3.13 -
 // 1、错误处理方式: Express - app.use((err,req,res,next)=>{res.status(500).send('err')})
	  // 错误处理方式: KOA2 try-catch 结合中间件实现
 // 2、上下文:Express res.send()/res.json() 都是基于 Node 底层 Response 的 res.end 的封装
- 第一遍 2025.3.5 -
// 2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码
- 第三遍 2025.3.20 -
中间件机制实现洋葱模型先入栈-执行最里层的中间件-再依次出栈
function compose2(middlewares) {
    return (ctx, next) => {
        let index = -1;
        return dispatch(0);
        function dispatch(i) {
            if (i <= index) { return Promise.reject('multiple times') }
            ⭐️ // 少写了 index = i
            let fn = middlewares[i];
            if (!fn) { return Promise.resolve() }
            if (i === middlewares.length) { fn = next }
            try {
                const nextFunc = () => dispatch(i + 1);
                return Promise.resolve(fn(ctx, nextFunc))
            } catch (e) { Promise.reject(e) }
        }
    }
}
路由处理: KOA2 没有内置路由模块使用 koa-router 实现 router.get('/cat', async (ctx) => {})
错误处理: app.use(async(ctx, next) => { 
    try{ await next() } catch(e) { ctx.status = 500; ctx.body = { message: 'err' } }
})
模版引擎:
ejs模版引擎  <% for %>适合复杂模版性能没那么好需要编译再执行 js
art-template模版引擎 {}代码简洁有预编译性能好可以直接执行 js
源码
import http from 'http'
class KOA2 {
    constructor() {
        this.middlewares = [];
    }
    listen(...arg) {
        const server = http.createServer(this.callback());
        return server.listen(...arg)
    }
    use(fn) { this.middlewares.push(fn) } ⭐️ // 少写了 return this
    callback() {
        const fnCompose = compose(this.middlewares);
        return (req, res) => {
            const context = createContext(req, res); ⭐️ // 应该是 this.createContext 调用
            return fnCompose(context).then(() => response(context)).catch(e => ctx.onerror(e))
        }
    }
    createContext(req, res) {
        const ctx = Object.create(this.context);
        ctx.request = Object.create(this.request);
        ctx.response = Object.create(this.response);
        ctx.req = ctx.request.req = req;
        ctx.res = ctx.response.res = res;
        return ctx
    }
}
function response(ctx) {
    ctx.res.statusCode = ctx.status;
    ctx.res.end(ctx.body)
}
- 第二遍 2025.3.13 -
 // 1、index 初始值设置为 -1,否则 0 就相等抛出重复错误了
 // 2、执行中间件时,ctx 和 nextFunc 作为参数传入
 // 3、处理错误时,改变 ctx.status 和 ctx.body
 // 4、callback 执行中 少写了 return + catch 错误处理使用 ctx.onerror
 // 5、statusCode 是在原生 Reponse 上 ctx.res.statusCode
- 第一遍 2025.3.5 -
 // 1、KOA2 是基于 NodeJS 的 HTTP 中间件框架
 // 2、中间件写法:应该是 async 函数,async (ctx, next)=>{ await next() }
 // 3、如果重复执行,直接返回 Promise.reject(new Error('dup'))
 // 4、少写了 fn 不存在时,return Promise.resolve()
 // 5、KOA2 路由参数 ctx.params + 链接参数 ctx.query
// 2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程
- 第五遍 2025.3.20 -
原理vue的数据更新后DOM 是异步更新的nextTick 提供了更新真实 DOM 后的执行时机 ⭐️ // 在下一个事件循环时,批量更新 DOM
Vue2 收集回调函数 - 根据环境模拟实现异步 - 执行没有传入回调时默认返回一个 resolve 状态 promise
vue3 收集 - 使用 queueMicrotask 模拟实现 - 执行始终返回 promise
const callbacks2 = [];
let pending22 = false;
function flushJobs() {
    pending22 = true;
    const copy = [...callbacks2];
    copy.forEach(it => it());
    callbacks2.length = 0;
    pending22 = false
}
let timeFunc22;
if (typeof Promise !== 'undefined') {
    timeFunc22 = () => Promise.resolve().then(flushJobs)
} else if (typeof MutationObserver !== 'undefined') {
    const ob = new MutationObserver(flushJobs);
    const dom = document.createTextNode('1');
    ob.observe(dom, { characterData: true });
    timeFunc22 = () => dom.data = '2';
} else if (typeof setImmediate !== 'undefined') {
    timeFunc22 = () => setImmediate(flushJobs);
} else {
    timeFunc22 = () => setTimeout(flushJobs, 0)
}

function vue2NextTick(cb, thisArg) {
    if (!cb) { return Promise.resolve() }
    callbacks2.push(() => {
        try { cb.apply(thisArg) } catch (e) { console.log(e) }
    })
    if (!pending) { timeFunc22() }
}
new Vue({
    data() { return { msg: '1' } },
    mounted() { this.$Vue2nextTick(() => { }) }
})

function flushJobs3() {
    pending22 = true;
    let job;
    while ((job = callbacks2.shift())) {
        job();
    }
    pending22 = false;
}
const timeFunc3 = () => { queueMicrotask(flushJobs3) }
function vue3NextTick(cb) {
    return new Promise((resolve, reject) => {
        if (!cb) { resolve() }
        callbacks2.push(() => {
            try {
                cb();
                resolve()
            } catch (e) { reject(e) } ⭐️ // 不返回 reject,nextTick 返回 promise 是为了递归
        })
        if (!pending22) { timeFunc3() }
    })
}
import { nextTick, createApp } from 'vue';
const app3 = createApp({
    data() { return { msg: '1' } },
    mounted() { nextTick(() => { }) }
})
app3.mount('#root') ⭐️ // #app
- 第四遍 2025.3.13 -
 // 1、Vue3:nextTick 返回 resolved 状态的 Promise
 // 2、Vue3:有 err 时,console 处理;resolve 放在 try-catch 外面 - 只要 cb 执行了,就 resolve
- 第三遍 2025.3.3 -
 // 1、vue2 timeFunc 应该包裹成回调函数,否则赋值时就直接执行了 timeFunc = ()=>p.then(..)
 // 2、vue2 节点创建函数错误 createTextNode + characterData +  .data='2'
 // 3、vue2 nextTick 有第二个参数 ctx -> 指定 cb 的上下文 
 // 5、vue3 中,回调函数需要把当前的 Promise 状态切换为 resolved
- 第二遍 2025.2.27 -
 // 1、vue2 执行所有回调函数中少写了:清空原数组,避免重复执行回调 callbacks.length=0
 // 2、vue2 根据环境判断,有 Promise 时 Promise.resolve().then 少写了执行()
 // 3、收集回调函数时,需要处理报错的情况 try-catch
- 第一遍 2025.2.24 -
 // 1、Vue3:和Vue差异是,使用 queueMicrotask 统一处理微任务
// 2024.12 模拟实现promise
- 第八遍 2025.3.20 -
const S7 = {
    pending: 'pending',
    resolved: 'fulfilled',
    rejected: 'rejected'
}
function MyPromise7(executor) {
    this.status = S7.pending;
    this.value = null;
    this.reason = null;
    this.resolvedCbs = [];
    this.rejectedCbs = [];
    const resolve = (value) => {
        if (this.status === S7.pending) {
            this.status = S7.resolved;
            this.value = value;
            this.resolvedCbs.forEach(it => it(value))
        }
    }
    const reject = (reason) => {
        if (this.status === S7.pending) {
            this.status = S7.rejected;
            this.reason = reason;
            this.rejectedCbs.forEach(it => it(reason))
        }
    }
    try {
        executor(resolve, reject);
    } catch (e) {
        reject(e);
    }
}
MyPromise7.prototype.then = function (onResolved, onRejected) {
    ⭐️ // 少写了对 onResolved, onRejected 校验为函数类型
    return new MyPromise7((resolve, reject) => {
        const resolveFunc = () => {
            setTimeout(() => {
                try {
                    const res = onResolved(this.value);
                    resolve(res)
                } catch (e) { reject(e) }
            }, 0);
        }
        const rejectFunc = () => {
            setTimeout(() => {
                try {
                    const res = onRejected(this.reason);
                    resolve(res)
                } catch (e) { reject(e) }
            }, 0);
        }
        switch (this.status) {
            case S7.pending:
                this.resolvedCbs.push(resolveFunc);
                this.rejectedCbs.push(rejectFunc);
                break;
            case S7.resolved:
                this.resolvedCbs.push(resolveFunc());
                break;
            case S7.rejected:
                this.rejectedCbs.push(rejectFunc());
                break;
        }
    })
}
MyPromise7.prototype.catch = function (onRejected) {
    return this.then(null, onRejected);
}
MyPromise7.prototype.finally = function (cb) {
    return this.then(v => { cb(); return v }, r => { cb(); throw r })
}
MyPromise7.resolve = function (value) {
    return new MyPromise((resolve, reject) => {
        if (value instanceof MyPromise7) {
            return value.then(resolve, reject)
        }
        resolve(value);
    })
}
MyPromise7.reject = function (reason) {
    return new MyPromise7((resolve, reject) => reject(reason))
}
MyPromise7.all = function (arr) {
    return new MyPromise7((resolve, reject) => {
        if (!arr.length) { resolve([]); return }
        let count = 0;
        const result = [];
        arr.forEach((it, index) => {
            MyPromise7.resolve(it).then(res => {
                count++;
                result[index] = res;
                if (count === arr.length) {
                    resolve(result)
                }
            }).catch(reject);
        })
    })
}
MyPromise7.race = function (arr) {
    return new MyPromise7((resolve, reject) => {
        arr.forEach(it => it(resolve, reject))
        ⭐️ // 更好的写法 it => MyPromise7.resolve(it).then(resolve).catch(reject);
    })
}
MyPromise7.allSettled = function (arr) {
    return new MyPromise7((resolve) => {
        if (!arr.length) { resolve([]); return; }
        const result = [];
        let count = 0;
        arr.forEach((it, index) => {
            MyPromise7.resolve(it).then(value => {
                result[index] = { status: S7.resolved, value }
            }).catch(reason => {
                result[index] = { status: S7.rejected, reason }
            }).finally(() => {
                count++;
                if (count === arr.length) { resolve(result) }
            })
        })
    })
}
const getErr = info => new AggregateError(info, 'all rejected')
MyPromise7.any = function (arr) {
    return new MyPromise7((resolve, reject) => {
        if (!arr.length) { reject(getErr([])); return; }
        const result = [];
        let count = 0;
        arr.forEach((it, index) => {
            MyPromise7.resolve(it).then(resolve).catch(reason => {
                count++;
                result[index] = reason;
                if (count === arr.length) { reject(getErr(results)) }
            })
        })
    })
}
- 第七遍 2025.3.13 -
// 全部返回,reduce 处理的是同步任务,异步会出现异常
 // 1、all 中:arr 为空数组时,forEach 不会执行 if(arr.length===0){resolve([]);return;}
 // 2、all 中:不知道是不是所有的 peomise 都返回了,需要一个计数器
- 第六遍 2025.3.5 -
 // 1、race 中 it 需要包裹一层 MyPromise.resolve(it).then(resolve, reject)
- 第五遍 2025.2.11 -
- 第四遍 2025.1.20 -
- 第三遍 2025.1.19 -
 // 1、模拟 Promise 的时候,一般都是用函数
 // 2、onRejected 返回的依旧是 resolve 的 promise
 // 3、then:try-catch不能写成 catch(reject),catch接收的参数是error - catch(e){ reject(e) }
// 作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明
- 第九遍 2025.3.20 -
React 运行时编译 JIT  JSX 先编译成 AST再转换成 React.createElement
渲染前 shouldComponentUpdate(nextProps,nextState) / getDerivedStateFromProps
render
	1 生成 fiberNode tree创建 FiberRootNode = WIP.current创建 fiberNode 作为 HostRootFiber 开始 DFS 深度遍历 React.createElement 生成的 React Element 执行 beginwork 根据 wip.tag 对应处理执行到叶子节点 completeWork ⭐️ // 不是 WIP.current,FiberRootNode 作为根节点,HostRootFiber = FiberRootNode.current
    2 存储所有 hooks 链表 ⭐️ // memorizedState
    3 标记收集副作用链表 side-effect-list ⭐️ // updateQueue
	4 Diff 增量式计算最小更新 UI
commit
	 Diff 计算出的更新应用到真实 DOM + 所有生命周期和副作用链表componentDidMount componentDidUpdate componentWillUnMount + useLayoutEffect/ref

count 更新时更新对应 hooks 中的节点值触发组件更新
- 遍历执行整个 hooks如果是 useEffect 依赖的一员收集副作用链表 
- 生成新的 VDOM 之后和真实 DOM 对应的 VDOM 进行计算得到最小更新 UI
- 应用到真实 DOM再执行生命周期函数和副作用链表
- 第八遍 2025.3.13 -
 // 1、少写了第一步:动态编译 JIT - 将 JSX 描述编译为 React createElement 方法
 // 2、构建新的 Fiber 树 WIP Tree 过程需要了解
 // 3、初次渲染时,存储 hooks、标记收集副作用链表
- 第七遍 2025.3.5 -
 // 1、shouldComponentUpdate 在 render 之前被调用,不在 commit 阶段
- 第六遍 2025.2.11 -
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
 // 1、遍历执行 hooks 时,如果遇到有副作用的节点,除了被副作用链表收集,react 也会在节点 Node 上标记 fiberNode.subtreeFlags
 // 2、count 这里应该将「如果有副作用」改写为「要注意是否有 useEffect 的作用:根据依赖数组是否变化确定是否要更新」
// 作业23:写出js 代码的执行顺序。词法作用域的特点是什么? 
- 第四遍 2025.3.20 -
- 可执行代码 AST
- 执行上下文 - this
		   - 变量环境 var - 函数作用域先创建arguments再函数声明提升再变量声明提升
		   - 词法环境 let/const 只和代码所在位置有作用域链由多个执行上下文的变量环境组成 ⭐️ // 链表
'bye 坑'
"global name"
undefined 'a' 'b' 'err'
- 第三遍 2025.3.13 -
 // 1、js 分为【可执行代码】和【执行上下文】,执行上下文分为三个:词法环境、变量环境、this
 // 2、变量环境 先创建arguments再函数声明提升最后变量声明提升
- 第二遍 2025.2.24 -
- 第一遍 2025.1.20 -
// 2021-06 CSS性能优化
- 第二遍 2025.3.19 -
代码1 从右向左解析减少层级
2 使用 link 预解析线程来触发提前下载
3 动画使用合成层tansform opacityGPU 加速避免回流重绘减少高消耗的属性使用 ⭐️ // 减少使用昂贵属性
⭐️ // 减少重排的方式:少改变/访问大小、少增删DOM、少增删样式表、少伪类激活、少style属性
4 code splitting懒加载模块化只加载当前必要代码减小代码体积
- 第一遍 2025.2.19 -
// 2021.04 12 垃圾回收机制
- 第四遍 2025.3.19 -
 // 1、少写了栈的清空方式:移动指针,将不需要的数据所在的内存,在后续使用中覆盖
// ⭐️ 堆
v8引擎 根据代际假说分为新生代和老生代
新生代 - 存活生命周期短占用内存空间小清理频繁 - 副垃圾回收机制
	分成两块活动区和副区在活动区存储快满时将仍旧有效的变量依次排序复制到副区复制完后交换两个区不产生内存碎片// ⭐️ 对象区域、空闲区域
    如果清理两次还存活则升级为老生代
老生代 - 占用空间大主垃圾回收机制
	标记-清除产生内存碎片清理过程最后还要向左移动所有变量来处理
- 第三遍 2025.3.5 -
- 第二遍 2025.2.27 -
 // 1、新生代 清理过程:先标记,再有序地复制到空闲区 - 复制完后翻转两个区域
- 第一遍 2025.2.24 -
 // 2、少写了:v8 引擎 基于代际假说
 // 3、少写了老生代:标记-清理,产生大量不连续的内存碎片
// 2021.05 27 HTTP/3 改进的点
- 第四遍 2025.3.19 -
HTTP/3 使用 QUIC 协议 TCP 改用为 UDP彻底解决 TCP 在传输层因为丢包重传机制导致的队头阻塞
// ⭐️ UDP 无连接,TCP三次握手四次挥手,节省时间
- 第三遍 2025.3.3 -
- 第二遍 2025.2.27 -
 /** 1、详细比较对比
HTTP/3(应用层)			 HTTP/1.1 或 HTTP/2.0(应用层)  		 HTTP(应用层)
|						|									|
QUIC(传输层,基于 UDP)   									   TLS(安全层)
|						|									|
UDP(传输层)			 TCP(传输层)						  TCP(传输层)
|						|									|
IP(网络层)				 IP(网络层)						  IP(网络层)
|						|									|
数据链路层				 数据链路层							  数据链路层
|						|									|
物理层					  物理层								 物理层
**/
- 第一遍 2025.2.24 -
 // 1、少写了 HTTP/2.0 存在的问题:HTTP/2.0 并没有完全解决队头阻塞,传输层的 TCP 有丢包重传机制 - TCP 为了保证可靠的传输,丢包必须等待重新传输确认,其他的包即使收到了也只能放在缓冲区,上层应用拿不出来,被丢的包不回来,大家都取不出来
 // 2、少写了:HTTP/3.0 UDP 无连接,HTTP/2.0 TCP 三次握手、四次挥手
// 2021.07 babel  作用+原理+补丁
- 第四遍 2025.3.19 -
babel  ES6 代码转译为 ES5 代码方便浏览器 js 引擎执行使用 // ⭐️ 使开发者便捷开发
原理@babel/parse 将 ES6 代码处理为 ES6 的 AST // ⭐️ 词法分析 语法分析 parser
	 @babel/tranverse 将 ES6 AST 处理为 ES5 AST
	 @babel/generate 将  ES5 AST 处理为 ES5 js 代码 // ⭐️ generateor
补丁babal 只能处理原始 ES6 语法例如箭头函数特殊语法都要补丁实现转换 Promise async/await Iterator Symbol // ⭐️ Generator Set Map Proxy Reflect Object.assign
- 第三遍 2025.3.3 -
- 第二遍 2025.2.27 -
// ⭐️ Set Map Proxy Reflect Object.assign - @babel/polyfill
- 第一遍 2025.2.24 -
 // 1、原理:少写了解析过程:解析Parsing[词法分析+语法分析得到AST]-转换Transformation[遍历AST转换 ES6的AST转换成ES5的AST]-生成Code Generation[根据转换后的AST转换成ES5 js代码]
 // 2、不能转换的ES6语法:Iterator Generator Set Map Proxy Reflect Symbol 等全局对象 + Object.assign 全局对象上的方法;@babel/polyfill
// 2023-09 二叉树:前中后序、层序遍历-递归/非递归版本、层序遍历、路径和
- 第一遍 2025.3.19 -
递归版本 - 前序遍历遍历顺序:--
function preC(head){ 
     // 1、缺少退出条件 if(!root){ return [] }
    return [head.value, ...preC(head.left), ...preC(head.right)] 
}
递归版本 - 中序遍历遍历顺序:--
function inC(head) { 
     // 2、缺少退出条件 if(!root){ return [] }
    return [...preC(head.left), head.value, ...preC(head.right)] 
}
递归版本 - 后序遍历遍历顺序:--
function post(head){
     // 3、缺少退出条件 if(!root){ return [] }
    return [...preC(head.left), ...preC(head.right), head.value] 
}
非递归版本 - 前序遍历
function preC(head){
     // 4、缺少边界条件 if(!root){ return [] }
    const stack = [head];
    const result = [];
    while(stack.length){
        const current = stack.pop();
        result.push(current.value);
        if(current.right){ stack.push(current.right); }
        if(current.left) { stack.push(current.left);  }
    }
    return result;
}
非递归版本 - 中序遍历
function inC(head) {
    const stack = [head]; // ❌ 逻辑问题 1:stack 应该初始为 [],配合 current,在循环里面添加首项
    const result = [];
    const current = head; // ❌ 应该是用 let 而不是 const
    while(stack.length){ // ❌ 逻辑问题 2:current 存储根节点/右节点,stack 可能会空 - 条件改为:stack.length || current
        while(current) {
            // ❌ 逻辑问题 3:在循环里面 push current,所以不需要判断 current.left 是否存在
            if(current.left) {
                stack.push(current.left); // ❌ 改为 push current
                current = current.left;
            }
        }
        current = stack.pop();
        result.push(current.value);
        if(current.right){ stack.push(current.right); } // ❌ current = current.right
        // ❌ 逻辑问题 4:使用 current 来存储下一个要推入 stack 的右节点,而不是直接推入
    }
    return result;
}
非递归版本 - 后序遍历
function postC(head){
     // 5、缺少边界条件 if(!root){ return [] }
    const stack = [head];
    const result = [];
    while(stack.length){
        const current = stack.pop();
        result.push(current.value);
        if(current.left) { stack.push(current.left);  }
        if(current.right){ stack.push(current.right); }
    }
    return result.reverse();
    // ⭐️ 可以优化为在 result 存储值时使用 unshift 添加 + 返回 result 即可
}
层序遍历
1 从上至下从左到右
2 左视图
1 从上至下从左到右 - 递归版本
function list(head, layer = 0, res = []) {
    if(!head) { return [] }
    if(!res[layer]) {
        res[layer] = [];
    }
    res[layer].push(head)
    
    layer++;
    [...list(head.left, layer, res), ...list(head.right, layer, res)];
     // 1、这里的写法很怪异,目的是触发 list 调用即可,合并为: list(head.left, layer + 1, res); list(head.right, layer + 1, res)
    return res
}
/** ⭐️  
function levelOrder(head) {
	const result = [];
	if(root) { FindLayer(root, result, 0) }
	return result;
	
	function FindLayer(node, layer, res) {
		if(!Array.isArray(res[layer])) {
			res[layer] = [];
		}
		res[layer].push(node.value);
		layer++;
		if(node.left) { FindLayer(node.left,  layer, res) }
		if(node.right){ FindLayer(node.right, layer, res) }
	}
}
*/
1 从上至下从左到右 - 非递归版本 - BFS 广度优先 队列实现
 // 1、想不出来
function list(head) {
    if(!head) { return [] }
    const queue = [head];
    const result = [];
    while(queue.length) { // 队列长度
        const size = queue.length;
        const current = [];
        for(let i = 0; i < size; i++) { // 队列长度限制
            const node = queue.shift();
            current.push(node.value);
            if(node.left) { queue.push(node.left) }
            if(node.right){ queue.push(node.right) }
        }
        result.push(current)
    }
    return result
}
2 左视图
function leftView(head) {
    if(!head) { return [] }
    const queue = [head];
    const result = [];
    while(queue.length) {
        const size = queue.length;
        const current = [];
        for(let i = 0; i < size; i++) {
            const node = queue.shift();
            current.push(node.value);
            ⭐️ // 1、在 for 循环中 current.push(node.value) + 在完成一个循环后,投入返回结果中 result.push(current[0]) -> 可以简单改写为 if(i===0){ result.push(node.value) }
            if(node.left) { queue.push(node.left)}
            if(node.right) {queue.push(node.right)}
        }
        result.push(current[0])
    }
    return result
}
路径和
function findPath(head, target){
    const result = [];
	const queue = [head];
    let rest = target;
    while(queue.length){
        const current = [];
        const size = queue.length;
        for(let i = 0; i < size; i++) {
            const node = queue.shift();
            current.push(node.value);
            rest = rest - node.value;
        }
        
    }
}
function findPath(head, target) {
    const result = [];
    function backTrack(node, target, path) {
        if(!node) return;
        path.push(node.value);
        if(!node.left && !node.right && target === node.value) {
            result.push([...path])
        }
        backTrack(node.left, target - node.value, path);
        backTrack(node.left, target - node.value, path);
        path.pop();
    }
    backTrack(head, target, []);
    return result;
}
// 2025-03 服务器 不明文密码处理
- 第二遍 2025.3.19 -
在服务器中配置文件 config.env
在项目中通过插件访问到该配置文件 - 由于当前项目在 docker 容器中启动
npm i dotenv
// ⭐️ dotenv 是零依赖的 Node 模块,主要作用是将环境变量从 .env 加载到 process.env 中
import dotenv from 'dotenv'
dotenv.config(path.resolve(_dirname, './config'))  // 1、config 配置的参数接收 path 属性 dotenv.config({ path: path.resolve(_dirname, './config') })
const KEY = process.env.KEY;
 docker-compose.yml 中将外部文件引入容器
volumns:
	- ../config.env:/app/config.env
- 第一遍 2025.3.11 -
 // 1、yml 配置的路径应该是 ../config.env 从项目外引入挂载到指定路径 + 文件名是 docker-compose.yml + volumes 卷,单词错了
 // 2、dotenv 配置时,传入具体的地址 path.resolve(__dirname, '../config.env')
// 2021.06 flex布局
- 第五遍 2025.3.19 -
1 便捷布局 flex-direction:column/row/column-reverse/row-reverse
主轴默认可缩小不可放大 justify-content:space-around[-a--b--c-] space-between[a--b--c]
交叉轴默认可放大拉伸 align-iterms: stretch
2 flex: flex-grow正整数是对flex-basis的倍数 flex-shrink flex-basis
3 flex 会让 float clear vertical 失效
4 独立容器 从上到下垂直排列 不和外部浮动元素重叠 计算高度时包含浮动元素
5 order align-self可以覆盖排列方式 // ⭐️ 允许单个项目与其他不一样的对齐方式
- 第四遍 2025.3.11 -
 // 1、少写了 flex flex-grow(基于basis的整数倍) flex-shrink flex-basis
- 第三遍 2025.3.2 -
// ❌ 1、flex-direction 中的值 row/column 没有 s
// ❌ 2、主轴默认不拉伸,但默认缩小
- 第二遍 2025.2.27 -
 // 1、少写了 flex 导致 float clear vertical-align 失效
- 第一遍 2025.2.23 -
// 作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?
- 第六遍 2025.3.18 -
13.x Link 之前内部写 a 标签之后 Nextjs 直接解析为 a 标签所以直接写 href 属性
路由文件系统  pages/index.js 改为 src/app/page.tsx + layout.ts + global.css  // 1、公共布局文件的路径记错了 src/app/layout.ts + src/app/global.css
 src/app/api 作为后端路由
- 第五遍 2025.3.13 -
 // 1、少写了:由之前的 pages/index.js 改为 src/app/page.tsx ,并支持共享布局 layout.tsx;从 pages/api 改为 src/app/api 
- 第四遍 2025.2.16 -
- 第三遍 2025.1.31 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
 // 1、只写了「文件路由系统」的变更,还少写了「后端路由系统」的变更 从 pages/api 变更为 src/app/api
// 作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?
- 第六遍 2025.3.18 -
path.join() 拼接两个路径
path.resolve() 解析为绝对路径/ 为根目录
process.cwd() node 执行环境的当前上下文
__dirname 文件所在的目录路径 // ⭐️ 每个模块的内部变量
- 第五遍 2025.3.13 -
 // 1、path.resolve 解析得到绝对路径,把 / 当做根目录
 // 2、__dirname 当前模块的目录名
- 第四遍 2025.2.16 -
- 第二遍 2025.1.29 -
- 第二遍 1.26 -
- 第一遍 2025.1.22 -
 // 2、process.cwd():node 执行时所在目录
// 作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序)
- 第六遍 2025.3.18 -
SELECT DISTINCT t.num,
    CONCAT('()', t.name, t.age, ')') as name,
	UPPER(t.age) as age,
    COUNT(t.sex) as without_null,
    COUNT(*) as with_null,
	MAX(t.age) as max,
    MIN(t.age) as age,
    SUM(t.age) as sum,
    AVG(t.age) as avg,
FROM routine as r, time as t
WHERE t.id = 1 OR  // 1、没有使用 AND 和 ()
    t.price > 10 OR
    t.price IS NULL OR
    t.price BETWEEN 5 AND 10 OR
    t.price IN (1,2) OR
    YEAR(t.age) = 2025
GROUP BY t.name
SORT BY t.age DESC  // 2、不是 sort 是 ORDER BY ..
- 第五遍 2025.3.11 -
 // 1、IN 后面要接小括号 (1, 2)
- 第四遍 2025.2.16 -
- 第三遍 2025.1.30 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.23 -
// 作业80:对DOM 树的理解 - 定义和作用
- 第六遍 2025.3.18 -
DOM 树是 HTML 的映射表示 HTMl 的结构 // ⭐️ 对象化表示,是树形结构的对象模型
浏览器 DOM树是浏览器渲染页面结构的基础 // ⭐️ 构建页面布局
js  DOM   js 可以操作访问 HTML 的接口 ⭐️ // 编程接口
框架 VDOM 可以计算最小更新 UI 提高 Diff 增量式效率
- 第五遍 2025.3.11 -
 // 1、少写了:DOM 是树形结构的对象模型
 // 2、少写了:按照 DOM 结构构建「页面布局」
 // 3、是更新而不是渲染:提高页面更新性能
- 第四遍 2025.2.16 -
- 第三遍 2025.1.31 -
 // 1、少写了浏览器视角-渲染页面的依据 和 框架视角-VDOM Diff 计算提高更新性能
- 第二遍 2025.1.25 -
- 第一遍 2025.1.24 -
// 2021.07 防抖节流
- 第七遍 2025.3.18 -
function debounce(fn, delay) {
    let timeId;
    return (...arg) => {
        if(timeId) {
            clearTimeout(timeId)
        }
        timeId = setTimeout(() => {
            fn(...arg)
        }, delay)
    }
}
function throttle(fn, delay) {
    let flag = false;
    return (...arg) => {
        if(flag) {
            return
        }
        flag = true;
        fn(...arg);
        setTimeout(() => { flag = false }, delay)
    }
}
- 第六遍 2025.3.11 -
 // 1、防抖:timeId 已存在,则清理定时器,保证不停读条,覆盖上一次 clearTimeout(time)
 // 2、正在触发时,不执行 if(flag)
- 第五遍 2025.2.23 -
- 第四遍 2025.2.18 -
- 第三遍 2025.2.16 -
 // 1、debounce 遗漏赋值:timeId = setTimeout()
 // 2、throttle:判断条件应该是 if(flag):flag 为 true 时,证明这段时间内已经执行了
- 第二遍 2025.1.21 -
// 2021.09 Object属性排序、与Map的区别
- 第七遍 2025.3.18 -
排序: 正整数>字符串>负数>浮点数>Symbol
区别有序存储键可以是任意值可迭代对象map.size()大量数据时map空间小 增删快
- 第六遍 2025.3.11 -
 // 1、浮点数和负数顺序反了:浮点数在后
 // 2、少写了键:Map 的键可以是任意值,Object 只有字符串和Symbol
- 第五遍 2025.2.16 -
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.21 -
 // 1、少写了浮点数
// 作业20:浏览器打开一个 URL 发生了什么
- 第七遍 2025.3.18 -
1 浏览器进程 UI线程 - 将用户输入转换成 查询链接/完整url
2 通过 IPC机制 传递给网络进程 查询本地缓存 - 有缓存且有效强制缓存生效
3 无缓存有缓存但失效  解析 DNS 得到 ip
 TCP 排队三次握手建立连接
 组装请求头cookie 信息 // ⭐️ 组装HTTP请求
 发起请求 301/302-重定向Location 304-协商缓存生效使用本地缓存并刷新有效时间
		  200 stream流 下载停止导航html 类型 通过管道连接 渲染进程
4 通过管道连接渲染进程变下载边解析
传输完成后触发浏览器的 前进/后退url页面安全锁
下载完成解析还没完成时触发解析白屏
渲染进程-主线程合成线程预解析线程在预解析线程中提前下载 js css 等资源
主线程解析DOM(DOM树)-样式计算(CSSOM)-布局(布局树)-分层(分层树)-绘制(指令列表)-
合成线程-栅格化(图块转换成位图) GPU进程加速-
浏览器 UI线程 合成()
- 第六遍 2025.3.11 -
 // 1、先 TCP 排队,再组装 HTTP 请求
 // 2、传输完成后触发更新页面,而不是下载后
 // 3、渲染流程中,布局阶段后,不是合成阶段,是分层 得到分层树
- 第五遍 2025.2.16 -
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.20 -
 // 1、协议名错了:IPC - 进程通信协议
 // 2、少写了:有缓存但缓存失效
 // 3、应该是先判断状态码,200的状态码下再判断类型
 // 4、少写了 304,使用本地缓存,并刷新缓存有效时间
// 2025-03 TCP 滑动窗口 VS TCP 排队
- 第一遍 2025.3.17 -
TCP 滑动窗口: 
	传输层只影响单个 TCP 的发送速率
    根据接收方的窗口大小动态调整发送方的请求发送速率
    保证最大程度使用接收方的处理能力但不会因为请求量过大导致影响处理  // 1、少写了:实现流量控制
TCP 排队:
	影响应用层是浏览器限制同个域名并发请求的数量
    优化 HTTP/1.1 请求应答模式导致的队头阻塞
    保护服务端能够安全处理请求响应避免请求量过大服务器处理不了
// 2025-03 js执行优先级 + 测试题
- 第一遍 2025.3.17 -
js执行优先级()小括号>new Foo()>.属性名调用>()函数执行
 // 1、排序 ()小括号>.属性名调用>new Foo无参数>函数执行()>new Foo()有参数
var a = new Foo.getName();  
顺序new (Foo.getName)() - new 函数() - 打印3a = {}
var b = new Foo().getName(); 
顺序(new Foo()).getName() - (实例.getName)() - 打印2只是执行函数b = undefined
var c = new new Foo().getName(); 
顺序new (new Foo()).getName() - new (实例.getName)() - 打印2c = {}
// 2025-03 浏览器前端性能优化RAIL
- 第一遍 2025.3.17 -
Response响应 300ms   // 1、100ms 内响应交互
Animation动画 css动画合成线程 GPU 加速避免回流重绘requestAnimationCallback 重绘时间60/秒不卡顿  // 2、重绘时间函数名称错误 requestAnimationFrame
Idle休闲时间 requestIdleCallback 在空闲时间执行数据计算等 // ⭐️ 预加载数据、加载非关键资源
LoadFCP 1s内加载完成白屏优化  // 3、3秒内
// 2022-10 TS 泛型函数/泛型接口/泛型约束、特性、type/keyof/enum/as/Partial
- 第一遍 2025.3.17 -
泛型函数 function <T, R>(a:T, b:R):[T, R] { return [a, b] }
 // 1、少写了函数名称:function getCode<T, R>(a:T, b:R):[T, R] { return [a, b] }
泛型接口 interface <T>MyCode{ value:T, function func(v:T):T[] {return [v]} }
 // 2、泛型写在名称后面 interface MyCode<T>{ value:T, function func(v:T):T[] {return [v]} }
泛型约束 interface <T extends {name: string}>MyCode{ v: T }
 // 3、泛型写在名称后面 function getCode<T extends {v:number}>(a:T):T {return a}
TS 特性:①面向接口编程 编译就是去除类型校验代码保留功能代码
enum 声明一组常量 enum C {a=1,b=2}
type 类型别名 type Code = string | number / Partial<C>  将属性变为可选
keyof 获取类型接口的属性作为联合类型校验 keyof C = "a" | "b"
as 强制类型转换 // ⭐️ 类型断言
// 2025.02 docker-compose 的配置管理 context 
- 第三遍 2025.3.17 -
context 配置 docker 构建上下文
 dockerfile 执行 docker 启动时COPY MOVE 会根据配置的根目录来处理  // 1、没有 move 指令,是 ADD 指令(可以解析tar文件
- 第二遍 2025.3.10 -
// ❌ 1、指定构建上下文路径
// ❌ 2、dockerfile + COPY ADD
- 第一遍 2025.2.28 -
// 2025.02 TS - dayjs 引入但失效问题
- 第三遍 2025.3.17 -
dayjs  CommonJS模块但现代框架使用 ES6
 tsconfig.ts 中配置 
esModuleInterop: true -  CommonJS  ES6 转换
allowSyntheticImportDefault: true - 解决 CommonJS 没有默认导出的问题  // 1、allowSyntheticDefaultImports 属性名称错误 - 记忆法:允许异步默认导入s
- 第二遍 2025.3.10 -
- 第一遍 2025.2.28 -
// 2021-04 01 浏览器进程组成 + 复用渲染进程 + 进程/线程
- 第一遍 2025.3.17 -
浏览器浏览器进程x1 渲染进程xn 插件进程xn 网络进程x1 GPU进程x1
复用渲染进程相同站点复用进程同协议同域名同端口
进程:①有独立的资源空间 是程序的实例  多个进程间独立通讯IPC协议管道消息队列等
线程:①没有资源多个线程共享同一进程的数据 是进程的一个实体  线程间数据共享但是有同步限制互斥锁-一次只能有一个线程访问流量体-一次只能限制某个数量的线程访问读写锁-读读不互斥读写写写互斥
// ⭐️ 不是“流量体”,是“信号量”

LTN ①⑨

LTN ①⑨ 共计 30错题/93 题(推荐做题时间 3.7-3.18):LTN1-38题 LTN2-31题 LTN3-11题 LTN4-16题

LTN1 【推荐做题时间 03-07 - 6题 实际做题时间: 3.10】 -✅作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ✅作业93:闭包的作用和原理 ✅2021-07 前端路由的两种模式 ✅2025.02 sequelize 在 Nestjs 中的 env 配置 ❌2025.02 TS - dayjs 引入但失效问题 ❌2025.02 docker-compose 的配置管理 context

LTN2 【推荐做题时间 03-07 - 1题 实际做题时间: 3.10】 ✅作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据

LTN1 【推荐做题时间 03-08 - 6题 实际做题时间: 3.10】 ✅作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅作业61:常见的数据结构有哪些 ✅2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝? ✅作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ✅2025.02 Mysql 8.0 以上,Sequel报插件错 ✅2025.02 Nextjs 获取链接中的参数

LTN1 【推荐做题时间 03-09 - 6题 实际做题时间: 3.11】 -✅作业66:React如何处理错误 ✅2021.06 跨页面通信 ✅2019.07 数组去重(算法整理) ✅2021.07 柯里化函数实现 ❌2021.06 flex布局 ❌2025-03 服务器 不明文密码处理

LTN2 【推荐做题时间 03-09 - 4题 实际做题时间: 3.11】 -❌2021.07 防抖节流 ✅2024.12 模拟实现jsonp ✅作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ✅作业4:什么是生成器?有什么作用?

LTN3 【推荐做题时间 03-09 - 7题 实际做题时间: 3.11 + 3.13】 ❌作业20:浏览器打开一个 URL 发生了什么 ❌2021.09 Object属性排序、与Map的区别 ❌作业80:对DOM 树的理解 - 定义和作用 ❌作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ❌作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别? ❌作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更? -✅2021.07 bind、apply/call三者异同+apply/call实现bind

LTN2 【推荐做题时间 03-10 - 1题 实际做题时间: 3.13】 ❌作业23:写出js 代码的执行顺序。词法作用域的特点是什么?

function bar(){
    console.log(myName);
}
function foo(){
    var myName = 'hi 坑';
    bar();
}
var myName = 'bye 坑';
foo();
--
var name = "global name";
var obj = {
    name: 'Alice',
    getName: function() { console.log(this.name) }
}
var getName = obj.getName;
getName();
--
(function(){
    try{
        console.log(1, a);
        var a="a";
        console.log(a);
        b();
        c();
        function b(){
            console.log("b");
        }
        var c=function(){
            console.log("c");
        }
        console.log("d")
    }catch(e){
        console.log('err')
    }
})() 

LTN1 【推荐做题时间 03-11 - 1题 实际做题时间: 3.13】 ✅2024.10 第九章 代理与反射 小结

LTN1 【推荐做题时间 03-12 - 6题 实际做题时间: 3.13】 ❌作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 ❌-2024.12 模拟实现promise ❌2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程 ✅2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序 ❌2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码 ❌2025.02 Express VS KOA2

LTN1 【推荐做题时间 03-13 - 4题 实际做题时间: 3.13 + 3.14】 ✅作业64:Vue渲染怎么实现的? ✅2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现? -❌2025-3 NextJS 全局中间件-校验权限 ❌2025-3 NestJS 守卫-校验权限

LTN1 【推荐做题时间 03-14 - 1题 实际做题时间: 3.14】 ❌2025-03 p-limit 使用+源码 、100个请求并发请求

LTN2 【推荐做题时间 03-14 - 10题 实际做题时间: 3.15】 ✅作业40:写出以下值

1Number(null)
2Number(undefined)
3isNaN() 检测那些值为 true?
4NumberparseIntparseFloat 检测 空字符串 得到?
51+'2'+'2'
1+ +'2'+'2'
1+ -'1'+'2'
'A'-'B'+'2'
'A'-'B'+ 2
6let i = 0, age = 30;
i++ + age;
++i + age;
7 12.34 | 0;
12.43 >> 0;
8null === undefined

✅作业32:什么是队头阻塞?原因是什么?怎么解决? -✅作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码 ✅2019.07 引用css,link和@import的区别 ❌作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法。写个POST调用流程 ✅2024-11 第十九章 表单脚本 ✅2024-10 第十二章 BOM 小结 ✅2024-09 第七章 迭代器与生成器 小结 ✅2019-06 第五章 基本引用类型 小结 ✅作业100:写一个原生的 form ,写出两个提交表单的方式 + 阻止提交方式,表单字段的公共属性/方法,怎么校验表单

LTN1 【推荐做题时间 03-15 - 1题 实际做题时间: 3.15】 ✅2025-03 Promise 的异步任务并发函数

LTN2 【推荐做题时间 03-15 - 7题 实际做题时间: 3.15 + 3.16】 ❌作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ✅作业1:写一个只能被迭代指定次数的类 ✅作业11:写出一个用 yield 实现的递归算法,从0数到指定n ✅作业 89:mysql 中类型的区别:varchar 和 char,date datetime ✅作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置 -❌作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ❌69 x 的平方根

LTN3 【推荐做题时间 03-15 - 1题 实际做题时间: 3.16】 ✅作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?

LTN4 【推荐做题时间 03-15 - 10题 实际做题时间: 3.16】 ✅作业10:分别用两种方式新建一个映射实例、一个集合实例 ✅作业29:Promise的进度通知 ✅作业44:怎么能够不把所有事情、语言往心里去 ❌作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置 ✅作业50:学习的感觉是? -❌作业51: 如何停止过度思考?如何停止焦虑 ✅作业52:道可道,非常道 翻译一下,有什么含义 ✅作业56:对那些没有 deadline 的事情,怎么解决拖延症 ✅2024.11 第十六章 DOM2和DOM3 ✅2021.04 02 网络协议 - IP/UDP/TCP协议

LTN2 【推荐做题时间 03-16 - 8题 实际做题时间: 3.16】 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅2023.07 clickhouse、mysql、mongodb异同 ✅2019.07 显示省略号 ✅2021.06 子盒子在父盒子中水平垂直居中有几种方法? ✅作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景 -✅2021.07 12-1 内存泄漏 ✅作业99:渲染合成层定义、触发方式、优点、缺点

LTN4 【推荐做题时间 03-16 - 6题 实际做题时间: 3.16】 ❌作业17:对斐波那切数列进行尾调用优化 ✅作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现 ✅作业34:写出强缓存、协商缓存的流程及相关属性 ✅作业57:“假装成功”的意义是什么 ✅2021.06 BFC特点、触发、存在/解决的问题 ✅2020.03 改变原数组+结束循环+性能排序

LTN1 【推荐做题时间 03-17 - 4题 实际做题时间: 3.16】 ❌2024.10 怎么实现跨域 ❌2019.10 两栏,左边固定,右边自适应的布局 -✅作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据 ✅作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?

LTN3 【推荐做题时间 03-17 - 3题 实际做题时间: 3.16】 ✅作业6:new操作符实例化一个对象的过程 ✅作业3:迭代器原理 ✅作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果

Promise.resolve().then(() => {
    console.log(1);
    Promise.resolve().then(() => {
        console.log(2);
    }).then(() => {
        console.log(3);
    }).then(() => {
        console.log(4);
    })
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);

})
    ------

async1 = async () => {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
function async2(){
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
})
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
    return new Promise(function(resolve) {
        resolve();
    })
}).then(() => {
    console.log('promise3');
})
console.log('script end');

做题记录

// 作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果
- 第五遍 2025.3.16 -
事件 - 同步任务 - 主线程执行 - 调用栈清空之前查询事件队列 - 循环清空事件队列完成一次宏任务
	- 异步任务 - Event Table - 异步函数有返回时将回调函数投入事件队列 -
宏任务I/O下载交互js 代码执行渲染setTimeout/setInterval
微任务Promise.then async/await MutationObserver
- 1 2 5 3 6 4
script start - async1 start - async2 - promise1 - script end
    - async1 end - promise2 - promise3
    - setTimeout
- 第四遍 2025.2.24 -
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
⭐️ // Event Queue + 触发时机 - 等异步任务有了结果,将回调函数投入事件队列
- 第一遍 2025.1.30 -
// 作业3:迭代器原理
- 第六遍 2025.3.16 -
使用 next 访问可迭代对象的每一个值返回的结构是 {value, done}
默认迭代器声明在 [Symbol.iterator]
- 第五遍 2025.2.24 -
- 第四遍 2025.2.11 -
- 第三遍 2025.2.9 -
- 第二遍 2025.1.30 -
 // 迭代器使用 next 方法在可迭代对象中遍历数据
- 第一遍 2024.12.25 -
// 作业6:new操作符实例化一个对象的过程
- 第六遍 2025.3.16 -
function newFunc() {
    const obj = new Object();
    const ctor = [].shift.call(arguments);
    obj.__proto__ = ctor.prototype;
    const res = ctor.call(obj, ...arguments);
    return typeof res === 'object' ? res : obj;
}
- 第五遍 2025.2.24 -
- 第四遍 2025.2.10 -
- 第三遍 2025.2.9 -
- 第二遍 2025.1.28 -
 // 1、实例的 __proto__ 指向的不是构造函数,是构造函数的原型对象 ctor.prototype;
- 第一遍 2025.1.20 -
 // 1、不是调整 constructor 的指向,是设置实例的原型 __proto__
// 作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?
- 第五遍 2025.3.16 -
const start = dayjs.utc(new Date(time)).startOf('day').toDate();
const end = dayjs.utc(new Date(time)).endOf('day').toDate();
const where = {
    date: {
        [op.between]: [start, end]
    }
}
const where2 = sequelize.where(sequelize.fn('DATE', sequelize.col('date')),
                           '=',
                           time)
const where3 = sequelize.where(sequelize.fn('DATE_FORMAT', sequelize.col('date'), '%y-%m-%d'),
                              '=',
                              time)
op.between 性能更高因为可以使用索引查询
索引是单独于数据表外的 b-tree 结构查询时可以直接查询结果而不是遍历整个表的值进行计算
- 第四遍 2025.3.8 -
 // 1、utc 使用方式错误 dayjs.utc(传递 Date类型) + 缺少转换成 Date 类型 .toDate()
 // 2、sequelize.fn 传递的第二个参数是 Sequelize.col('date')
 // 3、DATE 函数直接判断是否等于 time;DATE_FORMAT 函数要传递参数 '%y-%m-%d' 再判断是否等于 time
- 第三遍 2025.2.22 -
- 第二遍 2025.2.17 -
 // 1、函数式写法,少写了 sequelize.where 
- 第一遍 2025.2.13 -
 // 1、需要先将传入的 string 转换成 date
// 作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据
- 第五遍 2025.3.16 -
Nextjs fetch 返回的 Response 对象包含了相应行响应头响应体状态码等信息
await response.json() 将响应体中的数据解析为 js 对象
plain:true findOne 去除 Sequelize 实例的其他内容 // ⭐️ 单条记录,查询结果扁平化
raw:true findAll/findOne 只返回查询结果不将查询结果进行实例化直接返回
- 第四遍 2025.3.8 -
 // 1、findOne({ where, raw: true }) - fineOne 中两个配置属性都能用
 // 2、plain:将查询结果-单条-转换为扁平化对象,输出一个简单的 js 对象,而不是 Sequelize 的实例对象
 // 3、raw:直接返回 SQL 原始的查询结果 数组/对象,不进行Sequelize实例化操作
- 第三遍 2025.2.21 -
- 第二遍 2025.2.17 -
 // 1、状态码、响应头、响应体
 // 2、await res.json()
- 第一遍 2025.2.12 -
// 2019.10 两栏,左边固定,右边自适应的布局
- 第六遍 2025.3.16 -
.left{width:200px}
1 .left{ float:left } .right{ margin-left:100px }
2 .left{ position:absolute } .right{ margin-left:100px }
3 .outer{ display:flex } .right{flex:1}
4 .outer{ display:table; width:100% } .left,.right{ diaplay:table-cell }
.outer{ display:gird; grid-template-columns: 100px auto; }
5 .left,.right{float:left} .right{width:calc(100% - 100px)}
 // 1、少写了 BFC .left{float:left} .right{overflow:hidden}
- 第五遍 2025.3.8 -
 // 1、grid-template-columns
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
 // 2、父盒子变成弹性盒子后, right 要变成可拉伸 .right { flex: 1 }
// 2024.10 怎么实现跨域
- 第六遍 2025.3.16 -
jsonp 使用 script 标签的 src 可以访问第三方资源  // 1、只支持 GET 请求
nginx 代理服务器之间没有同源协议限制
CORS 跨域资源共享Allow-Control-Access-Origin 服务端允许访问的域名 - 响应头meta设置
 // 2、属性名错误 Access-Control-Allow-Origin、配置方式是响应头
Websocket 不受同源协议限制
- 第五遍 2025.3.8 -
 // 1、jsonp 只能处理 GET 请求
 // 2、CORS 服务端允许的哪些域名可以访问 - 是为了让浏览器知道哪些域名可以访问,而不是从客户端角度描述
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
// 2020.03 改变原数组+结束循环+性能排序
- 第四遍 2025.3.16 -
push pop shift unshift sort reverse splice fill
return break - forEach filter map reduce
for > for-of > forEach > map > for-in
- 第三遍 2025.2.16 -
- 第二遍 2025.1.21 -
// 2021.06 BFC特点、触发、存在/解决的问题
- 第四遍 2025.3.16 -
BFC特点:①从上至下垂直排列 独立容器 不和外部浮动元素重叠 高度计算包含浮动元素
触发:①float不为none 
overflow不为staticscroll/hidden/auto // ⭐️ overflow 不为 visible
根元素 display:flex/inline-block/table-cell // ⭐️ 少写了 table-caption
position absolute/fixed 
存在问题内部上下盒子的margin 会重叠外边距重叠
解决方案.outer{display:flex}
- 第三遍 2025.2.16 -
- 第二遍 2025.1.21 - 
// 作业57:“假装成功”的意义是什么
- 第四遍 2025.3.16 -
不是 Fake it until make it 而是 fake it until you become it
大脑会跟随你的语言行动心理的意义能够让身体有积极感受不是一直伪装而是直到自己真的变成这样
You deserved to be here
- 第三遍 2025.2.16 -
⭐️ // 控制激素-睾丸酮,压力激素-皮质醇
- 第二遍 2025.1.21 -
// 作业34:写出强缓存、协商缓存的流程及相关属性
- 第四遍 2025.3.16 -
强缓存 发起请求前浏览器本地查找有缓存则直接使用缓存强缓存生效//⭐️ Cache-control:max-age/Expires
协商缓存 缓存失效 If-none-match/ETag If-modified-since/Last-modified 304 协商缓存生效 ⭐️ // 刷新本地缓存时间
- 第三遍 2025.2.16 -
- 第二遍 2025.1.21 -
// 作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现
- 第四遍 2025.3.16 -
Promise.all([A(), B()]).then(C);
async function func() {
    await Promise.all([A(), B()]);
    C();
}
- 第三遍 2025.2.16 -
- 第二遍 2025.1.21 -
// 作业17:对斐波那切数列进行尾调用优化
- 第四遍 2025.3.16 -
function fib(n) {
    if(n<2) return n;
    return fib(n-1) + fib(n-2)
}
function opFib(a = 0, b = 1, n) {
    if(n === 0) return 0;  // 1、return a
    return opFib(a+b, b, n - 1)  // 2、第二个参数 应该是 a,否则 b 一直是 1
} 
- 第三遍 2025.2.16 -
 // 1、参数传递可以传 b,a+b,n-1 或者 a+b,a,n-1
- 第二遍 2025.1.20 -
// 作业99:渲染合成层定义、触发方式、优点、缺点
- 第四遍 2025.3.16 -
DOM - CSSOM - 布局 - 分层 - 绘制 - 栅格化 - 合成
将页面结构划分成多个层有独立的渲染机制互不影响最后合成 // ⭐️ 每个层都有自己独立的绘制和处理流程
触发方式:①视频3D will-change opacity transform等 // ⭐️ canvas 元素
优点分层后有修改时只修改当前层避免其他无关布局的回流重绘提高性能 // ⭐️ GPU 加速合成层
缺点分层越多维护的内存消耗越多合成时消耗的性能越多
- 第三遍 2025.3.2 -
- 第二遍 2025.2.27 -
 // 1、少写了:优点 实现流程流畅的动画效果
- 第一遍 2025.2.23 -
 // 1、少写了:渲染时,为了提高渲染效率和性能,将页面中不同元素划分成多个层
// 2021.07 12-1 内存泄漏
- 第四遍 2025.3.16 -
不再使用的对象但是无法被垃圾回收机制清理 - 内存泄漏
清理定时器 clearTimeout/clearInterval 移除事件监听 removeEventListener/off 少写闭包全局变量 尽可能使用弱引用 WeakMap/WeakSet或者删除 Map.delete(key)/Set.delete(value)
- 第三遍 2025.3.2 -
- 第二遍 2025.2.27 -
// ⭐️ 内存 Chrome Memory 录制查看
- 第一遍 2025.2.23 -
// 作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景
- 第五遍 2025.3.16 -
WeakMap/WeakSet 弱引用键的类型为对象当空对象时可被清理
	- 私有变量键是 DOM 节点
MapSet 强引用键的类型是任意值只要被 Map  Set 引用就无法被垃圾回收机制清理
- 第四遍 2025.3.2 -
- 第三遍 2025.2.27 -
- 第二遍 2025.2.23 -
 // 1、Map 和 Set 对存储的对象持有「强引用」,只要它们存在,对象就不会被回收 - 即使在其他地方没有对该对象的引用,Map 或 Set 内部的引用会阻止垃圾回收器将其回收
 // 2、而 WeakMap 和 WeakSet 对存储的对象持有「弱引用」,不会阻止对象被垃圾回收
- 第一遍 2024.12.25 -
// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第七遍 2025.3.16 -
 .father{ display: flex; align-iterms: center; justify-content: center; }
 .father{ display: table-cell; width: 100%; vertial-align: middle; text-align: center; }
.son { display: inline-block } // ⭐️ 不需要 width
 .son{ position: absolute; top:0; right:0; bottom:0; left:0; margin:auto; }
 .son{ position: absolute; top:50%; left:50%; transform:translate(-50%, -50%); }
- 第六遍 2025.3.2 -
- 第五遍 2025.2.27 -
- 第四遍 2025.2.24 -
 // 1、table 少写了 .son{ display: inline-block }
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
- 第一遍 2025.1.30 -
 // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中
// 2019.07 显示省略号
- 第七遍 2025.3.16 -
.line {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.lines {
    overflow: hidden;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    
    padding: 20px;
    line-height: 30px;
    height: 30*3-20px;
}
- 第六遍 2025.3.2 -
- 第五遍 2025.2.27 -
 // 1、属性值错误 -webkit-box
- 第四遍 2025.2.23 -
 // 1、属性名错误 -webkit-box-orient: vertical;
- 第三遍 2025.2.2 -
// 2023.07 clickhouse、mysql、mongodb异同
- 第九遍 2025.3.16 -
mysql 关联数据库可以存储结构型数据支持事务操作读写性能好在高并发时有一致性问题
mongodb 文档类型数据库存储半结构 JSON HTML XML等或者非结构型数据读的性能好写的性能弱在异步操作时有一致性问题 // ⭐️ 分布式场景
clickhouse 列式数据库读写性能好支持异步操作不支持事务操作适合高吞吐低延迟场景 // ⭐️ 支持分布查询
- 第八遍 2025.3.2 -
- 第七遍 2025.2.27 -
- 第六遍 2025.2.23 -
 // 1、少写了:clickhouse 支持分布式操作
- 第五遍 2025.2.18 -
 // 1、mysql 少写了:支持事务操作
- 第四遍 2025.2.16 -
 // 1、mysql 少写了:高并发时出现数据一致性问题
 // 2、mongodb 少写了:在分布式场景时出现数据一致性问题
 // 3、clickhouse 少写了:列式数据库 + 不支持事务 + 适合高吞吐、低延迟的场景
- 第三遍 2025.1.30 -
- 第二遍 2025.1.25 -
- 第一遍 2025.1.22 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第九遍 2025.3.16 -
Redux 核心是使用 store 管理整个应用的状态管理只有  action dispatch  store 才更更改 state // ⭐️ store 是一棵对象树 - 对象模型 b-tree
reducer 使用  state  action 计算得到新的 state
RTK=redux tool kit
import {createStore} from 'redux'
const reducer = (old={v:1}, action) => { if(action.type) { return {v: old.v + 1} } }
const store = createStore(reducer);
store.dispatch({type: true});
store.subscribe(() => {console.log(store.getState())})
- 第八遍 2025.3.2 -
⭐️ // reducer:返回新的 state 而不是改变;通过 subscribe 更新 UI
- 第七遍 2025.2.27 -
- 第六遍 2025.2.23 -
 // 1、 store 的函数名称错了:getState
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、reducer 参数顺序是 oldState,action
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 作业70:Vue 和 React 的 Diff 算法比较
- 第十二遍 2025.3.16 -
Vue 更新是组件级别的 跳过静态节点 双端指针比较算法 - 匹配不到时查找 同类型同key 的索引比较 - 都不一致时组件列表同类型同key复用
React 更新是节点属性级别的 分层遍历 + 同层级优先比较 - 非同标签重新渲染 - 同标签修改属性复用 - 同标签同key移动复用  // 1、React 同类型优先比较
- 第十一遍 2025.3.2 -
- 第十遍 2025.2.27 -
 // 1、Vue 是组件级别的更新 + React 是节点属性级别的更新
- 第九遍 2025.2.23 -
 // 1、Vue 少写:Diff 跳过静态节点
 // 2、React 少写了:分层比较 + DFS 深度优先遍历子节点,逐个比较
- 第八遍 2025.2.18 - 
- 第七遍 2025.2.16 -
 // 1、Vue 少写了:双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
- 第三遍 2025.1.18 -
- 第二遍 2025.1.13 -
// 2021.04 02 网络协议 - IP/UDP/TCP协议
- 第三遍 2025.3.16 -
应用层 HTTP
传输层 TCP/UDP // ⭐️ 通过端口找程序
网络层 IP // ⭐️ 通过 ip 找电脑
网际链路层 MAC // ⭐️ 通过 mac 找网络服务
data]TCP/UDP]IP---data]TCP/UDP---data
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业56:对那些没有 deadline 的事情,怎么解决拖延症
- 第三遍 2025.3.16 -
人的一生是有限的向死而生
- 第二遍 2025.2.15 -
 deadline 的事情脑中有一个怪兽可以警醒
但人生很多事情是不做出行动就不会有改变要做出行动掌握到主动性才可以有所改变
想要解决人生的拖延症就要明确人生是有限的人就活这么多天不去做就少一天
- 第一遍 2025.1.9 -
// 作业52:道可道,非常道 翻译一下,有什么含义
- 第三遍 2025.3.16 -
如果不变的道可以被说出来那就不是永恒不变的道了
含义语言有局限言传身教
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业51: 如何停止过度思考?如何停止焦虑
- 第三遍 2025.3.16 -
1do it badlyAnything worth doing is worth doing badly the first time
2给自己一些同情
3找到意义不一定要别人认同但自己要找到
 // 1、思考令人上瘾,get it,回到当下 + 焦虑失败则写下所有可以成功的方式,去做
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业50:学习的感觉是?
- 第三遍 2025.3.16 -
学如飞鸟过脑内了无痕
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置
- 第三遍 2025.3.16 -
input size宽度单位为字符数 maxLength最大长度 value初始值
textarea cols宽度单位字符串 rows高度单位字符数 value有设置时就是初始值无时取标签中间值  // 1、textarea 的初始值取标间中间值,而不是 value,该标签没有 value 属性,即设置了也无效
option 有value  value没有取标签中间值为初始值
- 第二遍 2025.2.15 -
- 第一遍 2025.1.9 -
// 作业44:怎么能够不把所有事情、语言往心里去
- 第三遍 2025.3.16 -
It's not about me. 和我无关,关注背后的意图
It's about me. 和我有关关注事实与否 - 学会正确表达自己的情绪不带指责但是告知对方情绪
// ⭐️ you will always keep your value
- 第二遍 2025.2.14 -
- 第一遍 2025.1.9 -
// 作业29:Promise的进度通知
- 第三遍 2025.3.16 -
class MyPromise extends Promise {
    constructor(execute){
        const handler = [];
        super((resolve, reject) => {
            return execute(resolve, reject, info => {
                this.handler.forEach(it => it(info))
            })
        })
        this.handler = handler;
    }
    handle(fn) {
        this.handler.push(fn);
        return this;
    }
}
const p1 = new MyPromise((resolve, reject, handlerFunc) => {
    function count(n){
        if(n>0) {
            setTimeout(() => {
                handlerFunc(`${n*20} remained`);
                // ⭐️ 1、少写了 count(n-1)
            }, 100)
        } else {
            resolve()
        }
    }
    count(5);
})
p1.handle(res => console.log(res)).then(() => console.log('end'))
- 第二遍 2025.2.14 -
- 第一遍 2025.1.9 -
// 作业10:分别用两种方式新建一个映射实例、一个集合实例
- 第三遍 2025.3.16 -
let m1 = new Map([['a',1], ['b',2]]);
let m2 = new Map().set('a', 1).set('b', 2)
let s1 = new Set([1,2,3])
let s2 = new Set({ *[Symbol.iterator](){ 
	yield 1;
    yield 2;
} })
- 第二遍 2025.2.14 -
- 第一遍 2025.1.9 -
// 作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?
- 第五遍 2025.3.16 -
arguments 同步命名参数但不同步设置了默认值的命名参数
数组 -> arguments=[[1,2,3]] ->(...arr)
收集独立参数 (first, ...rest)
对象字面量 {...obj} 复制一个对象属性中的所有可枚举属性 // ⭐️ 创建一个新对象
数组字面量 [...arr] 遍历可迭代对象的所有值
- 第四遍 2025.2.22 -
- 第三遍 2025.2.8 - 
 // 1、数组直接传入的 arguments 错了,应该是 [[1,2,3]]
 // 2、{...obj} 作用描述有问题,复制的是 可枚举属性
// 69 x 的平方根
- 第四遍 2025.3.16 -
function sqrt(n) {
    let mid = Math.ceil(n/2); // ⭐️ 可以使用 Math.floor
    let res = 0;
	for(;res <=mid; res++) {  // 1、不需要等于号,完全平方数时,导致多余的循环
        if(res*res <=n && (res+1)*(res+1) > n) {
            return res
        }
    }
    return res
}
function sqrt(x) {
    let left = 0;
    let right = Math.ceil(x/2);  // 1、right 取 x + 1
    while(left <= right) {  // 2、会导致死循环 left + 1 < right
        const mid = Math.ceil((left + right)/2);
        if(mid*mid > x) {
            right = mid;
        } else {
            left = mid;
        }
    }
    return left
}
- 第三遍 2025.3.1 -
// ❌ 1、while(left + 1 < right)
- 第二遍 2025.2.26 -
- 第一遍 2025.2.21 -
 // 1、res 应该从 0 开始数,否则 n 为 0 时会返回 1
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第六遍 2025.3.16 -
const [issue, created] = await table.findOrCreate(data, where);  // 1、data 的接收属性是 defaults
if(!created) {
    issue.set(data);
    await issue.save()
}
return NextResponse.json({data}) 
- 第五遍 2025.3.1 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 1、findOrCreated 参数错误 findOrCreated({ where, defaults: data })
 // 2、issue.set 不需要 await
- 第二遍 2025.2.17 -
 // 1、findOrCreate 的数据源属性名记错 defaults
 // 2、先set数据,再保存 issue.set(data); await issue.save();
- 第一遍 2025.2.13 -
// 作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置
- 第六遍 2025.3.15 -
唯一索引组合字段唯一不重复
sequelize.define('database', {
    name: DataTypes.STRING
},{
    timestamps: false,
    underscored: true,
    // ⭐️ 少写了 tableName 指定数据库中表的名称
    indexes: [{
        unique: true,
        fileds: ['date', 'sort']
    }]
})
必须配合 MySQL 声明
Create Unique Index `idx_date_sort` ON `table` (`date`, `sort`)
// ⭐️ Alter Table table
Add Unique Index `idx_date_sort` (`date`, `sort`) // ⭐️ Add Unique Key 是 Key 而不是 Index
updateOnDuplicate  bulkCreate 批量添加数据时使用如果 唯一索引重复配置了这个属性则不会抛出错误而是更新 updateOnDuplicate 中指定的字段不声明的不更新
// ⭐️ await table.bulkCreate(data, { updateOnDuplicate: [name, age] })
- 第五遍 2025.3.1 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.21 -
 // 1、CREATE UNIQUE INDEX - 少写了 UNIQUE
 // 2、属性名称 underScored、timeStamps
- 第二遍 2025.2.17 -
- 第一遍 2025.2.13 -
// 作业 89:mysql 中类型的区别:varchar 和 char,date datetime
- 第五遍 2025.3.15 -
varchar 存储可变长度根据内容来
char 存储固定长度如果长度不够则补充空格填充
date YYYY-MM-DD3字节
datetime YYYY-MM-DD HH-MM-SS8字节
- 第四遍 2025.3.1 -
- 第三遍 2025.2.26 -
- 第二遍 2025.2.22 -
 // 1、tinytext 255字符;text 不限长度
- 第一遍 2025.2.8 -
// 作业11:写出一个用 yield 实现的递归算法,从0数到指定n
- 第五遍 2025.3.15 -
function * nTimes(n) {
    if(n>0) {
        yield * nTimes(n-1);
        yield n - 1; 
    }
}
for(let i of nTimes(3)){
    console.log(i); 0 1 2 
}
- 第四遍 2025.3.1 -
- 第三遍 2025.2.27 -
 // 1、少写了 *:yield 只能在生成器中使用 function *
- 第二遍 2025.2.22 -
 // 1、的确是 n > 0 才需要继续递归 -> 但是这么写,不是 next 的{value,done}格式,调用迭代器调用拿不到 0 这个值
 // 2、少写了 yield n-1 -> 上面的 yields * 是把 nTimes 进行一一迭代,yield 才是传递的每个迭代里的具体值
- 第一遍 2024.12.25 -
// 作业1:写一个只能被迭代指定次数的类
- 第五遍 2025.3.15 -
class nTimes() {
    constructor(n) {
        this.limit = n; 
    }
    [Symbol.iterator]() {
        const limit = this.limit;
        let count = 0;
        return {
            next() {
                if(count < limit) {
                    return { value: count++, done: false }
                } else {
                    return { value: undefined, done: true }
                }
            }
            return() {
                return {done: true}
            }
        }
    }
}
- 第四遍 2025.3.1 -
- 第三遍 2025.2.27 -
- 第二遍 2025.2.22 -
- 第一遍 2024.12.25 -
// 答题核心:迭代器返回的是迭代器对象(包含next()、return()) + next 返回一个 done+value组成的对象
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第十遍 2025.3.15 -
Next 预渲染主要由 SSR 服务端预渲染  SSG 静态增生 + revalidate
SSR 在发起请求时由服务端将数据引入后渲染成 html客户端拿到后直接渲染不需要客户端js执行
SSG 在构建运行时 revalidate 指定更新时间增量触发 重新生成 html
SSG
export default function Blog({slug}) { return <div>{slug}</div> }
export const revalidate = 60;
export function getStaticParams() {   // 1、 generateStaticParams + async
    return [{slug: 'first'}, {slug: 'second'}]
}
SSR
import { getServerSideProps } from 'next'  // 2、依赖包错了 next/server
export default function Blog({data}) { return <div>{data}</div> }
export async function getServerSideProps() {
    const res = await fetch('xxx');
    const data = res.json();  // 3、少写了 await
    return { props: {data} }
}
- 第九遍 2025.3.1 -
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
 // 1、静态生成 的函数名称是 generateStaticParams 中间不是 state 是 static
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、SSG + SSR:函数 都少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR + 客户端、服务端生成的结果都是 HTML
- 第三遍 2025.1.31 -       
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 2025-03 Promise 的异步任务并发函数
- 第一遍 2025.3.15 -
MyPromise.all = function(arr) { // ⭐️ 只要有一个被拒绝,则返回拒绝原因
    return new MyPromise((resolve, reject) => {
        if(arr.length === 0) { resolve([]); return; }
        let count = 0;
        const result = [];
        arr.forEach((it, index) => {
            MyPromise.resolve(it).then(res => {
                result[index] = res;
                count++;
                if(count === arr.length) { resolve(result) }
            }).catch(reject);
        })
    })
}
返回第一个非待定的 promise // ⭐️ 空数组 - 返回 pending 状态的 promise
MyPromise.race = function(arr){
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => MyPromise.resolve(it).then(resolve).catch(reject));
    })
}
MyPromise.allSettled = function(arr){
    return new MyPromise((resolve) => {
        if(arr.length === 0) { resolve([]); return; }
        const result = [];
        let count = 0;
        arr.forEach((it, index) => {
            MyPromise.resolve(it).then(value => {
                result[index] = { status: 'fulfilled', value }
            }).catch(reason => {
                result[index] = { status: 'rejected', reason }
            }).finally(() => {
                count++;
                if(count === arr.length) { resolve(result) }
            })
        })
    })
}
// ⭐️ 返回第一个 resolve 的 promise,空数组 - rejected 的 promise,全是拒绝时返回原因数据
// ⭐️ const getErr = (info) => new AggregateError(info, 'All rejected')
MyPromise.any = function(arr) {
    return new MyPromise((resolve, reject) => {
        if(arr.length == 0) { reject([]); return; } // ⭐️ reject 应该包裹一层 getErr
        const errs = [];
        let errCount = 0;
        arr.forEach((it, index) => {
            MyPromise.resolve(it).then(resolve).catch(reason => {
                errCount++;
                errs[index] = reason;
                if(errCount === arr.length) { reject(errs) } // ⭐️ 应该包裹一层 getErr
            })
        })
    })
}
// 作业100:写一个原生的 form ,写出两个提交表单的方式 + 阻止提交方式,表单字段的公共属性/方法,怎么校验表单
- 第二遍 2025.3.15 -
<form method='POST' action='/foo'></form>
type='submit' 的inputbutton 或者 form.submit()
const form = documents.forms[0];
form.addEventListener('submit', (e) => { e.preventDefault() })
公共属性/方法 disable/type/value blur/focus/change
form.checkValidity()
- 第一遍 2025.2.28 -
// 作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法
- 第六遍 2025.3.15 -
序列化前端js对象转换成便于存储传输的格式例如JSON
序列化方法JSON.stringify()\new FormDate(data) // ⭐️ 无法处理function和循环
反序列化JSON.parse()\await NestResponse.json()
const options = {
    data: JSON.stringify(data)/new FormData(form),  // 1、POST 的数据在 body 中
    methods: 'POST'
     // 2、少写了 headers: { 'Content-Type': 'application/json' }
}
const res = await fetch('xxx', options);
const data = res.json();
NextResponse.json({data, status: 200})
- 第五遍 2025.2.28 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
 // 1、将「数据结构、对象」转换为可以「存储、传输」的格式的过程
- 第二遍 2025.2.10 -
 // 1、JSON.stringify 无法处理 Function、无法处理循环引用;new FormData 收集表单数据并序列化,转换成键值对格式
- 第一遍 2025.2.8 -
// 2019.07 引用css,link和@import的区别
- 第五遍 2025.3.15 -
link xml标签都兼容支持rss事务预解析线程提前下载js可修改
@import css2提出低版本写兼容只支持 css引入遇到再下载由于加载顺序问题js修改样式可能会失败
- 第四遍 2025.2.28 -
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、link 少写了:可以处理 rss 等其他事务 + @import 少写了:只能加载 css
- 第一遍 2025.1.8  -
// 作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码
- 第五遍 2025.3.15 -
请求头 GET /index HTTP/1.1
响应头 HTTP/1.1 200 OK
HOST
200 OK ⭐️ // 少写了 204 No Content 只有响应头
301 Moved Permanently 永久重定向浏览器取响应头的 Location 重定向
302 Moved Temperatly 临时重定向浏览器取响应头的 Location 重定向
304 Not Modified If-none-match/ETag If-modified-since/Last-modified 取缓存更新缓存时间
400 Bad Request 请求头语法错误
403 Forbidden 禁止访问
404 Not Found 找不到文件
500 Service 服务端
- 第四遍 2025.2.28 -
- 第三遍 2025.2.26 - 
- 第二遍 2025.2.20 -
 // 1、GET /index HTTP/1.1
 // 2、400 Bad Request 请求有语法错误(格式不正确、缺少必要请求参数、请求体格式错误)
 // 3、405 Method Not Allowed 使用了服务器不支持的 HTTP 请求方法
- 第一遍 2025.1.8  -
// 作业32:什么是队头阻塞?原因是什么?怎么解决?
- 第五遍 2025.3.15 -
队头阻塞一个包不返回影响其他包的请求响应 http 的长连接无关是请求响应模型导致的
HTTP/1.1 因为请求-响应模型无法彻底解决优化方案有域名分片并发请求
HTTP/2.x 改用二进制帧-双向传输形成虚拟的流多路复用-一个连接可以处理多个请求响应多个请求响应之间没有顺序关系解决了应用层的队头阻塞TCP 传输层的还存在
HTTP/3.x TCP的丢包机制导致在一个请求的响应不返回时会发起丢包重新请求在返回前即使其他响应已返回存储在缓冲区上一层也无法拿取 改用 QUIC 协议保证丢包问题改用 UDP
- 第四遍 2025.2.28 -
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、队头阻塞的原因:请求-应答模式导致的
 // 2、HTTP/1.1 优化的方案:域名分片 + 并发连接
- 第一遍 2025.1.20 -
// 作业40:写出以下值
- 第五遍 2025.3.15 -
10 2NaN 3NaN {} undefined 40-有非数字字符串时返回NaN NaN NaN-非数字开头的字符串即NaN
5'122' '32' '02' 'NaN2' NaN 630 32 712 位操作符只能操作32位的正整数所以会将左右转换 8true undefined 其实派生自 null所以规定相等性测试相等
- 第四遍 2025.2.28 -
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、isNaN 为 true 少写了 {}
 // 2、Number-含有非数字字符就NaN parseInt-非数字开头即NaN
- 第一遍 2025.1.20 -
// 2025-03 p-limit 使用+源码 、100个请求并发请求
- 第一遍 2025.3.14 -
使用
import pLimit from 'p-limit';  // 1、还可以导出一个函数 { limitFunction } 处理
const limit = new pLimit(6);  // 2、创建限制器不需要 new:limit = pLimit(6)
const arr = [A,B,C].map(it => { 
    return limit.use(it)  // 3、limit 使用的时候直接包裹函数即可 limit(it)
})
arr.forEach(it => it()).then(res => console.log(res));  // 4、执行所有 promise 使用 Promise.all(arr).then(res => console.log(res))
源码
 /**
function validateConcurrency(limit){ if(!Number.isInteger(limit)) {throw err} }
function pLimit(concurrency) { 函数实现,而不是类
	valiadte(concurrency); 校验是否为正整数
	const queue = []; 任务队列,存储等待执行的任务
	let activeCount = 0; 计数器,记录正在执行的任务数量
	
	const next = () => { 执行队列中下一个任务
		activeCount--; 一个任务完成则减一
		if(queue.length > 0) { queue.shift()() } 如果还有待执行的任务,从队列中取出并执行
	}
	
	const run = async (fn, resolve, args) => { 执行任务封装
		activeCount++; 表示有个新任务开始开始执行
		try {
			const res = await fn(...args);
			resolve(res);
		} catch(e){ resolve(Promise.reject(e)) }
		next();
	}
	const generator = (fn, ...args) => {
		return new Promise((resolve, reject) => {
			if(activeCount < concurrency) {
				run(fn, resolve, args) 执行
			} else {
				queue.push(run.bind(null, fn, resolve, args)) 加入队列
			}
		})
	}
	Object.defineProperties(generator, {
		activeCount: {get: () => activeCount},
		pendingCount: {get: () => queue.length},
		clearQueue: {value: () => queue.length = 0}
	})
	return generator;
}
export function limitFunction(cb, option) {
	const { concurrency } = option;
	const limit = pLimit(concurrency);
	return (...arg) => limit(cb(...arg))
}
*/
class pLimit {
    constructor(limit) {
        this.limit = limit;
        this.running = [];
        this.stack = [];
    }
    use(fn) {
        if(this.running.length < this.limit) {
            this.running.push(fn);
        } else {
            this.stack.push(fn)
        }
    }
    apply() {
        const result = [];
        this.running.forEach((it, index) => {
            Promise.resolve(it).then(res => {
                result[index] = res;
                this.running.splice(index, 1);
				if(this.stack.length) {
					this.running.push(this.stack.shift());
                }
                if(index === this.running.length) {
                    return result
                }
            })
        })
        
    }
}
100个请求并发请求
/** ❌ // 6、脑袋空空 async function execute(arr, limit) {
    const indexedArr = arr.map((it, index) => ({
        index, promise: it.then(value => ({ value, key: index }))
    }))
    const tasks = indexedArr.splice(0, limit); // 初始化
    const result = [];
    while(tasks.length > 0) {
        const {key, value} = await Promise.race(tasks.map(it => it.promise));
        const resolvedIndex = tasks.findIndex(it => it.index === key);
        tasks.splice(resolvedIndex, 1);
        result[key] = value;
        if(indexedArr.length > 0) { tasks.push(indexedArr.shift()) }
    }
    return result
}*/
// 2025-3 NestJS 守卫-校验权限
- 第一遍 2025.3.14 -
auth-check.ts
 // 1、少写了:装饰符 @Injectable()
class AuthCheck implements CanActivate {  // 2、少写了:export
    canActivate(ctx, next){  // 3、不需要 next,守卫返回布尔值即可
        const name = ctx.header.get('name'); 
		 // 4、获取请求头: ctx.switchToHttp.getRequest().headers
        if(name !== 'xxx') {
            ctx.body = {err: 'xxx'}
             // 5、如果没有权限 throw new UnauthorizedException('没有权限')
        }
        next();  // 6、如果可以继续 return true
    }
}
⭐️ // @UseGuards(AuthCheck)
// 2025-3 NextJS 全局中间件-校验权限
- 第一遍 2025.3.14 -
 src/app 同层级下面添加文件 src/middleware.ts 
 // 1、中间件利用 NextRequest NextResponse 处理逻辑 + 路由配置器
 // 2、少写了 export const config = { matcher: '/api/:path*' } 匹配api下所有接口
export default function authCheck(ctx, next) {  // 3、入参 req: NextRequest
    if(ctx.header.name === 'xxx'){  // 4、header 值的获取: req.header.get('name')
        return next();  // 5、next 是封装在框架响应中: NextResponse.next()
    }
	 // 6、如果不满足条件,返回错误 NextResponse.json({message:'111'}, {status: 401})
}
// 2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现?
- 第三遍 2025.3.13 -
css 动画的优势
	代码兼容
    渲染进程的预加载提前下载不阻塞主线程
    合成线程可以开启GPU加速
    transform opacity 可以避免回流重绘 ⭐️ // 直接在合成层操作
- 第二遍 2025.3.6 -
 // 1、css 动画可以在合成线程中完成,例如 transform opacity,有效避免回流重绘的性能损耗
- 第一遍 2025.1.30 -
// 作业64:Vue渲染怎么实现的?
- 第十遍 2025.3.13 -
模版编译 ⭐️ // AOT 预编译优化
	parse 词法语法分析得到AST-optimize遍历标记静态节点-generate生成render函数代码
双向数据绑定
	使用订阅发布和数据劫持 Object.defineProperty(obj,property,{get,set})/Proxy
	初次渲染实例化 Watcher setter 时收集和属性相关的 Watcher 收集到 Dep ⭐️ // getter
    更新时通过访问 getter 触发更新dep.notify() ⭐️ // setter
执行 render 函数生成 VDOM
Diff 增量式计算得到最小更新 UI并更新到真实 DOM
- 第九遍 2025.3.6 -
 // 1、首次渲染时 getter 触发 Watcher 收集;更新时 setter 触发通知
- 第八遍 2025.2.20 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.11 -
 // 1、模版编译的阶段名称错误:parse - optimize - generate
 // 2、函数使用方法错误:Object.defineProperty(obj, 属性, { get, set })
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
// 2025.02 Express VS KOA2
- 第二遍 2025.3.13 -
都是基于 nodejs 的服务框架中间件机制路由机制
Express 
	中间件线性模型
    模型内置功能完善适合开箱即用不强调性能
    主要使用回调函数实现之前会出现回调地狱之后支持了 Promise async/await
KOA2 
	中间件洋葱模型
    代码简洁都是以插件扩展功能灵活性能高 ⭐️ // 中间件
    原生就支持 async/await,适合用于复杂回调场景 ⭐️ // 复杂异步场景
 // 1、错误处理方式:Express -app.use((err,req,res,next)=>{res.status(500).send('err')})
// 错误处理方式:KOA2 try-catch结合中间件实现 -app.use(async(ctx,next)=>{try{await next()}catch(e){ ctx.status=500; ctx.body={err} }})
 // 2、上下文:Express res.send()/res.json() 都是基于 Node 底层 Response 的 res.end 的封装
// 上下文:KOA2 封装了 ctx 响应 ctx.body
- 第一遍 2025.3.5 -
 // 1、Express 异步处理方式:早期使用回调函数处理异步操作,会出现回调地狱
 // 2、KOA2 异步处理方式:原生支持 async/await,异步操作简洁,避免回调地狱
// 2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码
- 第二遍 2025.3.13 -
中间件机制实现
	描述洋葱模型先入栈-到最里层中间件-后出栈
function compose(middlewares) {
    return (ctx, next) => {
        let index = 0;  // 1、初始值设置为 -1,否则 0 就相等抛出重复错误了
        return dispatch(0);
        function dispatch(i) {
            if(i<=index){ return Promise.reject(new Error('multiple')) }
            index = i;
            let fn = middlewares[i];
            if(!fn) { return Promise.resolve() }
            if(i === middlewares.length) { fn = next }
            try{
                const nextFunc = () => dispatch(i+1);
                return Promise.resolve(fn.apply(ctx, nextFunc)) 
                 // 2、执行中间件时,ctx 和 nextFunc 作为参数传入
            }catch(e) { Promise.reject(e) }
        }
    }
}
路由处理
	KOA2 并没有内置路由使用 koa-router: router.get('/cat', async (ctx) => {})
错误处理
	全局路由处理中间件
    app.use(async (ctx, next) => {
        try{ await next() } catch(e) { 
            ctx.onerror(e)  // 3、处理错误时,改变 ctx.status 和 ctx.body
        }
    })
模版引擎
	ejs模版引擎 <% for %>适合复杂模版性能差一点因为要动态解析执行js
	art-template模版引擎性能好有预解析所以只需要直接执行js
源码
import http from 'http';
class KOA2 {
    constructor() {
        this.middlewares = [];
    }
    listen(...arg){
        const server = http.createServer(this.callback());
        return server.listen(...arg)
    }
    use(fn) { this.middlewares.push(fn) }  // 4、少写了 return this
    callback(){
        const fnMid = compose(this.middlewares);
        return (req, res) => {
            const context = this.createContext(req, res);
            fnMid(context).then(() => respond(context)).catch(e => console.err(e)) 
             // 5、少写了 return + catch 错误处理使用 ctx.onerror
        }
    }
    createContext(req, res) {
        const ctx = Object.create(this.context);
        ctx.request = Object.create(this.request);
        ctx.response = Object.create(this.response);
        ctx.res = ctx.response.res = res;
        ctx.req = ctx.request.req = req;
        return ctx
    }
}
function respond(ctx){
    ctx.statusCode = ctx.status;  // 6、statusCode 是在原生 Reponse 上 ctx.res.statusCode
    ctx.res.end(ctx.body)
}
- 第一遍 2025.3.5 -
 // 1、KOA2 是基于 NodeJS 的 HTTP 中间件框架
 // 2、中间件写法:应该是 async 函数,async (ctx, next)=>{ await next() }
 // 3、如果重复执行,直接返回 Promise.reject(new Error('dup'))
 // 4、少写了 fn 不存在时,return Promise.resolve()
 // 5、KOA2 路由参数 ctx.params + 链接参数 ctx.query
// 2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序
- 第二遍 2025.3.13 -
核心特性模块化架构依赖注入DI支持多个协议http/Websocket
基本概念服务 provider - 控制器 - 子模块/根模块 - 应用入口 main.ts
		在模块中配置将服务注入控制器
        令牌 - 依赖注入时查找实例的唯一标识
		装饰器 - 一个表达式参数是字符串属性实例返回一个函数
生命周期:①初始化应用框架启动读取provider控制器的配置和依赖关系 ⭐️ // 解析配置,收集依赖关系
		解析依赖和实例化深入解析所有依赖依次实例化所有provider控制器
		启动服务在指定端口上监听提供服务
        请求和响应运行时请求依次经过中间件-守卫-请求拦截器-管道-路由处理函数-响应拦截器后返回响应
		应用关闭关闭前销毁所有实例
获取请求和响应@Req 包含请求头请求行请求体 @Query@Body 开箱即用 @Param 动态路由参数
			响应是对象或数组时Next 默认序列化为 JSON 返回基础类型直接返回
不同组件的实现和执行顺序
中间件
	时机进入Nest路由系统之前可以直接访问原生请求和响应主要处理日志跨域等 ⭐️ // 全局请求预处理
    实现:①logger(req, res, next){} 
		 class xxx implements NestMiddle{ use(req, res, next){} } ⭐️ // NestMiddleware
	配置:①main.ts app.use(logger) 
		 app.module.ts 导出类 class xxx implements NestModule{ 
             configure(consumer){ consumer.apply(logger).forRoutes('cats') } }
守卫
	时机权限校验返回一个布尔值判断是否能够继续往下执行
    实现class ... implements CanActivate{ canActivate(ctx){} }
	配置:①全局 main.ts app.useGlobalGuards(/类型)/useClass
		 控制器 *.controller.ts @UseGuards(/类型)
拦截器请求
	时机在请求进入路由处理函数之前处理请求头
    实现class ... implements NestIntercepter{ intercept(ctx, next){} }⭐️//NestInterceptor
	配置:①全局 main.ts app.useGlobalInterceptors(/类型)
		 控制器 *.controller.ts @UseInterceptors(/类型)
管道
	时机处理路由处理函数的参数返回的参数会覆盖之前的参数
    实现:①内置9个管道 class xxx implements PipeTransform{ tansform(ctx, next){} } ⭐️ // transform(value, metaData)
	配置:①全局 main.ts app.useGlobalPipes(/类型)
		 控制器 *.controller.ts @UsePipes(/类型)
		 路由处理函数 @Body(/类型) @Param/@Query('property', 类/类型 * n)
路由处理函数
拦截器响应
- 第一遍 2025.3.5 -
 // 1、核心特性:更改描述为“模块化架构” ;依赖注入 DI;多种协议 HTTP Websocket
 // 2、少写了 令牌:类是代码的具体实现,令牌是依赖注入的 标识依赖项的唯一标识,指向相应实例
 // 3、生命周期:描述不清楚 应用初始化:解析模块、控制器、provider等配置信息,收集、整理组件间的依赖关系注入容器(容器是个抽象概念,用于管理组件、依赖,启动时创建)
 // 4、依赖解析和实例化:根据配置,深度解析各组件依赖关系,根据关系依次实例化 provider、实例化控制器
 // 5、少写了响应方式:2种 对象/数组-自动序列化为JSON;基础类型-直接返回,不序列化
// 2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程
- 第四遍 2025.3.13 -
nextTick 出现的原因因为 Vue 是异步更新 DOM 当数据改变Vue 会把更新收集到事件队列等到下个事件循环开始前更新 DOM ⭐️ // nextTick 确保在 DOM 更新后执行某些操作
Vue2 先收集回调再根据环境切换实现最后执行
const callbacks = [];
let pending = false;
function flushJobs() {
    pending = true;
    const copy = [...callbacks];
    copy.forEach(it => it());
    copy.length = 0;
    pending = false;
}
let timeFunc;
if(typeof Promise !== 'undefined'){
    timeFunc = () => Promise.resolve().then(flushJobs)
} else if (typeof MutationObserver !== 'undefined') {
    let ob = new MutationObserver(flushJobs);
    const dom = document.createTextNode('111');
    ob.observe(dom, { characterData: true });
    timeFunc = () => { dom.data = '2' }
} else if(typeof setImmediate !== 'undefined') {
    timeFunc = () => setImmediate(flushJobs)
} else {
    timeFunc = () => setTimeout(flushJobs, 0)
}
function nextTick (cb, thisArg) {
    if(!cb) {
        return Promise.resolve();
    }
    callbacks.push(() => {
        try{ cb.call(thisArg) } catch(e) { return Promise.reject(e) }
    })
    if(!pending) {
        timeFunc();
    }
}
new Vue({
    data() { return {msg:'xxx'} },
    mounted() { this.$nextTick(cb) }
})
Vue3  Vue2 的差异是 nextTick 始终返回 promise实现微任务的方式是 queueMicrotask
const callbacks = [];
let pending = false;
function flushJobs() {
    pending = true;
    let job;
    while((job = callbacks.pop())) {
        job()
    }
    pending = false;
}
let timeFunc = () => { queueMicrotask(flushJobs) };
function nextTick(cb) {
    return new Promise((resolve, reject) => {  // 1、nextTick 返回 resolved 状态的 Promise
        if(!cb) {
            return reject()
        }
        callbacks.push(() => {
            try{cb(); resolve;} catch(e){reject(e)}  // 2、所以这边有 err 时, console 处理;resolve 放在 try-catch 外面 - 只要 cb 执行了,就 resolve
        })
        if(!pending) {
            timeFunc();
        }
    })
}
import { createApp, nextTick } from 'vue';
const app = createApp({
    data(){ return {msg: 'xxx'} },
    mounted() { nextTick(cb) }
})
app.mount('#root')
- 第三遍 2025.3.3 -
 // 1、vue2 timeFunc 应该包裹成回调函数,否则赋值时就直接执行了timeFunc =()=>Promise.resolve().then(flushCalls)
 // 2、vue2 MutationObserver 文本节点创建函数错误 createTextNode + 配置的属性名称错误 characterData + 文本节点修改值是 .data='2'
 // 3、vue2 nextTick 有第二个参数 ctx -> 指定 cb 的上下文 
 // 4、vue2 使用示例 new Vue({data(){return{value:1}},mounted(){this.$nextTick(()=>{})}})
 // 5、vue3 中,收集回调时,传入回调函数需要把当前的 Promise 状态切换为 resolved
 /** 6、vue3 使用示例 import {createApp, nextTick} from 'vue';
const app = createApp({ data(){ return {value:1} }, mounted() {nextTick(() => {})} })
app.mount('#app')*/
- 第二遍 2025.2.27 -
 // 1、vue2 的 NextTick 根据回调函数判断是否返回 Promise;vue3 不论回调函数,都会返回 Promise
 // 2、vue2 执行所有回调函数 flushJobs 中少写了:清空原数组,避免重复执行回调 callbacks.length=0;
 // 3、vue2 根据环境判断,有 Promise 时 Promise.resolve().then 少写了执行()
 // 4、收集回调函数时,需要处理报错的情况 try-catch
- 第一遍 2025.2.24 -
 // 1、Vue3:和Vue差异是,使用 queueMicrotask 统一处理微任务
// 2024.12 模拟实现promise
- 第七遍 2025.3.13 -
const S = {
    pending: 'pending',
    resolved: 'fullfiled',
    rejected: 'rejected'
}
function MyPromise(executor) {
    this.status = S.pending;
    this.value = null;
    this.reason = null;
    this.resolvedCalls = [];
    this.rejectedCalls = [];

    const resolve = (value) => {
        if(this.status === S.pending) {
            this.value = value;
            this.status = S.resolved;
            this.resolvedCalls.forEach((it) => it(value))
        }
    }
    const reject = (reason) => {
        if(this.status === S.pending) {
            this.reason = reason;
            this.status = S.rejected;
            this.rejectedCalls.forEach(it => it(reason))
        }
    }
    try{
        executor(resolve, reject)
    } catch(e) {
        reject(e);
    }
}
MyPromise.prototype.then = function(onResolved, onRejected) {
    onResolved = typeof onResolved === 'function' ? onResolved : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : r => {throw r};
    return new MyPromise((resolve, reject) => {
        const resolveFunc = () => {
            setTimeout(() => {
                try{
                    const res = onResolved(this.value);
                    resolve(res);
                } catch(e) {
                    reject(e);   
                }
            }, 0)
        }
        const rejectFunc = () => {
            setTimeout(() => {
                try{
                    const res = onRejected(this.reason);
                    resolve(res);
                } catch(e) {
                    reject(e);   
                }
            }, 0)
        }
        switch(this.status) {
            case S.pending:
                this.resolvedCalls.push(resolveFunc);
                this.rejectedCalls.push(rejectFunc);
                break;
            case S.resolved:
                this.resolvedCalls.push(resolveFunc());
                break;
            case S.rejected:
                this.rejectedCalls.push(rejectFunc());
                break;  
        }
    })
}
MyPromise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected);
}
MyPromise.prototype.finally = function(callback) {
    return this.then(v => { callback(); return v; },
                     r => { callback(); throw r; })
}
MyPromise.resolve = function(value) {
    return new MyPromise((resolve, reject) => {
        if(value instanceof MyPromise) {
            return value.then(resolve, reject)
        }
        resolve(value);
    })
}
MyPromise.reject = function(reason) {
    return new MyPromise((resolve, reject) => reject(reason));
}
// 全部返回,reduce 处理的是同步任务,异步会出现异常
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) => {
         // 1、arr 为空数组时,forEach 不会执行 if(arr.length===0){resolve([]);return;}
        const result = [];
        const length = arr.length;
        try{
            for(let i = 0; i< length; i++) {
                result[i] = arr[i]();
            }
            resolve(result);  // 2、这样写不知道是不是所有的 peomise 都返回了,需要一个计数器
        }catch(e) {
            reject(e)
        }
        /**
        	let completedCount = 0;
        	arr.forEach((it, index) => {
        		MyPromise.resolve(it).then(res => {
        			results[index] = res;
        			completedCount++;
        			if(completedCount === arr.length) { resolve(results) }
        		}).catch(reject);
        	})
        */
        // arr.forEach(async (it, index) => {}) check 一下 forEach 和 async 的兼容情况
    })
}
MyPromise.race = function(arr) {
    return new MyPromise((resolve, reject) => {
		arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))  // 3、改写成 .then(resolve).catch(reject) 更合适
    })
}
四个异步处理并发函数 all race + allSettled - 返回数组 any
/**
MyPromise.allSettled = function(arr) {
	return new MyPromise((resolve) => {
		const result = [];
		let completedCount = 0;
		if(arr.length === 0) { resolve([]); return; }
		arr.forEach((it, index) => {
			MyPromise.resolve(it).then(value => {
				result[index] = { status: 'fulfilled', value }
			}).catch(reason => {
				result[index] = { status: 'rejected', reason }
			}).finally(() => {
				completedCount++;
				if(completedCount === arr.length) { resolve(result) }
			})
		})
	})
}
const getErr = (info) => new AggregateError(info, 'All promises were rejected');
MyPromise.any = function (arr) {
	return new MyPromise((resolve, reject) => {
		const errs = [];
		let count = 0;
		if(arr.length === 0){ reject(getErr([])); return;}
        arr.forEach((it, index) => {
        	MyPromise.resolve(it).then(resolve).catch(err => {
        		errs[index] = err;
        		count ++;
        		if(count == arr.length) { reject(getErr(err)) }
        	})
        })
	})
}
*/
- 第六遍 2025.3.5 -
 // 1、all 中 reduce 中的 pre 需要返回:return pre
 // 2、race 中 it 需要包裹一层 MyPromise.resolve(it).then(resolve, reject)
- 第五遍 2025.2.11 -
- 第四遍 2025.1.20 -
- 第三遍 2025.1.19 -
 // 1、模拟 Promise 的时候,一般都是用函数
 // 2、遗漏捕获错误,需要使用 try-catch + onRejected 返回的依旧是 resolve 的 promise
 // 3、then:try-catch不能写成 catch(reject),catch接收的参数是error - catch(e){ reject(e) }
// 作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明
- 第八遍 2025.3.13 -
React渲染  // 1、少写了第一步:动态编译 JIT - 将 JSX 描述编译为 React createElement 方法
	shouldComponentUpdate(nextProps, nextState) 判断是否要渲染
render fiberVDOMhooks副作用链表Diff增量算法
 /** 2、构建新的 Fiber 树 WIP Tree:
	创建 FiberRootNode
	- 创建 fiberNode 作为 HostRootFiber - 也就是 FiberRootNode.current
	- 以 HostRootFiber 为起点,DFS 遍历执行beginWork,根据 wip.tag 区分元素处理,生成 fiberNode
	- 遍历到叶子节点,调用 completeWork 构建出 tree
*/
 // 3、初次渲染时,存储 hooks、标记收集副作用链表
	初次渲染更新时执行所有 hooks收集副作用链表
commit 更新真实DOM执行所有生命周期函数和副作用链表
- 第七遍 2025.3.5 -
 // 1、shouldComponentUpdate 在 render 之前被调用,不在 commit 阶段
- 第六遍 2025.2.11 -
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
 // 1、遍历执行 hooks 时,如果遇到有副作用的节点,除了被副作用链表收集,react 也会在节点 Node 上标记 fiberNode.subtreeFlags
 // 2、count 这里应该将「如果有副作用」改写为「要注意是否有 useEffect 的作用:根据依赖数组是否变化确定是否要更新」
// 作业23:写出js 代码的执行顺序。词法作用域的特点是什么?
- 第三遍 2025.3.13 -
js this 
   执行上下文 - 词法环境 let/const
			   变量环境 var 
   可执行代码 AST
 // 1、js 分为【可执行代码】和【执行上下文】,执行上下文分为三个:词法环境、变量环境、this
 // 2、变量环境 先创建arguments再函数声明提升最后变量声明提升
词法作用域只和代码所写位置有关
作用域链由多个执行上下文的变量环境组成 ⭐️ // 链表
'bye 坑'
"global name"
undefined 'a' 'b' 'err'
- 第二遍 2025.2.24 -
- 第一遍 2025.1.20 -
// 2021.07 bind、apply/call三者异同+apply/call实现bind
- 第六遍 2025.3.13 -
都是为了改变函数的this指向如果不传参数的话函数的 this 默认指向 window
bind 返回的是函数副本改变完 this 指向后执行的话需要再调用
apply和call 都是改变完 this 指向立即执行函数apply 接收的参数是类数组数组bind 接收一组参数
Function.prototype.fakeBind = function (ctx, ...arg1) {
    let func = this;
    return function F(...arg2) {
        if(func instanceof F) {
            return new func(...arg1, ...arg2)
        }
        func.call(ctx, ...arg1, ...arg2)
    }
}
- 第五遍 2025.2.15 -
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、bind 实现应该绑定在原型对象上实现: Function.prototype.fakeBind - 这样 this 才是指向调用 bind 的函数
- 第二遍 2025.1.19 -
 // 1、「bind 有多个参数,第一个是 this 指向,后面接收多个参数 」...arg1
 // 2、遗漏了判断是否为 new 调用
// 作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?
- 第五遍 2025.3.13 -
13.x 开始 Link 中不写 a 标签Link 会被解析为 a直接在 Link 上写 href
路由改为 src/app/ 下解析前端路由 /api 写后端路由
 // 1、少写了:由之前的 pages/index.js 改为 src/app/page.tsx ,并支持共享布局 layout.tsx;从 pages/api 改为 src/app/api 
- 第四遍 2025.2.16 -
- 第三遍 2025.1.31 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
 // 1、只写了「文件路由系统」的变更,还少写了「后端路由系统」的变更 从 pages/api 变更为 src/app/api
// 作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?
- 第五遍 2025.3.13 -
path.join(AB) 单纯拼接地址
path.resolve(_dirname, xxx) 解析地址得到相对地址  // 1、解析得到绝对路径,把 / 当做根目录
process.cwd() node 环境的执行上下文 ⭐️ // node 执行时的工作目录
__dirname 当前文件的路径  // 2、当前模块的目录名
- 第四遍 2025.2.16 -
- 第二遍 2025.1.29 -
- 第二遍 1.26 -
- 第一遍 2025.1.22 -
 // 1、path.resolve(a,b):是「绝对路径解析」,把 / 当做根目录 -> /a/b
 // 2、process.cwd():node 执行时所在目录
// 作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序)
- 第五遍 2025.3.11 -
select d.name, 
    distinct d.sex, 
	concat('(', d.age,')') as my_age,
	upper(t.time) as my_time,
	COUNT(d.name) as my_without_null,
	COUNT(*) as my_with_null,
	SUM(d.age) as my_sum,
    MAX(d.age) as my_max,   
    MIN(d.age) as my_min,
	AVG(d.age) as my_avg,
from daily as d, times as t
where t.id = 1 AND (
	t.price > 10 OR
    t.price is NULL OR
    t.price BETWEEN 5 AND 10 OR
    t.price IN [1,2] OR  // 1、IN 后面要接小括号 (1, 2)
	YEAR(d.time) = 2025
)
group by d.age
sort by d.name desc  // 2、不是 sort 是 ORDER BY ..
- 第四遍 2025.2.16 -
- 第三遍 2025.1.30 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.23 -
// 作业80:对DOM 树的理解 - 定义和作用
- 第五遍 2025.3.11 -
DOM 表示 HTML 的结构HTML 的属性解析成为 DOM 的属性  // 1、少写了:DOM 是树形结构的对象模型
浏览器 DOM 当做渲染的基础按照 DOM 结构渲染  // 2、少写了:按照 DOM 结构构建「页面布局」
jsDOM  JS 操作 HTML 的接口 ⭐️ // 编程接口
框架框架通过 VDOM 比较最小更新 UI提高渲染性能  // 3、是更新而不是渲染:提高页面更新性能
- 第四遍 2025.2.16 -
- 第三遍 2025.1.31 -
 // 1、少写了浏览器视角-渲染页面的依据 和 框架视角-VDOM Diff 计算提高更新性能
- 第二遍 2025.1.25 -
- 第一遍 2025.1.24 -
// 2021.09 Object属性排序、与Map的区别
- 第六遍 2025.3.11 -
Object属性排序: 正整数字符串浮点数负数Symbol  // 1、浮点数和负数顺序反了:浮点数在后
Map:①Map是可迭代对象Object不是 Map有序按存储顺序记录Object无序 Map计算长度 .size() 简单Object计算方式绕 Object.keys(obj).length 存储数量大时Map 存储空间占用更小增删速度更快  // 2、少写了键:Map 的键可以是任意值,Object 只有字符串和Symbol
- 第五遍 2025.2.16 -
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.21 -
 // 1、少写了浮点数
// 作业20:浏览器打开一个 URL 发生了什么
- 第六遍 2025.3.11 -
1浏览器进程 UI线程 将用户的输入内容/链接整合为完整的 url
2通过 IPC 传递给网络进程 查找本地缓存 - 有且缓存没有过期则直接返回强制缓存生效
/缓存已过期 -  DNS 解析获取 IP
			   请求头cookie 添加  // 1、先 TCP 排队,再组装 HTTP 请求
			   TCP 排队三次握手建立连接
               发送请求
301/302 Moved Permanently/Temperily 永久/临时重定向使用响应头中的 Location 进行重新导航
304 Not Modified 使用本地缓存更新缓存时间 If-none-match/ETag If-modified-sine/Last-modified
400 Bad Request 请求头有语法问题
403 Forbidden 资源禁止访问
404 Not Found 找不到资源
500 服务器错误
200 OK 如果是字节流类型进行下载结束导航如果是 html - 和渲染进程 建立管道边下载边解析
3渲染进程预解析线程合成线程主线程
下载完但没解析完出现解析白屏
下载完会告知 浏览器进程 UI线程 更新 页面前进/后退安全锁url 内容  // 2、传输完成后触发更新页面
预扫描 html 文件 js css 资源提前下载
主线程解析DOMDOM树- 样式计算CSSOM- 布局布局树- 合成合成树 - 绘制指令列表-  // 3、渲染流程中,布局阶段后,不是合成阶段,是分层 得到分层树
合成线程- 栅格化图块转换成位图GPU 可加速合成线程 - 
浏览器进程 UI线程- 合成帧
- 第五遍 2025.2.16 -
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.20 -
 // 1、协议名错了:IPC - 进程通信协议
 // 2、少写了:有缓存但缓存失效
 // 3、应该是先判断状态码,200的状态码下再判断类型
 // 4、少写了 304,使用本地缓存,并刷新缓存有效时间
// 作业4:什么是生成器?有什么作用?
- 第五遍 2025.3.11 -
Generator 暂停和恢复代码执行自定义可迭代对象和实现协程
- 第四遍 2025.2.23 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
 // 1、少写了:暂停、恢复代码执行,* 声明
- 第一遍 2025.1.30 -
// 作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询
- 第七遍 2025.3.11 -
INNER JOIN 内联查询交集同时满足 A  B  fields
A LEFT OUTER JOIN B ON 左联查询满足 A 和剩余 A 相关的 B 数据
A RIGHT OUTER JOIN B ON 右联查询满足 B 和剩余 B 相关的 A 数据
UNION 组合上下两次查询 ⭐️ // 拼接
封装
CREATE VIEW my_search AS SELECT * FROM table
SELECT * FROM my_search
- 第六遍 2025.2.23 -
- 第五遍 2025.2.18 -
 // 1、封装:关键词不是 ON 是 AS
- 第四遍 2025.2.16 -
 // 1、INNER JOIN 不是合集,是交集,表达错误 - AB都满足的行,内连接
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 2024.12 模拟实现jsonp
- 第六遍 2025.3.11 -
function fakeJsonp(url, params, callbackName) {
    const suffix = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&') + `&callback=${callbackName}`;
    const totalUrl = url + url.includes('?') ? '&' : '?' + suffix;
    
    const dom = document.createElement('script');
    window[callbackName] = function(...arg) {
        callbackName(...arg);
        document.body.removeChild(dom);
    }
    dom.src = totalUrl;
    document.body.appendChild(dom);
}
- 第五遍 2025.2.23 -
- 第四遍 2025.2.18 -
- 第三遍 2025.2.16 -
 // 1、计算 url 时少了 '&' -> 使用 map + join 更简洁:Object.keys(params).map(it => `${it}=${params[it]}`).join('&') -> 使用 entries 也可以 Object.entries(params).map(([key, vlaue]) => `${key}=${value}`).join('&')
- 第二遍 2025.1.21 -
- 第一遍 -
// 2021.07 防抖节流
- 第六遍 2025.3.11 -
function debounce(fn, delay) { ⭐️ // 间隔一段时间不输入,才会触发搜索
    let time;
    return (...arg) => {
        if(time) {
            return
             // 1、已存在,则清理定时器,保证不停读条,覆盖上一次 clearTimeout(time)
        }
        time = setTimeout(() => {
            fn(...arg);
            clearTimeout(time);  // 2、不在这边清理
        }, delay)
    }
}
function throttle(fn, delay) { ⭐️ // 一直按着技能键也能触发,以固定频率触发
    let flag = false;
    return (...arg) => {
        if(!flag) {  // 1、正在触发时,不执行 if(flag)
            return;
        }
        fn(...arg);
        flag = true;
        setTimeout(() => {
            flag = false
        }, delay)
    }
}
- 第五遍 2025.2.23 -
- 第四遍 2025.2.18 -
- 第三遍 2025.2.16 -
 // 1、debounce 遗漏赋值:timeId = setTimeout()
 // 2、throttle:判断条件应该是 if(flag){return}:flag 为 true 时,证明这段时间内已经执行了
- 第二遍 2025.1.21 -
// 2025-03 服务器 不明文密码处理
- 第一遍 2025.3.11 -
存储在项目之外的 config.env 
项目中读取使用 dotenv
dockerfile.yml 配置文件中将文件引入镜像 volumns: -./config.env:/app/config.env
使用  // 1、应该是 ../config.env 从项目外引入挂载到指定路径 + 文件名是 docker-compose.yml + volumes 卷,单词错了
import {dotenv} from 'dotenv'
dotenv.config({path: '/app/config.env'}) 
 // 2、配置时,传入具体的地址 path.resolve(__dirname, '../config.env')
process.env.XXX
// 2021.06 flex布局
- 第四遍 2025.3.11 -
1布局排列方便 flex-direction row/column/row-reverse/column-reverse
 // 1、少写了 flex flex-grow(基于basis的整数倍) flex-shrink flex-basis
2主轴 交叉轴 justify-content:flex-start/flex-end/space-aound[-a--b--c-]/space-between[a--b--c] align-iterms 主轴默认可以压缩但不能拉伸交叉轴默认拉伸 stretch
3可以设置层级 orderalign-self 可以覆盖 align-iterms的值设置当前项目的样式 ⭐️ // 对齐方式
4flex 会使 float vertical-align clear 失效
5独立容器 从上至下垂直排列 不和外部浮动元素重叠 计算高度时包含浮动元素
- 第三遍 2025.3.2 -
// ❌ 1、flex-direction 中的值 row/column 没有 s
// ❌ 2、主轴默认不拉伸,但默认缩小
- 第二遍 2025.2.27 -
 // 1、少写了 flex 导致 float clear vertical-align 失效
- 第一遍 2025.2.23 -
// 2021.07 柯里化函数实现
- 第四遍 2025.3.11 -
function curry(fn, ...args1){
    if(fn.length > args1.length) {
        return (...args2) => curry(fn, ...args1, ...args2)
    } else {
        return fn(...args1)
    }
}
- 第三遍 2025.3.2 -
// ❌ 1、不是直接返回回调,而是根据参数数量判断返回
// ❌ 2、参数不够时,返回一个包裹的函数
// ❌ 3、执行应该是 fn(...args)
- 第二遍 2025.2.8 -
// 2019.07 数组去重(算法整理)
- 第五遍 2025.3.11 -
[...new Set(arr)] Array.from(new Set(arr)) Array.of(...new Set(arr))
arr.filter((it, index) => arr.indexOf(it) === index)
function unique(arr) {
    const hash = {};
    return arr.filter(it => hash[it] ? false : hash[it]=true)
}
- 第四遍 2025.3.2 -
// ❌ 1、写成拍平函数了
- 第三遍 2025.2.8 -
// 2021.06 跨页面通信
- 第五遍 2025.3.11 -
同源 1 localStorage 只能处理非同源页面的更新触发
		localStorage.setItem(key,v) + window.onstoragechange=()=>{} 
         // 1、window.onstorage 监听函数名称错了
2 BroadCastChannel 两个页面创建同广播分别传递和监听  const b1 = new BroadCastChannel('b1');
	b1.postMessage('xxx') + b1.onmessage=()=>{}
非同源
1 postMessage H5 API 打开新页面 let win = window.open(url) 
	win.postMessage('xxx') + 在新页面 window.onmessage=()=>{}
2 iframe 设置 origin 为同域名使用 1 2
- 第四遍 2025.3.2 -
// ❌ 1、localStorage 获取响应的方式是在 window 上调用回调函数 window.onstorage=()=>{}
- 第三遍 2025.2.8 -
 // 1、构造函数的名称记错:BroadCastChannel
// 作业66:React如何处理错误
- 第十二遍 2025.3.11 -
React - static getDerivedStateFromError + componentDidCatch 处理 render + commit 阶段的错误避免错误溢出应用影响 UI
class ErrorBoundaries extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasErr: false };
    }
    static getDerivedStateFromError(e, info) {
        return { hasErr: true }
    }
    componentDidCatch(e) {
        console.log(e);
    }
    render() {
        if(this.state.hasErr){
            return <div> There are errs. </div>
        } else {
			return this.props.children;
        }
    }
}
- 第十一遍 2025.3.2 -
// ❌ 1、少写了 super(props)
- 第十遍 2025.2.27 -
- 第九遍 2025.2.24 -
 // 1、静态函数名称错了 getDerivedStateFromError 
- 第八遍 2025.2.10 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 2025.02 Nextjs 获取链接中的参数
- 第四遍 2025.3.10 -
NextJS 由于预渲染在服务端生成 HTML路由参数要异步拿到
import { useSearchParams } from 'next/navigation';
const params = useSearchParams();

import { useSearchParams } from 'react-route-dom'; ⭐️ // react-router-dom
const [params, setParams] = useSearchParams();

const date = params.get('date');

 Layout.tsx 中使用 Suspense 处理异步情况
export default function Layout(children: React.ReactNode) {
    return <html>
            <body> ⭐️ // Suspense 兜底属性是 fallback={}
				<Suspense callback="<div>Loading</div>">  </Suspense>
            </body>
        </html>
}
- 第三遍 2025.3.1 -
 // 1、依赖包是 react-router-dom,是 router 不是 route
- 第二遍 2025.2.26 -
- 第一遍 2025.2.22 -
 // 1、核心函数的名称记错:useSearchParams
// 2025.02 Mysql 8.0 以上,Sequel报插件错
- 第四遍 2025.3.10 -
本地安装包 - 本地安装最后一步切换认证插件 use_mysql_password ⭐️ // Use Legacy Password
线上切换 root 账号下的认证插件 ⭐️ // caching_sha2_password
- 第三遍 2025.3.1 -
 // 1、线上改变的是 认证插件 mysql_native_password
- 第二遍 2025.2.26 -
- 第一遍 2025.2.22 -
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第六遍 2025.3.10 -
A.hasMany(B, {
    foreignKey: B_id,
    sourceKey: A_id
})
B.belongsTo(A, {
    foreignKey: B_id,
    targetKey: A_id
})
const res = await A.findAll({
    where1,
    include:[{
        model: B,
        where2
    }]
})
SELECT * 
FROM tableA
LEFT JOIN B ON A_id = B_id
- 第五遍 2025.3.1 -
// ❌ 1、include 关键词记错了
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 1、查询需要 await
 // 2、查询关键词 include: [{ model:B }]
- 第二遍 2025.2.17 -
- 第一遍 2025.2.13 -
 // 1、关键词记错了:belongsTo
// 2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?
- 第五遍 2025.3.10 -
对基础类型 - 深浅拷贝都是拷贝值新值的修改不影响旧值
对引用类型 - 浅拷贝拷贝的地址新值的修改会影响旧值深拷贝拷贝的是地址指向的内存新值的修改不影响旧值
浅拷贝 [...arr] Array.from(arr) arr.slice() arr.concat() {...obj} Object.assign({}, obj)
深拷贝 ⭐️ // JSON.parse(JSON.stringify(obj))
function isObj(t) { 
    const type = typeof t; 
    return t !== null && (type === 'function' || type === 'object') 
}
function normalHandler(t, type) {
    switch(type) {
        case '[object Date]':
            return new Date(t.getTime());
            break;
        case '[object RegExp]':
            return new RegExp(t.source, t.flags);
            break;
        case '[object Symbol]':
            return Symbol.for(t.description);
            break;
        default:
            const ctor = t.constructor;
            return new ctor(t);
    }
}
function deepClone(target, m = new WeakMap()) {
    if(!isObj(target)) {
        return target;
    }
    const specialType = ['Map','Set','Object','Array'].map(it => `[object ${it}]`);
    const detailType = Object.prototype.toString.call(target);
    if(!specialType.includes(detailType)) {
        return normalHandler(target, detailType);
    }
	const ctor = t.constructor;
    let res = new ctor();
    if(m.has(target)){
        return target
    }
    m.set(target, res);
    if(detailType === '[Object Map]') {
        target.forEach((value, key) => {
            res.set(key, deepClone(value, m))
        })
    } else if(detailType === '[Object Set]') {
        target.forEach(value => {
            res.add(deepClone(value, m))
        })
    } else if(Array.isArray(target)) {
        target.forEach((it, index) => {
            res[index] = deepClone(it, m)
        })
    } else {
        for(let i in target) {
            res[i] = deepClone(target[i], m)
        }
    }
}
- 第四遍 2025.3.1 -
// ❌ 1、浅拷贝中 Object.assign(obj) 其实是直接返回了 obj 原对象,就是原对象,不算是浅复制,应该是 Object.assign({}, obj)
// ❌ 2、深拷贝中,需要初始化 res 为基础类型,方便后续操作:const ctor = target.constructor; let res = new ctor();
// ❌ 3、Map 的 forEach 的回调函数的参数是 (value, key, map)
- 第三遍 2025.2.8 -
 // 1、属性名写错了:正则类型的 t.flags
 // 2、方法错了:object 不是可迭代对象,不能使用 for...of,这里应该改为 for...in
// 作业61:常见的数据结构有哪些
- 第七遍 2025.3.10 -
按照逻辑结构分
线性结构数组队列链表 非线性结构
- 第六遍 2025.3.1 -
// ❌ 1、线性结构
// ❌ 2、非线性结构
- 第五遍 2025.2.15 -
- 第四遍 2025.2.9 -
- 第三遍 2025.2.2 -
 // 1、少写了一个类型:数组
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第十遍 2025.3.10 -
mysql2  Node 环境提供的数据库驱动可以直接执行 SQL ⭐️ // 可以直连数据库
	- 通过 mysql2/promise 提供异步操作管理多个数据库连接
Sequelize ORM 将数据库的表和行映射为对象模型提供函数式操作
mysql
import { mysql } from 'mysql2/promise'
const pool = mysql.createPool({host, user, databse, password});
const con = await pool.getConnection();
const [rows] = await con.execute('SQL 语句')
Sequelize
import { Sequelize, Modal, DataTypes } from 'sequelize';
const se = new Sequelize(databse, user, password, {
    host: 'localhost',
    dialect: 'mysql'
});
声明 User 模型方法1
const User1 = se.define('Users', {
    name: DataTypes.STRING
}, {
    underscored: true,
    timestamps: true
})
声明 User 模型方法2
class User2 extends Modal{}
User2.init({
    name: DataTypes.STRING
}, {
    sequelize: se,
    modalName: 'Users'
})
- 第九遍 2025.3.1 -
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
 // 1、mysql2:获取连接的函数错了 await getConnection()
 // 2、mysql2:执行 SQL 需要 await
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、sequelize:实例化参数错误 + init 参数错误
 // 2、sequelize:define 第三个参数应该是一些配置
- 第四遍 2025.2.9 -
- 第三遍 2025.1.31 -
 // 1、mysql2:导入依赖包错了 mysql2/promise + createPool的参数 少写了 database
- 第二遍 2025.1.26 -
 // 1、mysql2:创建连接池 createPool 不需要 await
- 第一遍 2025.1.22 -
// 作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据
- 第五遍 2025.3.10 -
返回的是 Response 对象包含了状态码响应头响应行响应体等信息
await res.json() 将响应体中的数据解析为 JSON js 对象
findOne - plain:true 去除查询结果的 Sequelize 实例的其他信息专注于结果对象 ⭐️ // 扁平化
findAll/findOne - raw:true 不对 SQL 查询结果实例化直接返回原始查询结果避免更多的性能消耗
- 第四遍 2025.3.8 -
 // 1、findOne({ where, raw: true }) - fineOne 中两个配置属性都能用
 // 2、plain:将查询结果-单条-转换为扁平化对象,输出一个简单的 js 对象,而不是 Sequelize 的实例对象
 // 3、raw:直接返回 SQL 原始的查询结果 数组/对象,不进行Sequelize实例化操作
- 第三遍 2025.2.21 -
- 第二遍 2025.2.17 -
- 第一遍 2025.2.12 -
// 2025.02 docker-compose 的配置管理 context 
- 第二遍 2025.3.10 -
context 指示 docker 运行的个根目录 // ❌ 1、指定构建上下文路径
在后续 docker.yml 中的 COPY MOVE 基于这个目录操作 // ❌ 1、dockerfile + COPY ADD
- 第一遍 2025.2.28 -
// 2025.02 TS - dayjs 引入但失效问题
- 第二遍 2025.3.10 -
安装 @type/dayjs
tsconfig.ts 中配置 dayjs  CommonJS 模块配置可以被 ES6模块 引入 + 没有导出时也可以被默认引入
{
    esModuleImport: true; // ❌ 1、esMoudleInterop 转换 CommonJS 和 ES6
    asyncSyntheticDefaultImport: true; // ❌ 2、allowSyntheticDefaultImports
}
- 第一遍 2025.2.28 -
// 2025.02 sequelize 在 Nestjs 中的 env 配置
- 第二遍 2025.3.10 -
npm i cross-env + npm i @nestjs/config
package.json: "script": {
    "start:dev": "cross-env NODE_ENV=development nest start --watch",
	"start:prod": "cross-env NODE_ENV=production node dist/main"
}
 app.moudle.ts
import { ConfigService, ConfigModule } from '@nestjs/config';
@Module({
    imports: [
        LtnsModule,
        RoutinesModule,
        TimesModule,
        ConfigModule.forRoot({
            envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
            isGlobal: true
        }),
        SequelizeModule.forRootAsync({
            imports: [ConfigModule],
            inject: [ConfigService],
            useFactory: (configService: ConfigService) => ({
            	port: 3306,
                dialect: 'mysql',
                username: 'root',
                database: 'Daily',
                models: [Ltn, Routine, Time],
                host: configService.get('DB_HOST'),
                password: configService.get('DB_PSW')
            })
        })
    ]
})
⭐️ // export class AppModule {}
- 第一遍 2025.2.28 -
// 2021-07 前端路由的两种模式
- 第四遍 2025.3.10 -
hash 模式:#hash 前端监听这个路由变化切换不会向服务器发送请求
	location.hash + window.onhashchange
history 模式history 修改浏览历史不向服务器发起请求但操作前进后退按钮会触发向浏览器发起请求
	history.pushState({ state: 1 }, 'title', '/route') ⭐️ // 少写了 replaceState
	操作 前进后退history.go(n)/hostory.forward()/hostory.back() 触发监听函数
    window.addEventListener('popState', (e) => { console.log(e.state) })
- 第三遍 2025.2.28 -
// ❌ 1、 window.addEventListener 是在 window 上而不是在 dom 上监听
- 第二遍 2025.2.26 -
- 第一遍 2025.2.20 -
// 作业93:闭包的作用和原理
- 第六遍 2025.3.10 -
原理内部函数引用外部函数的活动对象导致外部函数执行完毕后活动对象没有被清理
	- 底层是词法作用域上下文只和代码所在位置有关 ⭐️ // 在定义时确定作用域,而不是在执行时
作用保存变量封装对象-私有变量回调函数函数工厂
- 第五遍 2025.2.28 -
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
 // 2、①保存状态:允许函数记住它被创建时的环境 + ②数据封装:创建私有变量 + ③回调函数:确保回调函数在调用时能访问到定义时的上下文 + ④函数工厂:创建定制化函数
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情?
- 第十遍 2025.3.10 -
类组件
挂载阶段
	constructor 初始化state和绑定事件处理函数的this
    getDerivedStateFromProps
    ⭐️ // render 用于生成 VDOM
    componentDidMount 挂载到真实 DOM 
更新阶段
	getDerivedStateFromProps
    shouldComponentUpdate(nextProps, nextState) 返回布尔值确定是否要重新渲染
    ⭐️ // render
    componentDidUpdate(preProps, preState) 更新到真实 DOM 
卸载阶段
	componentWillUnMount 卸载前
函数组件
	useEffect 模拟  componentDidMount componentDidUpdate componentWillUnMount
    useLayoutEffect 更新 DOM 同步更新 DOM不可中断阻塞主线程重绘
- 第九遍 2025.2.28 -
- 第八遍 2025.2.12 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.31 -
 // 1、少写了 constructor - 初始化 state 和绑定事件处理函数的 this
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
 // 1、shouldComponentUpdate 写错在挂载阶段,实际应该归属于更新阶段
 // 2、阶段划分是:挂载-更新-卸载,其中更新阶段记错了 - 应该是当组件的 state/props 变化,进入更新阶段,重新执行 render 函数来更新 DOM
- 第三遍 2025.1.19 -

LTN ①⑧

工具推荐周期:2.28-3.8 8天 LTN1-46题,LTN2-30题,LTN3-31题,LTN4-1题,LTN5-18题,LTN6-1题

共计 124 题,开始做题时间

LTN1 【推荐做题时间 02-26 - 6题 实际做题时间: 2.28】 ✅作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法 ❌作业93:闭包的作用和原理 ✅2024-11 第十九章 表单脚本 ✅2024-10 第十二章 BOM 小结 ✅2024-09 第七章 迭代器与生成器 小结 ✅作业100:写一个原生的 form ,写出两个提交表单的方式 + 阻止提交方式,表单字段的公共属性/方法,怎么校验表单

LTN2 【推荐做题时间 02-26 - 2题 实际做题时间: 2.28】 ❌作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ✅2021.07 数组扁平化(一层、全部展开、指定深度)

LTN5 【推荐做题时间 02-26 - 8题 实际做题时间: 2.28】 ✅作业15:类由哪些组成,不同定义的内容在继承时有什么特点?类是怎么实现继承的? ✅2019.06 检测数据类型 + 布尔转换为0的值 ✅2019.07 for循环+计时器,如何实现i按序输出 ✅2021.07 怎么判断数组类型?4种方法 ❌2024.10 第九章 代理与反射 小结 ✅2024.10 第十章 函数 小结 ✅2021.04 03 为什么很多网站第二次打开速度会很快 ✅2019.06 函数参数传值修改

LTN1 【推荐做题时间 02-27 - 9题 实际做题时间: 2.28】 ✅作业40:写出以下值

1Number(null)
2Number(undefined)
3isNaN() 检测那些值为 true?
4NumberparseIntparseFloat 检测 空字符串 得到?
51+'2'+'2'
1+ +'2'+'2'
1+ -'1'+'2'
'A'-'B'+'2'
'A'-'B'+ 2
6let i = 0, age = 30;
i++ + age;
++i + age;
7 12.34 | 0;
12.43 >> 0;
8null === undefined

✅作业32:什么是队头阻塞?原因是什么?怎么解决? ✅作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码 ✅2019.07 引用css,link和@import的区别 ✅2019-06 第五章 基本引用类型 小结 ❌2021-07 前端路由的两种模式 ❌2025.02 sequelize 在 Nestjs 中的 env 配置 ❌2025.02 TS - dayjs 引入但失效问题 ❌2025.02 docker-compose 的配置管理 context

LTN4 【推荐做题时间 02-27 - 1题 实际做题时间: 2.28】 ✅作业41:TED 如何解决焦虑

LTN1 【推荐做题时间 02-28 - 4题 实际做题时间: 3.1】 -✅作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置 ✅69 x 的平方根

LTN1 【推荐做题时间 03-01 - 7题 实际做题时间: 3.1】 ✅作业1:写一个只能被迭代指定次数的类 ✅作业11:写出一个用 yield 实现的递归算法,从0数到指定n ✅作业 89:mysql 中类型的区别:varchar 和 char,date datetime ✅作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 -❌作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ❌2025.02 Mysql 8.0 以上,Sequel报插件错 ❌2025.02 Nextjs 获取链接中的参数

LTN2 【推荐做题时间 03-01 - 7题 实际做题时间: 3.1】 ✅作业25:简单写下 请求创建过程 ✅作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? ✅作业55:flex 常见缩写 ✅作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ❌作业61:常见的数据结构有哪些 ✅作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅作业76:显卡的作用?

LTN3 【推荐做题时间 03-01 - 7题 实际做题时间: 3.1 + 3.2】 ✅2021.05 29 WebSocket -✅2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击 ❌2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝? -✅2020.03 模块化 ❌2021.06 跨页面通信 ❌2019.07 数组去重(算法整理) ❌2021.07 柯里化函数实现

LTN1 【推荐做题时间 03-02 - 8题 实际做题时间: 3.2】 -✅作业70:Vue 和 React 的 Diff 算法比较 ✅作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅2023.07 clickhouse、mysql、mongodb异同 ✅2019.07 显示省略号 ✅作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景 ✅2021.07 12-1 内存泄漏 ❌2021.06 flex布局 -✅作业99:渲染合成层定义、触发方式、优点、缺点

LTN1 【推荐做题时间 03-03 - 7题 实际做题时间: 3.2 + 3.5 】 ❌作业66:React如何处理错误 ✅2021.06 子盒子在父盒子中水平垂直居中有几种方法? -✅2021.07 babel 作用+原理+补丁 -✅2021.05 27 HTTP/3 改进的点 ❌2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程 -✅2021.04 12 垃圾回收机制 ❌2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序

LTN3 【推荐做题时间 03-03 - 3题 实际做题时间: 3.5】 -✅作业71:React为什么要合成事件?和原生事件有什么不同? ✅作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口) -✅作业53:胜利者一无所获 说的是什么

LTN1 【推荐做题时间 03-04 - 1题 实际做题时间: 3.5】 ❌2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码

LTN3 【推荐做题时间 03-04 - 8题 实际做题时间: 3.5】 ❌作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 ✅作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期 ✅作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的 ✅作业85:Lambdas是什么 -❌2024.12 模拟实现promise -✅2024.05 rem是基于什么原理进行适配的? ✅2021.04 05-3 白屏优化? ✅作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?

LTN6 【推荐做题时间 03-04 - 1题 实际做题时间: 3.5】 ✅作业5:什么是生成器对象?有什么特点?什么时候会被执行?

LTN1 【推荐做题时间 03-05 - 1题 实际做题时间: 3.5】 -❌2025.02 Express VS KOA2

LTN2 【推荐做题时间 03-05 - 5题 实际做题时间: 3.5】 ✅作业90:金玉满堂是什么意思?在花卉中的说法是什么? ✅作业91:三次握手的过程和具体包的作用 ✅2021.07 事件流 + 事件模型 ✅2021.06 链表 141. 环形链表 ✅2021.09 get和post有什么区别

LTN3 【推荐做题时间 03-05 - 1题 实际做题时间: 3.5】 ✅作业78:使一个标签看不见的几种样式及其区别

LTN2 【推荐做题时间 03-06 - 3题 实际做题时间: 3.5 + 3.6】 ✅作业58:React和Vue是怎么描述UI的 -✅作业62:fiber架构是什么?优点和实现方式? ❌作业64:Vue渲染怎么实现的?

LTN5 【推荐做题时间 03-06 - 10题 实际做题时间: 3.6】 ✅作业18:多进程浏览器会开启几个进程(5个点)?相同站点的页面,可以复用么? ✅作业19:发起请求后,得到 301,有效信息是哪些? ✅作业21:下面这段代码输出结果是什么?为什么

console.log(11, fun1);
function fun1(n1, n2) { arguments[1] = 10; return n1 + n2; }
var fun1 = 1;
console.log(22, fun1)

✅作业22:分别输出什么?

function foo(){
    var a=1;
    let b=2;
    {
        let b=3;
        var c=4;
        let d=5;
        console.log(a);
        console.log(b);
    }
    console.log(b);
    console.log(c);
    console.log(d);
}
foo();

✅作业27:写一个 sleep 函数 ✅作业28:写出 js 按什么顺序执行

<script src='./a.js' defer></script>
<script src='./b.js'></script>
<script>
    console.log('event start');
    document.addEventListener('DOMContentLoaded', () => {
        console.log('DOMContentLoaded finish');
    })
    window.addEventListener('load', () => {
        console.log('onload finish');
    })
</script>
<body>
    <div>
        <script>
            let i = 0;
            while(i<1000) {
                i++;
            }
            console.log('compute finished');
        </script>
    </div>
</body>

✅作业35:写出HTPPS和HTTP的差异?对称加密和非对称加密有什么异同?混合加密是怎么做的? ❌2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现? ✅2024.10 第十一章 期约与异步函数 小结 ✅2019.06 随机给一个盒子添加一个十六进制的颜色

LTN2 【推荐做题时间 03-07 - 6题 实际做题时间: 3.6 + 3.8】 ✅作业67:React 怎么做的性能优化? ✅作业68:React hooks 的原理是什么?useEffect useState模拟实现 ✅2020.07 对象分类、Promise按序执行、实现map ✅2024.11 第十七章 事件 ✅2021-07 常见Webpack问题 -❌作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据

LTN3 【推荐做题时间 03-07 - 8题 实际做题时间: 3.8】 ✅2024.11 简单的发布 - 订阅模式实现 EventEmitter ❌2024.10 怎么实现跨域 ✅2022.08 包管理工具 ❌2019.10 两栏,左边固定,右边自适应的布局 ✅2019.10 三栏,两边固定,中间自适应 -✅2024.09 第八章 对象、类和面向对象编程 小结 ✅2021-06 Position属性 - 占位?相对什么定位? ✅2019-06 第四章 变量、作用域和内存问题 小结

LTN2 【推荐做题时间 03-08 - 7题 实际做题时间: 3.8】 ✅作业2:支持迭代器的原生语言特性有哪些? ✅2019.07 为什么要清除浮动?怎么清除浮动? ✅作业45:原生+canvas 显示一张png格式的图片 ✅2024.10 第十四章-第十五章 DOM、DOM扩展 ✅2024.12 模拟实现instanceof ✅2019.07 h5新标签和语义化,块/行内元素 ❌作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?

LTN3 【推荐做题时间 03-08 - 4题 实际做题时间: 3.8】 ✅作业43:TED 怎么提升自信 ✅作业42:TED 如何和大脑正确交流沟通 ✅作业39:shim 和 polyfill 区别 ✅2021.06 CSS 选择器 - 权重/匹配方式


做题记录

// 2021.06 CSS 选择器 - 权重/匹配方式
- 第五遍 2025.3.8 -
#id > .red attr[href] :hover LVHA > div ::after ::before > *
!important > 行内 > 内联按下载顺序 ⭐️ // 内联/外联
- 第四遍 2025.2.15 -
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
!important > 内联 > 行内   // 1、行内 > 内联/外联
// 作业39:shim 和 polyfill 区别
- 第五遍 2025.3.8 -
shim 垫片提供新的 API利用浏览器原生功能优雅降级 ⭐️ // 处理兼容性问题
polyfill 补丁利用低版本的功能实现浏览器原生功能
- 第四遍 2025.2.15 -
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
// 作业42:TED 如何和大脑正确交流沟通
- 第五遍 2025.3.8 -
1I like itI want it I've chosen it!
2、让大脑对它变熟悉
大脑只会对我们告诉它的语言和画面有反应
大脑天然追寻快乐,所以把想做的事情捆绑到快乐,把不想做的事情捆绑到痛苦
大脑喜欢熟悉的事情
- 第四遍 2025.2.15 -
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
// 作业43:TED 怎么提升自信
- 第五遍 2025.3.8 -
1repetition repetition repetition 自信的前提是自我效能真的相信自己能够做到
2不批评自己夸奖表扬想要的样子 - 做自己最好的引导型导师
3用自己的语言解释世界 - 没有客观存在的世界世界本就是各个视角
- 第四遍 2025.2.15 -
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
// 作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?
- 第四遍 2025.3.8 -
const start = dayjs(new Date(time)).utc().startOf('day');
 // 1、utc 使用方式错误 dayjs.utc(传递 Date类型) + 缺少转换成 Date 类型 .toDate()
const end = dayjs(new Date(time)).utc().endOf('day');
第一种
const where = {
    date: {
        [op.between]: [start, end]
    }
}
第二种
const where = sequelize.where(sequelize.fn('DATE', {field: 'date'}), '=', time)
 // 2、sequelize.fn 传递的第二个参数是 Sequelize.col('date')
 // 3、DATE 函数直接判断是否等于 time;DATE_FORMAT 函数要传递参数 '%y-%m-%d' 再判断是否等于 time
一般推荐第一种因为可以使用索引第二遍需要遍历
索引是数据库查询时为表建立的单独的 b-tree 结构查询某个条件时可以直接根据索引查到指定内容而不需要遍历整个表
- 第三遍 2025.2.22 -
- 第二遍 2025.2.17 -
 // 1、函数式写法,少写了 sequelize.where 
- 第一遍 2025.2.13 -
 // 1、需要先将传入的 string 转换成 date + 还需要处理成 Date 类型查询
// 2019.07 h5新标签和语义化,块/行内元素
- 第五遍 2025.3.8 -
1新样式 2SEO友好
新标签header footer aside nav article address detail section hgroup filedset datalist  ⭐️ // time
input 新属性:placeholder required multiple autocomplete autofocus accesskey
input type 新属性: tel url email range time week month date number ⭐️ // datatime search
div hr table dl dt dd ol ul li table-caption p ⭐️ // section tr address
行内块img input td
行内select label span button a em i u strong ⭐️ // b br textarea
- 第四遍 2025.2.22 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
 // 1、br - 错了,不是块元素,而是行内元素 
- 第一遍 2025.1.9 -
// 2024.12 模拟实现instanceof
- 第五遍 2025.3.8 -
function fakeInstanceof(target, Ctor){
    let proto = target.__proto__; // ⭐️ Object.getPrototypeOf(target)
    while(proto){
        if(proto === Ctor.prototype) {
            return true
        }
        proto = proto.__proto__;
    }
    return false
}
- 第四遍 2025.2.22 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
 // 1、获取 target 的原型链->应该是 target.__proto__ 或者 Object.getPrototypeOf(target)
 // 2、判断原型链上是否和构造函数的原型对象相等,而不是和构造函数相等 proto===Ctor.prototype
 // 3、不停往上找原型链 -> 通过 __proto__找,而不是 prototype 
- 第一遍 2025.1.9 -
// 作业45:原生+canvas 显示一张png格式的图片
- 第五遍 2025.3.8 -
<canvas height=200 width=200 id='drawing'></canvas>
const drawing = document.getElementById('drawing');
if(drawing.getContext){
    const imgUrl = drawing.toDataImage('image/png'); // ⭐️ toDataUrl
    const dom = document.createElement('img');
    dom.src = imgUrl;
    document.body.appendChild(dom);
}
- 第四遍 2025.2.22 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.14 -
 // 1、需要判断是否存在 context 作为大前提:if(drawing.getContext){}
 // 2、image 标签添加 url 的属性是 src:image.src = imageData;
- 第一遍 2025.1.9 -
// 2019.07 为什么要清除浮动?怎么清除浮动?
- 第七遍 2025.3.8 -
对浮动元素块级元素布局时当做不存在行内元素环绕布局在父元素内的子元素浮动后高度塌陷背景无法撑开
解决1父级元素添加高度 2父元素内最后添加一个盒子 .box{clear:both}
3父元素添加伪元素 .father::after{content:''; display:block; clear:both;}
4BFC .father{overflow:hidden;}
- 第六遍 2025.2.22 -
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、父元素+伪元素 应该是 display: block,否则撑不开父级全部宽度
- 第三遍 1.28 -
- 第二遍 1.25 -
 // 1、高度塌陷的描述:背景无法撑开 + padding、border 无法正常展示
 // 2、给父元素添加高度:宽度不对,高度+padding+border
- 第一遍 1.20 -
// 作业2:支持迭代器的原生语言特性有哪些?
- 第七遍 2025.3.8 -
for-of Array.from ... 数组解构 new Set new Map yield * Promise.race Promise.all
- 第六遍 2025.2.22 -
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、少写了 yield * 只能在生成器中使用
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
// 2021-06 Position属性 - 占位?相对什么定位?
- 第三遍 2025.3.8 -
static 占位相对于父级 // ⭐️ 标准文档流
relative 占位相对于自己
absolute 不占位相对于上一级 position  static 的元素
fixed 不占位相对 window 或者视口
sticky 占位超过一个阈值后和 fixed 一样
- 第二遍 2025.2.14 -
- 第一遍 2024.12.25 -
// 2019.10 三栏,两边固定,中间自适应
- 第五遍 2025.3.8 -
.left, .right{ width: 100px }
1 .left,.right{ position:absolute; top:0 } .right{ right:0 } .middile{ margin: 0 100px }
2 .outer{ display:flex } .middle{ flex:1 }
3 .outer{ display:table;widtg:100% } .left,.right,.middle{ display: table-cell }
.outer{ display:grid; grid-template-columns: 100px auto 100px }
4 .left,.right,.middle{ float:left } .middle{ width: cacl(100% - 200px) }
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
// ❌ 1、cacl:需要三浮动,否则middle 的存在会让 right 因为要向左靠齐换行了
// 2019.10 两栏,左边固定,右边自适应的布局
- 第五遍 2025.3.8 -
.left { width: 200px }
1 float .left{ float:left } .right{ margin-left: 200px }
2 position .left{ position:absolute } .right{ margin-left: 200px }
3 BFC .left{ float: left } .right{ overflow: hidden }
4 table/grid .outer{ display: table; width: 100%; } .left,.right { display: table-cell }
.outer{ display: grid; grid-template-column: 200px auto; }  // 1、grid-template-columns
5 flex .outer{ display: flex } .right{ flex:1; 1 1 0 }
6 cacl .left,.right{ float: left } .right{ width: cacl(100% - 200px) }
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
 // 1、grid-template-columns
 // 2、父盒子变成弹性盒子后, right 要变成可拉伸 .right { flex: 1 }
// 2024.10 怎么实现跨域
- 第五遍 2025.3.8 -
原因浏览器有同源协议
1jsonp 利用 script  src 标签可以请求第三方资源  // 1、只能处理 GET 请求
2nginx 转发服务端之间没有同源协议限制
3CORS Access-Control-Allow-Origin 允许引入的第三方资源域名 响应头/meta 标签设置  // 2、 服务端允许的哪些域名可以访问
4Websocket 不受同源协议限制
- 第四遍 2025.2.14 -
- 第三遍 2025.1.24 -
// ❌ 1、CORS:哪些域名可以访问资源 - 是为了让浏览器知道哪些域名可以访问,而不是从客户端角度描述
// 2024.11 简单的发布 - 订阅模式实现 EventEmitter
- 第六遍 2025.3.8 -
class EventEmitter {
    constructor() {
        this.events = [];
        this._max = 10;
    }
    add(event, cb){
        if(!this.events[event]){
            this.events[event] = [];
        }
        this.events[event].push(cb);
        if(this.events[event].length > this._max) {
            throw new Error('exceed events')
        }
    }
    changeLimit(n) {
        this._max = n;
        return this;
    }
    remove(event, fn) {
        if(!fn) {
            delete this.events[event]
        }
        if(Array.isArray(this.events[event])){
            const index = this.events[event].indexOf(fn);
            index > -1 && this.events[event].splice(index, 1);
        }
    }
    once(event, fn) {
        const onceFunc = (...arg) => {
            fn(...arg);
            this.remove(event, onceFunc);
        }
        this.add(event, onceFunc);
    }
    emit(event) {
        const args = arguments.slice(1);
        if(Array.isArray(this.events[event])) {
            const copy = [...this.events[event]];
            copy.forEach((it, index) => {
                const arg = [...args][index];
                Array.isArray(arg) ? it(...arg) : it(arg);
            })
        } // ⭐️ else { return false } 没有该 event 的事件,则返回 false
    }
}
- 第五遍 2025.2.14 -
- 第四遍 2025.1.24 -
- 第三遍 2025.1.19 -
 // 1、remove:这里要判断 index 存在才去删除:if(index>-1){ ... }
 // 2、emit:这里一定要注意,由于 once 的存在,this.events[event] 在执行过程是动态改变的,所以这里一定要将 this.events[event] 拷贝下来再处理 let static = [...this.events[event]]
 // 3、emit:args 还是要传进函数的 Array.isArray(args) ? it(...args) : it(args)
// 作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据
- 第四遍 2025.3.8 -
Nextjs fecth 返回的 NextResponse 包含了响应头响应行响应体 // ⭐️ 还有状态码
.json() 会取出响应体中的数据转换为 json 格式数据 // ⭐️ 是异步函数 await res.json();
findAll({ where, raw: true }) 
findOne({ where, plain: true })  // 1、findOne({ where, raw: true }) - fineOne 中两个配置属性都能用
plain - 是将一个 row 打平但不包括嵌套的格式嵌套依旧是嵌套  // 2、plain:将查询结果-单条-转换为扁平化对象,输出一个简单的 js 对象,而不是 Sequelize 的实例对象
raw - 将所有行的数据打平包括嵌套格式  // 3、raw:直接返回 SQL 原始的查询结果 数组/对象,不进行Sequelize实例化操作
- 第三遍 2025.2.21 -
- 第二遍 2025.2.17 -
 // 1、状态码、响应头、响应体
 // 2、await res.json()
- 第一遍 2025.2.12 -
// 2021-07 常见Webpack问题
- 第五遍 2025.3.6 -
/** 1、webpack的几个核心概念理解:Chunk、Module、plugin
2、常见配置项:entry、output、module、resolve等
自定义 loader 怎么配置?
3、Code Splitting 和 Tree Shaking 的区别?懒加载怎么实现?
4、html-webpack-plugin 作用?
5、sourceMap不同环境的区别?怎么开启配置?
6、热更新怎么实现?
7、webpack原理/执行过程?
开发插件的桥梁?*/
1Chunk 由多个模块组成通过 entry 分组Module 万物皆模块plugin 构建过程中在指定时机注入代码
2entry: './main.js'/{ main: './main.js', sub: './sub.js' }
output: { filename: '[name]-[hash].js', path: path.resolve(_dirname, 'dist') }
module:{ rules: [
    { test:/.\js/, use: ['my-loader'] }, 
    { test:/.\css/, use: ['style-loader', 'css-loader', 'sass-loader'] }
]}
resolve: {
    alias: { "@c": 'src/components' },
	extensions: ['jsx', 'js', 'json'],
	modules: ['node_modules', path.resolve(_dianame, 'src/my')]
}
自定义 loader:
 resolve.modules 中配置自定义模块的路径 +  module.rules 中配置直接的 loader如上
 module.rules 中直接定义路径 use: [path.resolve(_dirname, 'src/my/my-loader')]
3Code Splitting 按需加载根据 entry 分组只加载当前需要需要的模块代码提高加载渲染性能 
// ⭐️ 学名:代码分割,开启方式 splitChunks 插件开启配置,适合单页应用
Tree Shaking 按需打包减小包大小根据 entry 查找依赖 // ⭐️ 默认开启
懒加载 首次渲染的时候 利用 code splitting 只加载当前所需模块 // ⭐️ 首屏
4html-webpack-plugin webpack打包后将静态资源插入 html 直接访问
5sourceMap
devtool: eval-cheap-source-map; 本地
devtool: hidden-source-map; 线上
6热更新  webpack 启动时在内存中启动一个服务 dev-server同时注入 HMR 代码使用 Websocket 和浏览器保持双向实时通讯
 有代码更新监听 Compiler  done 事件得到 manifest.json  chunk.js 文件 mainfest.json 主动推送给浏览器浏览器从中获得 chunk.js 的信息发起 ajax  webpack 服务发起请求
 浏览器拿到新的模块代码后不需要刷新整个页面执行代码替换window.webpackUpdate -> hotApply 替换模块执行 webpack.__require__执行模块代码 // ⭐️ window.webpackHotUpdate hotApply热重载
7原理
 合并 shell 脚本和配置文件 // ⭐️ 初始化参数
 得到的配置文件初始化 webpack  Compiler并加载所有插件执行 .run 开始执行 // ⭐️ 实例化
  entry 入口按照依赖遍历模块每个模块通过 loader 转换得到结果 // ⭐️ 串行调用
 按照 entry 得到 chunk输出到下载列表
 按照 output 配置输出到指定地址
Compiler - webpack 的生命周期唯一
Complication - 更新一次代码触发一次构建就创建一次
- 第四遍 2025.2.21 -
- 第三遍 2025.2.18 -
- 第二遍 2025.2.14 -
 // 1、entry: 配置方式记错 - 不识别 path 这个属性 + output 配置属性记错
 // 2、resolve 的作用不是解析,是「依赖查找配置规则」+ module 的作用是:依赖解析、转换配置
 // 3、resolve 的属性 modules + extensions 错误
 // 4、开启 Code Splitting 配置的方式:splitChunk 插件配置
 // 5、devtool:cheap-eval-source-map; (不提供列信息)方便代码调试
 // 6、 监听到 compiler 的 done 事件 得到的文件是 chunk.js 文件,浏览器从 mainfest.json 文件中得到了 ChunkId,获取 chunk.js
 // 7、window.webpackHotUpdate -> hotApply 热更新函数,替换原有模块代码
- 第一遍 2024.12.26 -
// 2020.07 对象分类、Promise按序执行、实现map
- 第七遍 2025.3.6 -
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[key];
        if(!pre[key]) {
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre
    }, {})
}
function sortList(arr, init) {
    return arr.reduce((pre, cur) => pre.then(cur), Promise.resolve(init))
}
Array.prototype.fakeMap = (cb, thisArg) => {
	return this.reduce((pre, cur, index, array) => {
        pre[index] = cb.call(thisArg, cur, index, array);
        return pre
    }, [])
}
- 第六遍 2025.2.21 -
- 第五遍 2025.2.18 -
 // 1、classify分类函数:reduce 的初始值应该是 {} 而不是 []
 // 2、map 是 Array 上的高阶函数,之前写错成 Function(想成bind了),重写了
- 第四遍 2025.2.14 -
 // 1、again,map 接收两个参数,一个 callback 函数,另一个是 this 指向
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
// 作业68:React hooks 的原理是什么?useEffect useState模拟实现
- 第九遍 2025.3.6 -
闭包 + 组件更新时执行全部 hooks 计算得到最新的 state
// ⭐️ hook 的目的是在不编写类组件的情况下,可以使用 state 状态管理能力、生命周期替代能力
useEffect
const memorizedState = {};
let cursor = 0;
const render = () => {
    ReactNode.render('<App />', document.getElementById('root')); // ⭐️ ReactDOM.render
    cursor = 0;
};
function useEffect(cb, dep) {
    const lastDep = memorizedState[cursor];
    const hasChanged = lastDep && lastDep.some((it, index) => !Object.is(it, dep[index]));
    if(!lastDep || hasChanged) {
        cb();
        memorizedState[cursor] = dep;
        render();
    }
    cursor++;
}
useState
function useState(init) {
    const current = cursor;
	memorizedState[current] = memorizedState[current] ?? init;
    const setValue = (newV) => {
        memorizedState[current] = newV;
        render();
    }
    cursor++;
    return [memorizedState[current], setValue]
}
- 第八遍 2025.2.21 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.12 -
 // 1、原理:没有事件委托,主要是 闭包和链表 + 少写了 hooks 的创建目的
 // 2、render:ReactDOM.render(<App />, document.getElementById('root')); 
 // 3、useEffect:使用 find 得到的是值,应该使用 some + 判断条件应该是 !Object.is
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 2025.1.13 -
 // 1、UseEffect:少写了「更新依赖数组」的逻辑:memorized[count] = array;
 // 2、UseState:使用 value 会导致返回后,value 不会自动更新,和 memorized[currentCount] 失去了联系,改为:memorized[currentCount] = memorized[currentCount] ?? init;
 // 3、UseState:调用 setValue 必须触发 render:render();
 // 4、UseEffect:cursor应该使用其他变量保存,和下文中 setValue 形成闭包 - 在异步情况下,cursor 可能会变化,导致索引错误
// 作业67:React 怎么做的性能优化?
- 第八遍 2025.3.6 -
1fiber架构时间分片渐进式渲染优先级调度Diff增量式计算
2是否重新渲染的优化策略eagerState 策略bailout 策略
eagerState 如果 state 的新值和旧值无关则直接使用新值进行渲染不再访问旧值
bailout 如果组件的 props  state 没有变化则跳过重新渲染
	- 类函数组件 shouldComponentUpdate(nextProps, nextState) 自定义是否重新渲染返回布尔值
    - 函数组件 React.memo((props) => {}) 浅比较 props没有变化则跳过渲染
    - useCallback/useMemo
- 第七遍 2025.2.21 -
- 第六遍 2025.2.17 -                                
- 第五遍 2025.2.12 -
 // 1、少写了 函数组件 React.memo - 浅比较props,不变则直接复用 
 ⭐️ // 记忆法:shouldComponentUpdate(类组件)/React.memo(函数组件)+2个hook:useMemo/useCallback
- 第四遍 2025.1.23 -
- 第三遍 2025.1.18 -
 ⭐️ // useCallback:每次渲染,函数都是重新生成的,用了这个就缓存住了
- 第二遍 2025.1.12 -
// 2019.06 随机给一个盒子添加一个十六进制的颜色
- 第二遍 2025.3.6 -
function addColor() {
    let res = '#';
    const arr = ['0','1'...'a', ...'f'];
    Array(6).fill(1).forEach(it => {
        const index = Math.floor(Math.random()*16);
        res += arr[index]
    })
    return res
}
- 第一遍 2025.1.30 -
// 2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现?
- 第二遍 2025.3.6 -
js 等待下载占用主线程执行浏览器不同版本兼容问题需要写代码
css 预解析线程下载完成即有动画渲染css 一般不存在兼容问题开启 GPU 加速  // 1、css 动画可以在合成线程中完成,例如 transform opacity,有效避免回流重绘的性能损耗
- 第一遍 2025.1.30 -
⭐️ // 避免回流重绘 + 自然降级 + 硬件GPU加速 + 预扫描加载快
// 作业35:写出HTPPS和HTTP的差异?对称加密和非对称加密有什么异同?混合加密是怎么做的?
- 第二遍 2025.3.6 -
HTTPS = HTTP + TLS/SSL
对称加密 加密解密用的一个密钥
非对称加密 加密用的公钥可以分发给客户端解密私钥只有服务端知道
混合加密 使用非对称加密商量出一个公钥之后使用这个公钥对称加密连接使用 // ⭐️ 协商
- 第一遍 2025.1.30 -
// 作业28:写出 js 按什么顺序执行
- 第二遍 2025.3.6 -
b.js - event start - compute finished - a.js - DOMContentLoaded finish - onload finish
- 第一遍 2025.1.30 -
// 作业27:写一个 sleep 函数
- 第二遍 2025.3.6 -
async function sleep(delay) {
	return new Promise(resolve => setTimeout(resolve, delay))
}
await sleep(200)
- 第一遍 2025.1.30 -
// 作业22:分别输出什么?
- 第二遍 2025.3.6 -
1 3 2 4 err
- 第一遍 2025.1.30 -
// 作业21:下面这段代码输出结果是什么?为什么 
- 第二遍 2025.3.6 -
function 1
在函数作用域内先创建 arguments再函数提升再变量提升
- 第一遍 2025.1.30 -
// 作业19:发起请求后,得到 301,有效信息是哪些?
- 第二遍 2025.3.6 -
响应头里面的 Location触发浏览器的重新导航
- 第一遍 2025.1.30 -
// 作业18:多进程浏览器会开启几个进程(5个点)?相同站点的页面,可以复用么?
- 第二遍 2025.3.6 -
浏览器进程 渲染进程xN 插件进程xN 网络进程 GPU进程
同域名同协议同端口可复用 渲染进程
- 第一遍 2025.1.30 -
// 作业64:Vue渲染怎么实现的?
- 第九遍 2025.3.6 -
// ⭐️ AOT 预编译 template
1模版编译parse-词法语法解析得到 ASToptimize-标记静态节点generate-生成 render 函数
2js数据双向绑定
- 利用数据劫持订阅发布对数据进行 Object.defineProperty(obj, property, {get, set})/Proxy
- 组件初始化时创建 Watcher 访问 set 时收集正在执行的 Watcher 到组件的 Dep   // 1、getter
- 组件更新/初次渲染时访问 get 触发通知所有 Watcher -  dep.notify() // 1、setter
3执行 render 函数得到 VDOM
4Diff 计算最小更新 UI应用到真实 DOM
- 第八遍 2025.2.20 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.11 -
 // 1、模版编译的阶段名称错误:parse - optimize - generate
 // 2、函数使用方法错误:Object.defineProperty(obj, 属性, { get, set })
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
// 作业62:fiber架构是什么?优点和实现方式?
- 第九遍 2025.3.6 -
三大主流程 
- Scheduler 调度器管理优先级处理在整个渲染阶段起到宏观调控
- Reconciler  对应 render 阶段在主线程空闲重绘之前计算是否可以执行时间分片 shouldYield // ⭐️ 协调器
- Render 渲染器对应 commit 阶段更新到真实 DOM同步执行无法被中断
Scheduler + Reconciler在内存中执行异步可被打断:①错误时间分片不够有更高优先级的任务 // ⭐️ 在内存中执行,不会更新宿主环境的 UI
优点
1时间分片 + 渐进式渲染之前 React 需要遍历更新整个应用生成 VDOM会长时间占用主线程阻塞重绘使用 fiber 架构实现时间分片后将渲染任务划分为一个一个小渲染任务单元在空闲重绘前执行计算有充足时间则执行
2优先级调度优先处理用户交互的渲染任务提高用户体验
3Diff 增量式更新根据渲染任务的执行情况计算最小更新UI提高 Diff 算法效率
- 第八遍 2025.2.20 -
- 第七遍 2025.2.17 -
- 第六遍 2025.2.10 -
 // 1、时间切片:之前 react 会对整棵树 对比+更新,同步执行,会长时间占用主线程,阻塞重绘;描述优化:小的单元任务,在空闲 + 重绘判断执行
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
 // 1、忘记中文:Scheduelor 是调度器
 // 2、增量式 Diff:不是一次性比较整棵树,而是根据任务执行进度,逐步比较、更新部分节点 - 减少了每次比较的范围,提高了效率 
// 作业58:React和Vue是怎么描述UI的
- 第八遍 2025.3.5 -
React: `{}` JSX类组件函数组件propsstate
Vue: 模版语法 + 指令 v-if/v-else-if/v-for/v-show/v-model + ``
- 第七遍 2025.2.20 -
- 第六遍 2025.2.17 -
- 第五遍 2025.2.10 -
 // 1、React 少写了 :类组件+函数组件
 // 2、Vue 少写了:模版语法 + v-show
- 第四遍 2025.1.22 -
- 第三遍 2025.1.18 -
// 作业78:使一个标签看不见的几种样式及其区别
- 第五遍 2025.3.5 -
display: none; 不占位但是 DOM 节点还在触发回流+重绘
	- v-if 不占位同时删除 DOM 中的节点
opacity: 0; 占位只重绘且绑定的事件依旧可以被触发
visibility: hidden; 占位只重绘绑定的事件无法被触发
	- v-show
- 第四遍 2025.2.12 -
- 第三遍 2025.1.23 -
- 第二遍 2025.1.19 -
 // 1、display: none 元素仍在 DOM 中,会触发回流+重绘; v-if 移除了 DOM 节点
// 2021.09 get和post有什么区别
- 第四遍 2025.3.5 -
get  参数携带在请求行参数大小 2kb 浏览器默认缓存  回退无害参数在历史记录中保存了
post 参数携带在请求体参数大小不限制浏览器默认不缓存回退有害参数不在记录中
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、从四个方面描述:数据参数 + 参数长度 + 回退安全性 + 缓存
// 2021.06 链表 141. 环形链表
- 第四遍 2025.3.5 -
function isCircle(head) {
    try{
        JSON.stringify(head);
    }catch(e){
        return true
    }
    return false
}
function isCircle(head) {
    while(head){
        if(head.isCircle) {
            return true;
        }
        head.isCircle = true;
        head = head.next;
    }
    return false
}
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、判断条件是 while(head) 
// 2021.07 事件流 + 事件模型
- 第四遍 2025.3.5 -
事件流:
	先捕获后冒泡 window-document-html-body-...
事件模型:
	事件委托 // ⭐️ 事件委托是利用了事件冒泡的特性
    dom.addEventListener('click', ()=>{})
- 第三遍 2025.2.19 -
⭐️ // 冒泡:从具体节点逐渐向上传播到 DOM 最高层父节点
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业91:三次握手的过程和具体包的作用
- 第四遍 2025.3.5 -
SYN 序号包 ACK 确认包 // ⭐️ SYN 同步序列编号包
客户端SYN
服务端SYN + ACK(标志位1)
客户端ACK
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业90:金玉满堂是什么意思?在花卉中的说法是什么?
- 第四遍 2025.3.5 -
形容财富多比如学富五车富有学识
玉棠春富贵 - 玉兰 海棠 迎春 牡丹 桂花
- 第三遍 2025.2.19 -
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 2025.02 Express VS KOA2
- 第一遍 2025.3.5 -
 // 1、Express 适合快速开发、功能完整性高的项目;KOA2 适合对性能、代码灵活性比较高的项目
 // 2、相同点:①基于NodeJS ②具备路由功能,将请求分发到处理函数 ③中间件机制来处理请求和响应
Express 
	中间件机制是线性的 // ⭐️ 5、中间件执行流程 - 线性流程,请求依次经过每个中间件
    模版引擎ejs
     // 3、设计理念:功能全面、成熟,内置了常用功能、工具,开箱即用,适合快速搭建
     // 4、异步处理方式:早期使用回调函数处理异步操作,会出现回调地狱
     // 6、错误处理:错误处理中间件 app.use((err, req, res, next)=>{res.status(500).send()})
KOA2 
	中间件机制是洋葱模型 // ⭐️ 5、中间件执行流程 - 洋葱模型,经过每个中间件入栈,再依次出栈
    封装 http 简化为 get set
    模版引擎ejs art-template
	 // 3、设计理念:轻量级,核心代码简洁,只提供基础功能,大部分基于第三方中间件实现
     // 4、异步处理方式:原生支持 async/await,异步操作简洁,避免回调地狱
     /** 6、错误处理:try-catch结合中间件 
    app.use(async (ctx, next)=>{ 
    	try{ await next() }catch(e){ ctx.status=500; ctx.body={error: e.message}; } 
	}) */
// 作业5:什么是生成器对象?有什么特点?什么时候会被执行?
- 第二遍 2025.3.5 -
生成器函数生成 suspended 悬停状态需要再次调用 next() 触发恢复执行返回 // ⭐️ 默认是可迭代对象
- 第一遍 2025.1.20 -
// 作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?
- 第五遍 2025.3.5 -
React 组件代码主要是描述做什么而不是怎么做返回可被渲染的 UI例如 JSX
hook 可以返回执行任何功能如果 hook 中没有使用其他 hook不建议作为 hook
自定义hook不会共享状态 state共享的是修改 state 的逻辑
渲染时组件更新时会按序执行所有 hook
- 第四遍 2025.2.11 -
- 第三遍 2025.1.22 -
// 2021.04 05-3 白屏优化?
- 第五遍 2025.3.5 -
1DNS 缓存服务器优化CDN 优化 
2html 精简结构减少层级
3css 从右向左解析减少层级
使用 link 触发预解析而不是 @import
模块化响应式code splitting 等减少 css 文件大小
使用性能消耗少的样式开启 GPU 加速
4js Tree-shaking使用 async/defer 异步加载 // ⭐️ 尽量不要使用内联的 JS 代码
5压缩
6减少 http 请求减小请求包
- 第四遍 2025.2.11 -
- 第三遍 2025.1.20 -
- 第二遍 2025.1.19 -
 // 1、css:使用 link 加载而非 @import,可以触发预加载
 // 2、js:尽量不使用「内联代码」
 // 3、少写 - 硬件加速:DNS 解析优化,例如缓存、预加载;TCP、服务器优化
 // 4、少写 - 对于大文件的 css,利用媒体查询拆分不同用途
// 2024.05 rem是基于什么原理进行适配的?
- 第五遍 2025.3.5 -
rem 根据根元素 html  fone-size 大小
em 子元素的 font-size 是相对于父元素的 fone-size如果是当前元素的 width/height/padding/margin 则是相对于当前元素的 fone-size 大小
- 第四遍 2025.2.11 -
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
 // 1、rem 不是相对于 body - body 只是html的一个子标签,是相对于 html
// 2024.12 模拟实现promise
- 第六遍 2025.3.5 -
const S = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
function MyPromise(executor) {
    this.status = S.pending;
    this.value = null;
    this.reason = null;
    this.resolvedCalls = [];
    this.rejectedCalls = [];
    const resolve = (v) => {
        if(this.status === S.pending) {
            this.status = S.resolve;
            this.value = v;
            this.resolvedCalls.forEach(it => it(v))
        }
    }
    const reject = (r) => {
        if(this.status === S.pending){
            this.status = S.reject;
            this.reason = r;
            this.rejectedCalls.forEach(it => it(r))
        }
    }
    try{
        executor(resolve, reject);
    }catch(e) {
        reject(e)
    }
}
MyPromise.prototype.then = (onResolved, onRejected) => {
    onResolved = typeof onResolved === 'function' ? onResolved : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r }
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
            setTimeout(() => {
                try {
                    const res = onResolved(this.value);
                    resolve(res);
                }catch(e) { reject(e) }
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                try {
                    const res = onRejected(this.reason);
                    resolve(res);
                }catch(e) { reject(e) }
            }, 0)
        }
        switch(this.status) {
            case S.pending:
                this.resolvedCalls.push(resolveFun);
                this.rejectedCalls.push(rejectFun);
                break;
            case S.resolved:
                resolveFun();
                break;
            case S.rejected:
                rejectFun();
                break;
        }
    })
}
MyPromise.prototype.catch = (onRejected) => {
    return this.then(null, onRejected);
}
MyPromise.prototype.finally = (callback) => {
    return this.then(v => { callback(); return v;},
                     r => { callback(); throw r;});
}
MyPromise.resolve = function(v) {
    return new MyPromise((resolve, reject) => {
        if(v instanceof MyPromise) {
            v.then(resolve, reject);
        }
        return resolve(v)
    })
}
MyPromise.reject = function (r) {
    return new MyPromise((resolve, reject) => reject(r))
}
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) => {
        return arr.reduce((pre, cur, index) => {
            cur.then(res => {
                pre.push(res);
            }, e => reject(e))  // ⭐️ then 第二个参数可以直接简写成 reject
            if(index === arr.length - 1) {
                resolve(pre);
            }
             // 2、all 中 reduce 中的 pre 需要返回:return pre
        },[])
    })
}
MyPromise.race = function(arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => it(resolve, reject))  // 1、race 中 it 需要包裹一层 MyPromise.resolve(it).then(resolve, reject)
    })
}
- 第五遍 2025.2.11 -
- 第四遍 2025.1.20 -
- 第三遍 2025.1.19 -
 // 1、模拟 Promise 的时候,一般都是用函数
 // 2、遗漏捕获错误,需要使用 try-catch + onReject 返回的依旧是 resolve 的 promise
/** try{ const res = onReject(that.reason); resolve(res); }catch(e){ reject(e) } 
	try{ const res = onResolve(that.value); resolve(res); }catch(e){ reject(e) }*/
 // 1、then:try-catch不能写成 catch(reject),catch接收的参数是error - catch(e){ reject(e) }
 // 2、all 中 cur.then - reject 是作为 then 的第二个函数 -> }, reject)
⭐️ // 3、race:可以简写 arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
// 作业85:Lambdas是什么
- 第三遍 2025.3.5 -
匿名函数
- 第二遍 2025.2.11 -
- 第一遍 2025.1.22 -
// 作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的
- 第五遍 2025.3.5 -
原型链一个函数的原型对象是另一个函数的实例
原型链继承 Sub.prototype = new Super();
	方法属性共享 修改一个实例的引用型属性会影响到其他实例
组合继承 Sub.prototype = new Super();
	function Sub() { Super.apply(this, arguments) }
	每个实例都有独立的属性互不影响 Super 执行两次
寄生组合式继承 子级的原型对象是父级的原型对象的副本
function inherit(Sub, Super) {
    const proto = Object.create(Super.prototype);
	Object.defineProperty(proto, "constructor", {
        enumerable: false,
        value: Sub
    });
    Sub.prototype = proto;
}
- 第四遍 2025.2.11 -
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
 // 1、Object.defineProperty(proto, "constructor", {enumberable: false, value: Sub})
// 作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期
- 第七遍 2025.3.5 -
computed/watch/methods 的区别
	computed 有缓存依赖的值不变就不会触发重新计算
    watch 监听的属性变化就会触发重新计算
    methods 每次组件更新都会重新执行
父子组件挂载
	父beforeCreate-created-beforeMount-子beforeCreate-created-beforeMount-mounted-父mounted
    父beforeDestroy-子beforeDestroy-destoryed父destoryed
vue组件中的data 必须返回函数形式这样实例化组件后每个实例都有独立的data 作用域
常见生命周期
	beforeCreatecreated data/method/watch/comouted 都初始化了
    beforeMount VDOM 生成 mounted 已更新到真实DOM
    beforeUpdate VDOM 生成updated 更新到DOM
    beforeDestroy 卸载前处理一些清理destoryed
- 第六遍 2025.2.11 -
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 -
 // 1、大虐,卸载是 beforeDestroy 和 destroyed
- 第二遍 -
 // 1、computed:咦,和上次错误的逻辑一样,不是「存储的值」是否变化,是「依赖的值」是否变化
 // 2、和上次错得一样 - 因为上次答案就是错的 😅,这就纠正了,下次不许再错了:子组件在父组件执行 父 beforeMounte 后触发
- 第一遍 -
 // 1、watch:执行回调
 // 2、methods:每次页面发生变化,都会被调用
// 作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明
- 第七遍 2025.3.5 -
 // 1、先动态编译 JIT,React 使用的是 JSX 描述 UI,JSX 编译为 createElement 方法
render// ⭐️ fiber树 + hooks按序全部执行 + 副作用链表收集 + Diff
	 // 2、少写了构建新的 Fiber 树 - WIP Tree(创建 FiberRootNode DFS 遍历 根据 wip.tag 处理)
	初次渲染更新渲染时执行全部以链表形式存储在 stateMemorized 中的 hooks同时使用组件的 Dep 收集副作用得到 VDOM  // 3、为组件的 hooks 存储在 memorizedState + 副作用链表存储在 updateQueue
	Diff 增量式计算最小更新 UI
commit Diff 计算得到的更新到真实 UI后执行生命周期 + 所有副作用链表
	componentDidMount componentDidUpdate componentWillUnMount shouldComponentUpdate
     // 4、shouldComponentUpdate 在 render 之前被调用,不在 commit 阶段
count 更新时触发更新组件组件执行所有 hooks 收集副作用链表VDOM 计算得到最小更新 UI 应用到真实 DOM查看有没有副作用有的话再执行
- 第六遍 2025.2.11 -
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 -
 // 1、渲染之前,React 已经通过 JIT 将 JSX 描述的 UI 编译为 React createElement方法
 // 2、「遍历AST逐个生成 fiberNode」可以更详细得表述为「遍历执行 beginWork,根据不同的 wip.tag 进行不同标签的处理,执行到叶子节点 completeWork,生成 WIP Tree」
 // 3、遍历执行 hooks 时,如果遇到有副作用的节点,除了被副作用链表收集,react 也会在节点 Node 上标记 fiberNode.subtreeFlags
 // 4、count「触发更新」其实应该描述成「更新 count 状态对应的节点」
 // 5、count 这里应该将「如果有副作用」改写为「要注意是否有 useEffect 的作用:根据依赖数组是否变化确定是否要更新」
// 2025-02 KOA2 中间件机制实现、路由处理、错误处理、模版引擎、源码
- 第一遍 2025.3.5 -
 // 1、KOA2 是基于 NodeJS 的 HTTP 中间件框架
中间件机制实现洋葱模型按序进入中间件最中间层再逐一执行出来
// ⭐️ 借用 async/await 避免回调地狱
function middle = (next) => {console.log('1'); next(); console.log('2')}  // 2、中间件写法:参数是 ctx + 可选的 next 执行函数 且应该是 async 函数,async (ctx, next)=>{ await next() }
function func(arrs) {  // 3、compose 是返回一个Promise来链式调用,方便传入中间件数组来执行 return (ctx, next) => {}
    let index = -1;
    return dispatch(0);
    
    function dispatch(i) {
        if(i<index) return  // 4、如果重复执行,直接返回 Promise.reject(new Error('dup'))
        index = i;
        const fn = arrs[i];
        if(i === arrs.length) {
            return  // 5、 执行到最后一个中间件数组时,fn 指向 next
        }
         // 6、少写了 fn 不存在时,return Promise.resolve()
        try{
            const nextF = () => dispatch(i+1);
            fn(nextF);  // 7、返回一个 Promise:return Promise.resolve(fn(ctx, next))
        }catch(e) {
            console.log(e)  // 8、返回一个 reject 的 Promise:return Promise.reject(e)
        }
    }
}
路由处理
	 // 1、KOA2 本身没有内置路由功能,需要使用 koa-router:router.get('/', async (ctx)=>{}) + 路由参数 ctx.params + 链接参数 ctx.query
	app.use('/route', middleware)
错误处理
	?❌ /** 1、全局错误处理中间件 
	app.use(async (ctx, next) => {
		try { await next() } catch(e) {  
			ctx.status = 500;
			ctx.body = { error: e.message }
		}
	})*/
模版引擎
	ejs模版引擎 - <% if %> 适用于复杂场景  // 1、性能弱,需要动态解析、执行 js 
    art-template模版引擎  // 2、,性能好,使用预编译技术,可以直接执行 js
源码
	KOA2的源码很简单context.js/request.js/response.js都是get/set设置访问将http包封装成use
     /** 1、封装 http 包,通过 get 和 set 简化 API ; 引入中间件机制/洋葱模型 
	const http = require('http');
	class KOA2 {
		constructor() { this.middlewares = [] } 初始化中间件数组
		listen(...args) { 创建 http 服务器
			const server = http.createServer(this.callback());  传入回调
			return server.listen(...args) 启动服务
		}
		use(fn) { this.middlewares.push(fn); return this; } 注册中间件
		createContext(req, res) { 创建上下文
			const ctx = Object.create(this.context);
			ctx.request = Object.create(this.request);
			ctx.response = Object.create(this.response);
			ctx.req = ctx.request.req = req; 将 req res 挂载到 ctx 上
			ctx.res = ctx.response.res = res;
			return ctx
		}
		handleRequest(ctx, fnMiddleware) { 
			return fnMiddleware(ctx) 调用组合后的中间件函数
				.then(() => respond(ctx)) 中间件处理完后调用 reponse 响应
				.catch(e => ctx.onerror(e)) 
		}
		callback() { 生成处理请求的回调函数
			const fn = compose(this.middlewares);
			return (req, res) => {
				const ctx = this.createContext(req, res);
				return this.handleRequest(ctx, fn);
			}
		}
	}
	function respond(ctx) { 响应处理函数
		const res = ctx.res;
		let body = ctx.body;
		const code = ctx.status;
		res.statusCode = code;
		res.end(body)
	}
	function compose(arrs) {}
    */
// 作业53:胜利者一无所获 说的是什么
- 第五遍 2025.3.5 -
海明威 战国春梦// ⭐️ 战地春梦
质疑赢家通吃的意义反战战争获胜方失去了善良的品质
鼓励人们要追寻内在的品质 // ⭐️ 是对胜利本质的质疑
- 第四遍 2025.2.10 -
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
// 作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口)
- 第五遍 2025.3.5 -
<canvas id="drawing" width=200 height=200></canvas>
const drawing = document.getElementById('drawing');
2d:坐标原点 - 左上角唯一形状 - 矩形
if(drawing.getContext) {
    const ctx = drawing.getContext('2d');
    ctx.beginPath();
    ctx.strokeStyle/fillStyle = 'red';
    ctx.strokeRect/fillRect = (x,y, width, height);
    ctx.fillText/drawImage();
    ctx.lineTo/moveTo();
    ctx.stroke();
}
3d:左下角
if(drawing.getContext) {
    const ctx = drawing.getContext('webgl');
    ctx.viewport(drawing.width/2, 0, drawing.width/2, drawing.height/2)
}
- 第四遍 2025.2.10 -
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
 // 1、3D: context.viewport
 // 2、2D: context.beginPath() - 创建路径
// 作业71:React为什么要合成事件?和原生事件有什么不同?
- 第六遍 2025.3.5 -
React 的合成事件  原生事件的封装将不同浏览器的差异抹平 // ⭐️ 跨浏览器兼容的事件系统
	- 利用事件委托在组件挂载时冒泡到绑定到 document 节省了绑定访问 dom 的性能开销
事件对象React 合成事件提供统一属性原生的各个浏览器有差异
事件绑定方式React 的事件处理函数addEventListener/onlick
事件执行时机合成事件在冒泡时执行原生先捕获后冒泡一般比合成事件早执行
- 第五遍 2025.2.10 -
⭐️ // 更好的说法:跨浏览器兼容 + 在挂载时,会在节点绑定事件处理函数
- 第四遍 2025.1.22 -
- 第三遍 2025.1.19 -
⭐️ // + 是对浏览器原生事件的封装
 // 1、合成事件的绑定,不是在「构建时」,事件委托发生在「组件挂载时」
 // 2、差异少写了:事件绑定方式 合成事件-JSX中属性指定事件处理函数;原生事件 标签的onclick或者addEventListener
// 2024-12 Nestjs 核心特性、基本概念、生命周期、获取请求和响应、不同组件的实现和执行顺序
- 第一遍 2025.3.5 -
核心特性后端框架结构化管理服务注入依赖模式  // 1、核心特性:结构化管理服务 - 可以更改描述为“模块化架构” ;依赖注入 DI;多种协议 HTTP Websocket
基本概念
	provider 提供服务 app.service.ts @Injectable
	控制器声明路由 app.controller.ts @Controller
	模块化管理 app.module.ts @Module// ⭐️ 根模块/子模块 ❌ // 2、少写了 可以配置将服务注入控制器
	总入口文件 main.ts 声明配置 // ⭐️ 应用程序入口
	装饰器声明一个函数/中间件返回一个值或者 next()  // 3、描述不对 装饰器:一个表达式,参数是字符串(目标、名称、属性名)、provider,返回一个函数
     // 4、少写了 令牌:类是代码的具体实现,令牌是依赖注入的 标识依赖项的唯一标识,指向相应实例
生命周期
	初始化时读取 main.ts 和结构关系  // 1、描述不清楚 应用初始化:解析模块、控制器、provider等配置信息,收集、整理组件间的依赖关系注入容器(容器是个抽象概念,用于管理组件、依赖,启动时创建)
    根据 *.module.ts 引入具体的 provider  // 2、依赖解析和实例化:根据配置,深度解析各组件依赖关系,根据关系依次实例化 provider、实例化控制器
     // 3、应用启动:所有组件实例化后,应用监听指定端口,正式提供服务
    中间件日志等)、守卫进路由系统前权限校验)、拦截器请求处理)、管道路由处理函数的处理)、拦截器响应头处理// ⭐️ 这里描述的是 请求处理:运行过程中,接收请求,按照如上顺序处理请求,返回响应
    卸载时销毁所有 provider // ⭐️ 这里描述的是 应用关闭:执行关闭操作,销毁各组件实例
获取请求和响应
	@Body @Query 开箱即用
    @Res @Req Express 提供的基础能力包含了头
    @Params 动态路由 // ⭐️ @Param
     // 1、少写了响应方式:2种 对象/数组-自动序列化为JSON;基础类型-直接返回,不序列化
不同组件的实现和执行顺序
	中间件 middleware.ts 全局 -  main.ts 引入 app.use(middleware)/某模块 -  *.module.ts 导出 export AppModule extends NextMiddleware {} 
	 /** 1、中间件
	功能-全局请求的预处理,日志记录、请求验证、请求解析、跨域处理;
	时机-在请求进入NestJS路由系统之前;
	实现-①函数中间件logger(req,res,next)②类...implements NestMiddleware{ use(req,res,next)}
	配置-
		①全局 - main.ts引入app.use(logger)
		②模块 - 在 *.module.ts 导出类*/
	守卫 在控制器 @UseGards(Auth) 全局 -  main.ts 引入 app.use()/某控制器 -  *.controler.ts 中的路由前使用 @UseGards(Auth)
	 /** 2、守卫
	功能-权限验证,决定请求是否可以继续处理
	实现-类...implements CanActivate{ canActivate(context){}}返回布尔值/可以解析为布尔的Promise
	配置-
		①全局 - main.ts 引入 app.useGlobalFilters(类/实例) 或者 
		在app.module.ts配置 @Module 的providers 属性声明[{ provide: APP_FILER, useClass: 类}]
		②控制器 - 在 *.controler.ts 的路由方法 @UseGards(类实例)*/
	拦截器 全局某控制器 /** 3、拦截器
	功能-添加额外的请求头、修改请求参数等
	时机-请求到达控制器处理程序之前
	实现-类...implements NestInterceptor{ intercept(ctx, next){} }
	配置-
		①全局 - main.ts 引入 app.useInterceptors(类/实例) 或者 
		在app.module.ts配置 @Module 的providers 属性声明[{ provide: APP_FILER, useClass: 类}]
		②控制器 - 在 *.controler.ts 的路由方法 @UseInterceptors(类实例)*/
    管道 某控制器参数 /** 4、管道
    功能-对控制器处理程序的输入参数的转换和验证,验证路由参数、查询字符串参数、请求体格式等
    实现-①内置9个管道(6个Parse*管道) ②自定义管道(返回的值完全覆盖原参数) ...implements PipeTransform{ transform(value, metadata){} }
    配置-
    	①全局 - main.ts 引入 app.useGlobalPipes(类/实例) 或者 
    	在app.module.ts配置 @Module 的providers 属性声明[{ provide: APP_FILER, useClass: 类}]
    	②控制器 - 在 *.controler.ts 的路由方法 @UsePipes(类实例)
    	③路由处理函数 - 在参数处理中使用管道 @Query/Param('property', 多个类/实例) @Body(类/实例)
    */
// 2021.04 12 垃圾回收机制
- 第三遍 2025.3.5 -
不需要的活动对象清除方式是指针向下移有新的活动对象进栈时直接覆盖内存
代际假说分成老生代新生代先标记清理移动集中避免内存碎片
新生代-副垃圾回收机制空间小清理频繁将区域划成2部分空闲区对象区每次有新对象时存入对象区对象区快满时触发清理将对象区还有效的数据存储入空闲区复制完毕后两个区域对调  清理2次都还存续的对象升级为老生代不会产生内存碎片 // ⭐️ 按序复制
老生代-主垃圾回收机制空间大标记-清理算法标记后清理产生碎片-移动清除碎片增量式清理不占阻塞主线程
- 第二遍 2025.2.27 -
 // 1、少写了 栈的清理方式 - 通过指针移动,不需要的数据的内存会被直接覆盖
 // 2、新生代 清理过程:先标记,再有序地复制到空闲区
 // 3、老生代 没有被引用就被标记为垃圾数据
- 第一遍 2025.2.24 -
 // 1、少写了:v8 引擎 基于代际假说
 // 2、划分区域名错误:新生代 - 副垃圾回收器,划分为 对象区域、空闲区域;清理过程错误:标记 - 将存活的对象有序地复制到空闲区 - 复制完后翻转两个区域
 // 3、少写了算法:标记-清理,产生大量不连续的内存碎片
// 2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程
- 第三遍 2025.3.3 -
原理 Vue 中数据更新后dom 更新是异步更新的nextTick 提供了 DOM 更新后再处理的时机
Vue2 流程1收集nextTick回调 2根据环境使用不同的机制实现异步 微任务宏任务 3执行触发
const callbacks = [];
let pending = false;
function flushCalls() {
    pending = true;
    const copies = callbacks.slice();
    copies.forEach(it => it());
    callbacks.length = 0;
    pending = false;
}
let timeFunc;
if(typeof Promise !== 'undefined') {
    timeFunc = Promise.resolve().then(flushCalls);
     // 1、应该包裹成回调函数,否则赋值时就直接执行了 timeFunc = () => Promise.resolve().then(flushCalls)
}else if(typeof MutationObserver !== 'undefined'){
    const ob = new MutationObserver(flushCalls);
    const dom = document.createText('1');  // 2、文本节点创建函数错误 createTextNode
    ob.observe(dom, { dataSource: true });  // 3、配置的属性名称错误 characterData
    timeFunc = () => { dom.nodeValue = '2' }  // 4、文本节点修改值是 dom.data='2'
}else if(typeof setImmediate !== 'undefined'){
    timeFunc = () => setImmediate(flushCalls)
} else {
    timeFunc = () => setTimeout(flushCalls, 0)
}

function nextTick(cb, ctx) {  // 5、nextTick 有第二个参数 ctx -> 指定 cb 的上下文
    if(!cb) { return Promise.resolve() }
    callbacks.push(cb);
    if(!pending) { try{ timeFunc() } catch(e) { return Promise.reject(e)}}
	 /** 6、主要错误在:cb执行的上下文,其他逻辑上没啥问题,函数内逻辑 
		let _resolve;
		callbacks.push(() => {
			if(cb) { 
				try{cb.call(ctx)} }catch(e) {console.log(e)
			} else if(_resolve) { _resolve(ctx) }
		})
		if(!pending) { timeFunc() }
		if(!cb && typeof Promise !== 'undefined') { 
			return new Promise(resolve => _resolve=resolve) 
		}
	*/
}
 // 7、使用示例 new Vue({data() {return {value:1}}, mounted() {this.$nextTick(()=>{})}})
Vue3 流程1收集 2queueMicrotask 实现 3执行触发
const queue = [];
let isFlushing = false;
function flushCalls() {
    isFlushing = true;
    let job;
    while((job=queue.shift())) {
        job();
    }
    isFlushing = false;
}
function timeFunc() {
    queueMicrotask(flushCalls);
}
function nextTick(cb) {
    return new Promise((resolve, reject) => {
        if(!cb) {
            reject()
        }
        queue.push(cb);  /** 8、传入回调函数需要把当前的 Promise 状态切换为 resolved
        queue.push(() => {
        	if(cb) { try{cb()}catch(e){console.err(e)} }
        	resolve();
        })
        */
        if(!isFlushing){
            try{
                timeFunc();
            }catch(e) {
                return reject(e)
            }
        }
    })
}
 /** 9、使用示例 
import {createApp, nextTick} from 'vue';
const app = createApp({ data(){ return {value:1} }, mounted() {nextTick(() => {})} })
app.mount('#app')
*/
- 第二遍 2025.2.27 -
 // 1、vue2 的 NextTick 根据回调函数判断是否返回 Promise;vue3 不论回调函数,都会返回 Promise
vue2
 // 2、执行所有回调函数 flushJobs 中少写了:清空原数组,避免重复执行回调 callbacks.length = 0;
 // 3、根据环境判断,有 Promise 时 Promise.resolve().then 少写了执行()
 // 4、根据环境判断,有 MutationObserver 时,文本节点改用 createTextNode:const dom = document.createTextNode('1') + characterData 属性配置监听文本节点的变化 + 赋值时使用 dom.textContent = '2'
 /** 5、收集回调函数时,需要处理报错的情况 try catch
    let _resolve;
    callbacks.push(() => {
    	if(fn) { 
    		try{ fn.call(ctx) } catch(e) { console.error(e) } 
		} else if(_resolve) {  _resolve(ctx) }
    })*/
 /** 6、 少写了:没有传递回调函数,支持 Promise 的调用
    if(!fn && typeof Promise !== 'undefined'){
    	return new Promise((resolve) => { _resolve = resolve })
    }*/
vue3
 /** 7、callbacks 需要清空,避免回调函数被重复执行 - 换一种写法
    while((job = callbacks.shift())) { try{ job() }catch(e){ console.error(e) }}*/
 // 8、vue3 中,在 nextTick 不论有没有传递回调,都会返回一个 pending 状态的 Promise
- 第一遍 2025.2.24 -
 // 1、Vue 的数据更新是异步执行,修改数据后,Vue 不会立即更新 DOM,而是放到队列中等到下一个事件循环时再批量更新 - 需要 nextTick 来确保在 DOM 更新后再执行
 // 2、Vue3:和Vue差异是,使用 queueMicrotask 统一处理微任务
// 2021.05 27 HTTP/3 改进的点
- 第三遍 2025.3.3 -
HTTP/3.0  HTTP 下的 TCP 改为 QUIC+TLS+UDP
	UDP无连接协议不需要和TCP一样三次握手四次挥手节省时间提高效率
     QUIC 中实现丢包校验但不会阻塞 // ⭐️ 完全解决了队头阻塞
HTTP/2.0  TCP 传输层还以为丢包重传机制响应不回来会导致后续的请求即使响应包已经返回存储在缓冲区上层协议也无法从中获取
- 第二遍 2025.2.27 -
 /** 1、详细比较对比
HTTP/3(应用层)			 HTTP/1.1 或 HTTP/2.0(应用层)  		 HTTP(应用层)
|						|									|
QUIC(传输层,基于 UDP)   									   TLS(安全层)
|						|									|
UDP(传输层)			 TCP(传输层)						  TCP(传输层)
|						|									|
IP(网络层)				 IP(网络层)						  IP(网络层)
|						|									|
数据链路层				 数据链路层							  数据链路层
|						|									|
物理层					  物理层								 物理层
**/
- 第一遍 2025.2.24 -
 // 1、少写了 HTTP/2.0 存在的问题:HTTP/2.0 并没有完全解决队头阻塞,传输层的 TCP 有丢包重传机制 - TCP 为了保证可靠的传输,丢包必须等待重新传输确认,其他的包即使收到了也只能放在缓冲区,上层应用拿不出来,被丢的包不回来,大家都取不出来
 // 2、少写了:HTTP/3.0 UDP 无连接,HTTP/2.0 TCP 三次握手、四次挥手
// 2021.07 babel 作用+原理+补丁
- 第三遍 2025.3.3 -
作用  ES6 代码转换为 ES5 代码方便使用者直接使用 ES6 开发
原理 
    @babel/parse 先将 ES6 代码转为 AST // ⭐️ 词法分析语法分析
	@babel/tranverse 将 ES6 AST 转为 ES5 AST
	@babel/generate 使用 ES5 AST 生成 js 代码 // ⭐️ @babel/generator
补丁
	babel 只能处理基础 ES6 例如箭头函数其他功能需要补丁来处理
    GeneratorIteratorObject.assignProxySymbol // ⭐️  Set Map Reflect Promise
- 第二遍 2025.2.27 -
// ⭐️ Set Map Proxy Reflect Object.assign - @babel/polyfill
- 第一遍 2025.2.24 -
 // 1、原理:少写了解析过程:解析Parsing[词法分析+语法分析得到AST]-转换Transformation[遍历AST转换 ES6的AST转换成ES5的AST]-生成Code Generation[根据转换后的AST转换成ES5 js代码]
 // 2、不能转换的ES6语法:Iterator Generator Set Map Proxy Reflect Symbol 等全局对象 + Object.assign 全局对象上的方法;@babel/polyfill
// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第六遍 2025.3.2 -
1.father{ display:flex; justify-content:center; align-iterms:center; }
2.father{ display:table-cell; vertial-align:center; text-align:center; }
.son{ display:inline-block; }
3.son{ position:absolute; top:0; roght:0; bottom:0; left:0; margin:auto; }
4.son{ position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); }
- 第五遍 2025.2.27 -
- 第四遍 2025.2.24 -
 // 1、table 少写了 .son{ display: inline-block }
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
- 第一遍 2025.1.30 -
 // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中
// 作业66:React如何处理错误
- 第十一遍 2025.3.2 -
react 提供了两个函数用于捕获 render  commit 阶段的错误避免错误溢出应用影响 UI
static getDerivedStateFromError  componentDidCatch 
class Boundaries extends React.Component{
	constructor(props){
        // ❌ 1、少写了 super(props)
        this.state = {hasErr: false}
    }
    static getDerivedStateFromError() {
        return {hasErr: true}
    }
    componentDidCatch(e, info) {
        console.log(e, info)
    }
    render() {
        if(this.state.hasErr) {
            return <div> there are errs </div>
        } else {
			return this.props.children;
        }
    }
}
- 第十遍 2025.2.27 -
- 第九遍 2025.2.24 -
 // 1、静态函数名称错了 getDerivedStateFromError 
- 第八遍 2025.2.10 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 作业99:渲染合成层定义、触发方式、优点、缺点
- 第三遍 2025.3.2 -
将页面划分成多层每层都有独立的布局机制 // ⭐️ 独立的绘制和处理流程
触发13D canvas 视频 2will-change opacity tansform
优点1避免不次要的回流重绘 2合成层可以开启 GPU 加速不会因为 CPU 造成卡顿
缺点分层越多占用的内存越大分层后合成消耗一定性能
- 第二遍 2025.2.27 -
// ⭐️ 每个层都有自己独立的绘制和处理流程
 // 1、少写了:优点 实现流程流畅的动画效果,合成层可以在 GPU 上进行加速
- 第一遍 2025.2.23 -
 // 1、少写了:渲染时,为了提高渲染效率和性能,将页面中不同元素划分成多个层
 // 2、少写了:触发 3D 变化、视频/canvas
 // 3、少写了:优点 减少不必要的回流重绘
// 2021.06 flex布局
- 第三遍 2025.3.2 -
1布局结构操作简便 flex-direction: columns/rows/rows-reverse/columns-reverse // ❌ 1、row/column 没有 s
2flex:flex-grow flex-shrink flex-basis 前面的整数值是基于 flex-basis 的倍数
主轴默认拉伸不缩小 // ❌ 2、主轴默认不拉伸,但默认缩小
交叉轴默认拉伸 align-iterms: stretch
justify-content space-between[a--b--c] space-around[-a--b--c-]
3align-self 可以设置子项目的布局属性可以覆盖 align-iterms
4flex 会导致 float vertical-align 失效 // ⭐️  clear
- 第二遍 2025.2.27 -
// ⭐️ flex-grow 正整数以 flex-basis 为基础
// ⭐️ align-self允许单个项目与其他不一样的对齐方式
 // 1、少写了 flex 导致 float clear vertical-align 失效
- 第一遍 2025.2.23 -
 /** -	space-between 元素排列后,再分配 [a--b--c]
	-	space-around 每个元素左右空间都相等 [-a--b--c-]*/
// 2021.07 12-1 内存泄漏
- 第三遍 2025.3.2 -
不再被使用的活动对象没有被清理造成内存被占用
1避免使用全部变量少使用闭包
2移除定时器 clearTimeout clearInterval
3取消监听 removeEventListener/.off
4弱引用 WeakMap WeakSet Map.delete(key) Set.delete(value)
- 第二遍 2025.2.27 -
// ⭐️ 内存 Chrome Memory 录制查看
- 第一遍 2025.2.23 -
// 作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景
- 第四遍 2025.3.2 -
WeakMap/WeakSet 键值是对象弱引用不阻止垃圾回收程序清理空对象
MapSet 键值是任意值强引用map set 存在时都不会被清理对象
私有变量DOM 节点-被删除就会被清理
- 第三遍 2025.2.27 -
- 第二遍 2025.2.23 -
 // 1、Map 和 Set 对存储的对象持有「强引用」,只要它们存在,对象就不会被回收 - 即使在其他地方没有对该对象的引用,Map 或 Set 内部的引用会阻止垃圾回收器将其回收
 // 2、而 WeakMap 和 WeakSet 对存储的对象持有「弱引用」,不会阻止对象被垃圾回收
- 第一遍 2024.12.25 -
// 2019.07 显示省略号
- 第六遍 2025.3.2 -
.line { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.lines { 
    overflow:hidden; 
    display:-webkit-box; 
    -webkit-box-orient:vertical;
    -webkit-line-clamp:3;
    padding: 10px;
    line-height: 30px;
    height: 30*3-10px
}
- 第五遍 2025.2.27 -
 // 1、属性值错误 -webkit-box
- 第四遍 2025.2.23 -
 // 1、属性名错误 -webkit-box-orient: vertical;
- 第三遍 2025.2.2 -
// 2023.07 clickhouse、mysql、mongodb异同
- 第八遍 2025.3.2 -
mysql 关系式数据库存储结构型数据读写性能好高并发时存在一致性问题支持事务操作
mongodb 文档型数据库存储半结构型非结构型数据库 JSON XML HTML读的性能好写的性能差分布式操作时可能存在一致性问题
clickhouse 列式数据库读写性能好不支持事务操作支持异步分布式操作适用于高吞吐低延迟的场景
- 第七遍 2025.2.27 -
- 第六遍 2025.2.23 -
 // 1、少写了:clickhouse 支持分布式操作
- 第五遍 2025.2.18 -
 // 1、mysql 少写了:支持事务操作
- 第四遍 2025.2.16 -
 // 1、mysql 少写了:高并发时出现数据一致性问题
 // 2、mongodb 少写了:在分布式场景时出现数据一致性问题
 // 3、clickhouse 少写了:列式数据库 + 不支持事务 + 适合高吞吐、低延迟的场景
- 第三遍 2025.1.30 -
- 第二遍 2025.1.25 -
- 第一遍 2025.1.22 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第八遍 2025.3.2 -
redux 将全局应用的状态 映射在 store 对象模型b-tree),只有 action 可以通过 dispatch  store 可以修改 state
reducer 利用 旧state  action 计算得到新的 state
RTK = redux toolkit
import { createStore } from 'redux';
const reducer = (oldV={v:1}, action) => {if(action.type){ return {v: oldV.v + 1} }}
const store = createStore(reducer);
store.dispatch({type: true});
store.subscribe(() => {console.log(store.getState())})
- 第七遍 2025.2.27 -
- 第六遍 2025.2.23 -
 // 1、 store 的函数名称错了:getState
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、注意 store 是对象树:Redux 将全局状态保存在 store 对象树中
⭐️ // reducer:返回新的 state 而不是改变;通过 subscribe 更新 UI
 // 2、reducer 参数顺序是 oldState,action
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 作业70:Vue 和 React 的 Diff 算法比较
- 第十一遍 2025.3.2 -
Vue 1、跳过静态节点 2、双端指针算法 - 同类型+同key 索引 - 列表组件 key 
- 精确到组件级别的更新
React 分层比较 同类型优先比较 不同类型直接重新渲染;同类型修改属性;同类型、同key 移动、复用 // ⭐️  DFS
- 精确到节点属性级别的更新 // ⭐️ 适合大型动态复杂场景
- 第十遍 2025.2.27 -
❌ // 1、Vue 是组件级别的更新 + React 是节点属性级别的更新
- 第九遍 2025.2.23 -
❌ // 1、Vue 少写:Diff 跳过静态节点
❌ // 2、React 少写了:分层比较 + 同类型优先比较 + DFS 深度优先遍历子节点,逐个比较
- 第八遍 2025.2.18 - 
- 第七遍 2025.2.16 -
❌ // 1、Vue 少写了:双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
- 第三遍 2025.1.18 -
- 第二遍 2025.1.13 -
// 2021.07 柯里化函数实现
- 第三遍 2025.3.2 -
function curry(fn, ...args1) {
    return (...args2) => { // ❌ 1、不是直接返回回调,而是根据参数数量判断返回
        const args = [...args1, ...args2];
        if(fn.length > args.length) {
            return curry(fn, ...args) // ❌ 2、参数不够时,返回一个包裹的函数
        } else {
            fn(args) // ❌ 3、执行应该是 fn(...args)
        }
    }
}
/**
function curry(fn, ...args1) {
	if(args1.length >= fn.length) {
		return fn(...args1)
	} else {
		return (...args2) => curry(fn, ...args1, ...args2)
	}
}
*/
- 第二遍 2025.2.8 -
// 2019.07 数组去重(算法整理
- 第四遍 2025.3.2 -
1[...new Set(arr)] Array.of(...new Set(arr)) Array.from(new Set(arr))
2arr.filter((it, index) => index === arr.indexOf(it));
3
function alone(arr){
    return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? alone(cur) : cur), [])
}/** ❌ 1、写成拍平函数了
function alone(arr){
	const hash = {};
	arr.filter(it => hash[it] ? false : hash[it]=true )
}
*/
- 第三遍 2025.2.8 -
- 第一遍 -
// 2021.06 跨页面通信
- 第四遍 2025.3.2 -
同源页面 1 const b1 = new BroadCastChannel('page');在两页页面创建同参数的广播 + 一个页面b1.postmessage('xxx') + 另一个页面 b1.onmessage=()=>{}
2localStorage 只能处理非同源页面的响应 localStorage.setItem(key, value) localStorage.getItem(key) // ❌ 1、localStorage 获取响应的方式是在 window 上调用回调函数 window.onstorage=()=>{}
不同源页面 3postMessage API 打开一个新页面 const win = window.open('xxx'); win.postMessage('xxx') 在新页面可以监听到 window.onmessage=()=>{}
4iframe  origin 设置为 同源域名使用1和2
- 第三遍 2025.2.8 -
 // 1、构造函数的名称记错:BroadCastChannel
// 2020.03 模块化
- 第四遍 2025.3.2 -
CommonJS 同步加载模块适合服务端导出值的拷贝运行时输出 require+exports
AMD 异步并行加载多个模块依赖前置无法按序执行先加载完先执行define(['./a.js','./b.js'], (a,b) => {a.c()})
CMD 异步并行加载多个模块依赖就近按序执行define((require, exports, modal) => {// ⭐️ module
    const a = require('a.js');
    a.c();
})
ES6 模块化 异步服务端和客户端的通用解决方案导出值的引用编译时输出 import default export from as // ⭐️ 静态解析时生成
- 第三遍 2025.2.8 -
 // 1、少写了 AMD - 加载模块后直接执行,无法保证执行顺序;CMD - 加载后,直调调用才按序执行
// 2022.08 包管理工具
- 第六遍 2025.3.2 -
npm 将依赖包打平存储在 node_modulesnpm>5后才有lockfilepackage-lock.json/.npmrc
yarn v1/classic 将包打平存储在 node_modulesyarn.lock/.yarnrc // ⭐️ 解决早期 npm 问题,并行加载
pnpm 依赖包在 node_modules 中继续结构化存储但是内容寻址存储只物理存储一次减少依赖空间,pnpm-lock.yml/.npmrc
yarn v2/berry 不再使用 node_modules使用 .pnp.cjs+.yarn/cache 存储 zip 格式即插即用yarn.lock/.yarnrc.yml // ⭐️ 依赖查找表
- 第五遍 2025.2.14 -
- 第四遍 2025.1.24 -
- 第三遍 2025.1.19 -
 // 1、yarn v2/Berry:即插即用,且lock文件和配置文件分别为 yarn.lock + .yarnrc.yml
 // 1、pnpm:内容寻址存储「不是pnpm.yml,而是 pnpm-lock.yml」
// 2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?
- 第四遍 2025.3.1 -
基础类型浅拷贝和深拷贝都拷贝值新值的改变不影响老值
引用类型浅拷贝直接拷贝新值的地址修改新值会影响老值深拷贝拷贝的是地址指向的值新值不会影响老值
浅拷贝 arr.slice()Array.from(arr)[...arr]arr.concat() {...obj}Object.assign(obj)
// ❌ 1、Object.assign(obj) 其实是直接返回了 obj 原对象,就是原对象,不算是浅复制,应该是 Object.assign({}, obj)
深拷贝 JSON.parse(JSON.stringify(obj))
function isObj(t) {
    const type = typeof t;
    return t !== null && (type === 'function' || type === 'object')
}
function cloneNormal(t, type) {
    switch(type) {
        case '[object Date]':
            return new Date(t.getTime())
            break;
        case '[object RegExp]':
            return new RegExp(t.source, t.flags)
            break;
        case '[object Symbol]':
            return Symbol.for(t.description);
            break;
        default:
            const ctor = t.constructor;
            return new ctor(t)
            break;
    }
}
function deepClone(target, m = new WeakMap()) {
    if(!isObj(target)) {
        return target;
    }
    const detailType = Object.prototype.toString.call(target);
    const specialtype = ['Map', 'Object', 'Set', 'Array'].map(it => `[object ${it}]`);
    if(!specialtype.includes(detailType)) {
        return cloneNormal(target, detailType);
    }
    let res; // ❌ 2、const ctor = target.constructor; let res = new ctor();
    if(m.has(target)){
        return target
    }
    m.set(target, res);
    if(detailType === '[object Map]') {
        target.forEach(([key, value]) => { // ❌ 1、Map 的 forEach 的回调 (value, key, map)
            res.set(key, deepClone(value, m))
        })
    } else if(detailType === '[object Set]'){
        target.forEach(value => {
            res.add(deepClone(value, m))
        })
    } else if(Array.isArray(target)) {
        target.forEach((it, index) => {
            res[index] = deepClone(it, m)
        })
    } else {
        for(let i in target) {
            res[i] = deepClone(target[it], m)
        }
    }
    return res;
}
- 第三遍 2025.2.8 -
 // 1、属性名写错了:正则类型的 t.flags
 // 2、 方法错了:object 不是可迭代对象,不能使用 for...of,这里应该改为 for...in
// 2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击
- 第四遍 2025.3.1 -
攻击的原本原因是因为浏览器为同源协议开了两个后门一个是可以引入外部资源一个是通过CORS访问第三方请求
XSS 跨域脚本攻击通过在前端页面注入 js 代码修改后端服务
解决方案:①服务端对用户输入存储的数据进行过滤转义 // ⭐️ 纯前端渲染
cookie 设置 HttpOnly 仅限 http 访问js引擎禁止js访问例如 document.cookie
CSP Content-security-policy 内容安全策略可以通过响应头/meta标签设置'src-script' 'self' 允许执行当前域名的脚本 // ⭐️  设置引入外部资源的可信度
在渲染期禁止 DOM 执行 js 代码
CSRF 跨站请求伪造冒用用户信息获取服务端信任修改服务端信息
解决方案:① cookie 设置 Samesite只有同源请求能够获取 cookie
校验请求头中的 referer 请求域名
验证码
 cookie 中校验第三方无法模拟的 csrf token
- 第三遍 2025.2.8 -
⭐️ // CSP - 从客户端角度讲的,哪些域名的资源可以被信任;CORS - 从服务端角度讲的,哪些域名可以访问这个资源
 // 1、少写了:XSS的解决方案中,在「渲染时」禁止标签执行 js 代码
// 2021.05 29 WebSocket
- 第四遍 2025.3.1 -
全双工-双向实时通讯HTTP/1.1 由于请求应答模型无法实现实时通讯
1不再使用 http 协议头 ws:80 wss:443
2没有同源协议限制采用二进制帧 // ⭐️ 语法、语义完全不兼容
3不再采用 协议+端口 发现服务采用 URI
4基于 HTTP GET 传输 Connection: update返回 Update: websocket 切换协议
- 第三遍 2025.2.8 -
 // 1、少写了:修改了协议名 ws:80 wss:443
// 作业76:显卡的作用?
- 第五遍 2025.3.1 -
合成图像保存到后缓存区 - 生成后和前缓存区换 // ⭐️ 后缓冲区
- 第四遍 2025.2.15 -
- 第三遍 2025.2.10 -
- 第二遍 2025.2.2 -
// 作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次?
- 第六遍 2025.3.1 -
在合成事件生命周期函数中的 setState 有批量执行机制是异步的在事件处理函数执行的过程中会将 setState 存储到队列中在事件处理函数执行完之后将队列中的 setState 统一处理后触发一次渲染
// ⭐️ 批量更新机制
在原生事件中setState 调用一次就执行一次触发一次渲染
- 第五遍 2025.2.15 -
- 第四遍 2025.2.10 -
- 第三遍 2025.2.2 -
 // 1、除了合成事件,还有「生命周期函数」中都是异步
// 作业61:常见的数据结构有哪些
- 第六遍 2025.3.1 -
按照逻辑结构划分
结构型 队列  数组 链表 // ❌ 1、线性结构
非结构型   // ❌ 2、非线性结构
- 第五遍 2025.2.15 -
- 第四遍 2025.2.9 -
- 第三遍 2025.2.2 -
 // 1、少写了一个类型:数组
// 作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例
- 第七遍 2025.3.1 -
高阶函数 输入或者输出都是函数的函数 map
高阶组件 输入是组件的组件
副作用除了函数的返回值还会修改全局变量传入参数IO操作等影响外部
const func = (My) => {
    return class extends React.component{
        constructor(props) {
            super(props)
        }
        render() {
            return <My {...this.props} />
        }
    }
}
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
- 第四遍 2025.2.2 -
⭐️ // 副作用,少写了:除了返回值
render() {
    return Child
     // 1、<Child {...this.props} />
}
// 作业55:flex 常见缩写
- 第七遍 2025.3.1 -
flex: none; 0 0 auto
flex: 1;  1 1 0
flex: initial; 0 1 auto
flex: auto; 1 1 auto
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
- 第四遍 2025.2.2 -
 // 1、少写了一个 flex: initial; // 0 1 auto
// 作业74:起舞弄清影 是那首诗?谁写的?下一句是什么?
- 第五遍 2025.3.1 -
苏轼 水调歌头 明月几时有
明月几时有把酒问青天不知天上宫阙今夕是何年
我欲乘风归去又恐琼楼玉宇高处不胜寒
转朱阁低绮户照无眠
起舞弄清影何似在人间// ⭐️ 顺序反了,先起舞,再转
人有悲欢离合月有阴晴圆缺此事古难全
不应有恨此时长向别时圆// ⭐️ 顺序反了,先不应有很,何事长向别时圆?再人有
千里共婵娟// ⭐️ 少写了 但愿人长久,千里共婵娟
- 第四遍 2025.2.15 -
⭐️ // 不应有恨,何事长向别时圆。人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟
- 第三遍 2025.2.9 -
⭐️ // 顺序反了 - 起舞弄清影,何似在人间。转朱阁,低绮户,照无眠。
- 第二遍 2025.2.2 -
// 作业25:简单写下 请求创建过程
- 第七遍 2025.3.1 -
function request(url){
    const xhr = new XMLHttpRequest();
    xhr.onerror = ()=>{};
    xhr.ontimeout = ()=>{};
    xhr.onreadystatechange = function(res) {
        switch(res.readyState) {
            case 0:
                break
            case 4:
                if(this.status === 200 || this.status === 304) {
                    console.log(this.responseText);
                }
        }
    }
    xhr.open('GET', url, true);
    xhr.timeout = 3000;
    xhr.setRequestHeader(key, value);
    xhr.responseType = 'text';
    xhr.send();
}
- 第六遍 2025.2.15 -
- 第五遍 2025.2.9 -
⭐️ // status 在this上
- 第四遍 2025.2.2 -
 // 1、函数名称错误:onreadystatechange
 // 2、状态属性不在 this 上,在 res 上:res.readyState
 // 3、switch 中 0 表示请求还没开始,要使用 break 跳出循环
 // 1、属性名称记错 :res.readyState
 // 1、open 的参数顺序记错了:xhr.open('GET', url, true)
// 2025.02 Nextjs 获取链接中的参数
- 第三遍 2025.3.1 -
import { useSearchParams } from 'next/navigation';
const params = useSearchParams();

import { useSearchParams } from 'react-route-dom';  // 1、是 router 不是 route
const [params, setParams] = useSearchParams();

const date = params.get('date');

需要配合 Suspense 标签处理路由的异步情况layout.tsx
export function Root({chidren}: {children: React.ReactNode}) {
    return <html>
        	<body>
        		<Suspense fallback={<div>Loading</div>}>
        			{children}
        		</Suspense>
        	</body>
        </html>
}
- 第二遍 2025.2.26 -
- 第一遍 2025.2.22 -
 // 1、核心函数的名称记错:useSearchParams
 // 2、另一个依赖包是 react-router-dom + 且为 params 和 setParams
 // 3、使用 useSearchParams 需要配套 <Suspense> 在 layout.tsx 处理路由的异步加载
// 2025.02 Mysql 8.0 以上,Sequel报插件错
- 第三遍 2025.3.1 -
本地 重新安装 mysql 安装包改变插件 // ⭐️ use legacy password
线上 改变 root 账户的登录插件 password  // 1、认证插件 mysql_native_password
- 第二遍 2025.2.26 -
 // 1、线上 改变的是认证插件
- 第一遍 2025.2.22 -
⭐️ // 本地:安装 Use Legacy Password Encryption
 // 1、将 root 用户的认证插件修改为 mysql_native_password
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第五遍 2025.3.1 -
A.hasMany(B, {
    foreignKey: B_id,
    sourceKey: A_id
})
B.belongsTo(A, {
    foreignKey: A_id,
    targetKey: B_id
})
await A.findAll({
    where1, 
    includes: [{ // ❌ 1、include 关键词记错了
        model: B,
        where2
    }]
})
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 1、查询需要 await
 // 2、查询关键词 include: [{ model:B }]
- 第二遍 2025.2.17 -
 // 1、查询不会 await A.findAll({ include: [{ model:B }] })
- 第一遍 2025.2.13 -
 // 1、关键词记错了:belongsTo
 // 2、查询不会 await A.findAll({ include: [{ model: B }] })
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第五遍 2025.3.1 -
const [issue, created] = await table.findOrCreate({
    where,
    defaults: data
});
if(!created) {
    issue.set(data);
    await issue.save()
}
// ⭐️ return NextResponse.json({ success: true, msg: 1111 })
- 第四遍 2025.2.26 -
- 第三遍 2025.2.22 -
 // 1、findOrCreated 参数错误 findOrCreated({ where, defaults: data })
 // 2、issue.set 不需要 await
- 第二遍 2025.2.17 -
 // 1、findOrCreate 的数据源属性名记错 defaults
 // 2、先set数据,再保存 issue.set(data); await issue.save();
- 第一遍 2025.2.13 -
// 作业 89:mysql 中类型的区别:varchar 和 char,date datetime
- 第四遍 2025.3.1 -
varchar 可变长度存储
char 固定长度存储补充空格
date YYYY-MM-DD 3字节
datetime YYYY-MM-DD HH-MM-SS 8字节
- 第三遍 2025.2.26 -
- 第二遍 2025.2.22 -
 // 1、varchar 可变长度存储;char 固定长度存储,不够则填充空格;tinytext 255字符;text 不限长度
- 第一遍 2025.2.8 -
// 作业11:写出一个用 yield 实现的递归算法,从0数到指定n
- 第四遍 2025.3.1 -
function * nTimes(n) {
    if(n>0) {
        yield * nTimes(n-1);
        yield (n-1)
    }
}
- 第三遍 2025.2.27 -
 // 1、少写了 *:yield 只能在生成器中使用 function *
- 第二遍 2025.2.22 -
 // 1、yield 只能在生成函数中使用 * ,少写了*号
 // 2、的确是 n > 0 才需要继续递归 -> 但是这么写,不是 next 的{value,done}格式,调用迭代器调用拿不到 0 这个值
 // 3、少写了 yield n-1 -> 上面的 yields * 是把 nTimes 进行一一迭代,yield 才是传递的每个迭代里的具体值
- 第一遍 2024.12.25 -
// 作业1:写一个只能被迭代指定次数的类
- 第四遍 2025.3.1 -
class count {
    constructor(n) {
        this.limit = n;
    }
    [Symbol.iterator]() {
        const limit = this.limit;
        let count = 0;
        return {
            next() {
                if(count < limit) {
                    return {value: count++, done: false}
                } else {
                    return {value: undefined, done: true}
                }
            },
            return() {
                return {value: undefined, done: true}
            }
        }
    }
}
- 第三遍 2025.2.27 -
- 第二遍 2025.2.22 -
 // 1、写成 nTimes 没有读题
- 第一遍 2024.12.25 -
// 答题核心:迭代器返回的是迭代器对象(包含next()、return()) + next 返回一个 done+value组成的对象
// 69 x 的平方根
- 第三遍 2025.3.1 -
function sqrt(n) {
    const mid = Math.ceil(n/2);
    let res = 0;
    for(;res<mid;res++){
        if(res*res<=n && (res+1)*(res+1) > n){
            return res
        }
    }
    return res
}
function sqrt(n) {
    let right = n+1;
    let left = 0;
    while(left < right){ // ❌ 1、while(left + 1 < right)
        let mid = Math.ceil(left + right);
        if(mid * mid > n) {
            right = mid
        } else {
            left = mid
        }
    }
    return left
}
- 第二遍 2025.2.26 -
- 第一遍 2025.2.21 -
 // 1、res 应该从 0 开始数,否则 n 为 0 时会返回 1
// 作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置
- 第五遍 2025.3.1 -
唯一索引:组合索引不重复 ⭐️ // 确保索引字段的组合值唯一
se.define(databse,{},{
    indexes: [{
        unique: true,
        fields: ['date', 'sort']
    }]
})
CREATE UNIQUE INDEX `idx_date_sort` ON `daily` (`sort`, `date`)
 ADD UNIQUE KEY `idx_date_sort` (`sort`, `date`)
updateOnDuplicate 配合 bulkCreate 批量更新数据使用在唯一索引重复时使用该属性后不报错而是更新数据只有在该属性中声明的属性被更新不声明的不更新
// ⭐️ await table.bulkCreate(data, { updateOnDuplicate: [name, age] })
underscored: true 驼峰转下划线
timestamps: false 不使用 created_at updated_at
// ⭐️ 少写了 tableName 指定数据库中表的名称
- 第四遍 2025.2.26 -
- 第三遍 2025.2.21 -
 // 1、CREATE UNIQUE INDEX - 少写了 UNIQUE,或者写 ADD UNIQUE KEY `dix_date_sort` (`date`, `sort`)
 // 2、属性名称 underScored、timeStamps
- 第二遍 2025.2.17 -
 // 1、创建的 SQL 关键词:CREATE UNIQUE INDEX idx_date_sort ON `table` (`date`, `sort`)
 // 2、ADD UNIQUE KEY `idx_date_sort` (`date`, `sort`)
 // 3、timestamps: false, tableName: 'users'
- 第一遍 2025.2.13 -
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第九遍 2025.3.1 -
mysql2 node 环境的数据库驱动直连数据库需要执行原生SQL
	- 提供了 mysql2/promise 支持异步 // ⭐️ 管理多个数据库连接
Sequelize ORM 将数据库表和行映射成为对象模型方便函数式操作
mysql2
import { createPool } from 'mysql2' // ❌ 1、数据包错了 mysql2/promise
const pool = createPool({host, user, password, database});
const con = await pool.getConnection();
const [rows] = await con.execute('SELECT * FROM table');
sequelize
import { Sequelize, DataTypes, Modal } from 'sequelize';
const se = new Sequelize(databse, user, password, {
    dialect: 'mysql',
    modalName: 'daily' // ❌ 2、少写了 host: 'localhost' 
})
--1
const Usr = se.define('Users', {
    name: DataTypes.STRING
},{
    underscored: true,
    timestamps: false
})
--2
class Usr extends Modal {}
Usr.init({
  name: DataTypes.STRING  
}, {
    sequelize: se,
    modalName: 'Users'
})
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
 // 1、mysql2:获取连接的函数错了 await getConnection()
 // 2、mysql2:执行 SQL 需要 await
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、sequelize:实例化参数错误 + init 参数错误
 // 2、sequelize:define 第三个参数应该是一些配置
- 第四遍 2025.2.9 -
- 第三遍 2025.1.31 -
 // 1、mysql2:导入依赖包错了 mysql2/promise + createPool的参数 少写了 database
- 第二遍 2025.1.26 -
 // 1、mysql2:创建连接池 createPool 不需要 await
- 第一遍 2025.1.22 -
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第九遍 2025.3.1 -
预渲染服务端渲染SSR-客户端发起请求后服务端引入data  生成 html客户端拿到后直接渲染不使用客户端js静态生成 SSG增量静态生成ISR-服务端在构建时运行时更新时间后自动创建新的 HTML实现增量
SSG
export default function Blog(({slug}) => (<div>{slug}</div>))
export const revalidate = 60;
export async function generateStaticParams() {
    return [{slug:'first'},{slug:'second'}]
}
SSR
import { getServerSideProps } from 'next/server';
export default function Blog(({data}) => (<div>{data}</div>))
export async function getServerSideProps() {
    const res = await fetch('xxx', options);
    const data = await res.json();
    return { props: {data} }
}
- 第八遍 2025.2.26 -
- 第七遍 2025.2.21 -
 // 1、静态生成 的函数名称是 generateStaticParams 中间不是 state 是 static
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、SSG + SSR:函数 都少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR + 客户端、服务端生成的结果都是 HTML
 // 2、SSR:函数名称、依赖包错误 getServerSideProps next/server
- 第三遍 2025.1.31 -
 // 1、SSG:记错函数名称 generateStaticParams,用于生成动态路由的参数列表 + 少写了 revalidate
 // 2、SSR:异步解析数据 需要 await - data.json() 是异步的,处理 json 数据转换为 js 对象       
- 第二遍 2025.1.26 -
 // 1、SSR: fetch 数据需要 await 
- 第一遍 2025.1.22 -
// 作业41:TED 如何解决焦虑
- 第三遍 2025.2.28 -
1Anything worth doingworth doing badly the first time
2给自己同情心 // ⭐️ 少批评自己,远离一直批评自己的朋友,包括自己
3和人建立连接找到价值感
- 第二遍 2025.1.30 - 
- 第一遍 2024.12.25 -
// 2025.02 docker-compose 的配置管理 context 
- 第一遍 2025.2.28 -
docker-compose.yml
context 配置根路径 // ❌ 1、指定 Docker 构建上下文的路径
后续的 dockerfile.json 执行会以此为根目录 // ❌ 2、后续 COPY ADD 用于将构建上下文中的文件复制到镜像,context 会影响到这些指令能够访问的文件范围
// 2025.02 TS - dayjs 引入但失效问题
- 第一遍 2025.2.28 -
需要再安装一个 @type/dayjs
同时在 ts.config 中配置 CommonJS  ES6 模块互相兼容的配置
// ❌ 1、在 tsconfig.json 中配置 esModuleInterop: true, allowSyntheticDefaultImports: true  esModuleInterop 用于处理 CommonJS 和 ES6 交互问题,dayjs 是 CommonJS 库;allowSyntheticDefaultImports 用于处理没有明确默认导出模块可以被导入
// 2025.02 sequelize 在 Nestjs 中的 env 配置
- 第一遍 2025.2.28 -
cross-env // ⭐️ 在指令中传入 环境变量NODE_ENV
// ❌ 1、少写了 @nestjs/config - envFilePath 读取不同配置的文件 + configService 获取配置
script: {
    "start:dev": "next start NODE_ENV=development",
    // ❌ 2、"cross-env NODE_ENV=development nest start --watch"
    "start:prod": "next start NODE_ENV=production",
    // ❌ 3、"cross-env NODE_ENV=production node dist/main"
}
const type = process.env.NODE_ENV;
@Moudle()
class My {constructor() {return {imports: [Sequelize.define(),Configuration]}} }
/** ❌ 4、 import { ConfigModule, ConfigService } from '@nestjs/config'
@Module({
	imports: [
		ConfigModule.forRoot({
			envFilePath: `.env.${process.env.NODE_ENV}` || 'development',
			isGlobal: true
		}),
		SequelizeModule.forRootAsync({
			imports: [ConfigModule],
			inject: [ConfigService],
			useFactory: (configService:ConfigService) => ({
				dialect: 'mysql',
				host: configService.get('DB_HOST'),
				port: 3306,
				username: 'root',
				password: configService.get('DB_PAAWORD'),
				database: 'Daily',
				models: [Ltn, Routine, Time]
			})
		}),
		LtnsModule,
		RoutinesModule,
		TimesModule
	]
})
export class AppModule {}*/
// 2021-07 前端路由的两种模式
- 第三遍 2025.2.28 -
hashlocation.hash + window.onhashchange
切换 #hash不会发起后端服务请求前端处理路由
historypushState replaceState  url 投入历史记录中前进后退history.go(n)history.forward()history.back() 会触发 popState
const title = '';
const state = {a:1};
const url = '/foo';
history.pushState(state, title, url);
dom.addEventlistener('popState', (e) => { console.log(e.state) }) 
// ❌ 1、 window.addEventListener
- 第二遍 2025.2.26 -
- 第一遍 2025.2.20 -
 // 1、少写了 replaceState + popstate; 少写了 location.hash + window.onhashchange
// 2019.07 引用css,link和@import的区别
- 第四遍 2025.2.28 -
link: XML 标签没有兼容性问题除了css还可以处理rss事务可以使用渲染进程预解析线程提前下载js可以修改DOM上的样式
@import:css2.0提出低版本要写兼容代码只能加载css代码遇到再加载js难以修改样式因为加载顺序问题
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、link 少写了:可以处理 rss 等其他事务
 // 2、@import 少写了:只能加载 css
- 第一遍 2025.1.8  -
// 作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码
- 第四遍 2025.2.28 -
GET /index HTTP/1.1
HTTP/1.1 200 OK
HOST
200 OK
204 No Content
301 Moved Permanently // ⭐️ 从响应头中获取 Location
302 Moved Templately
304 Not ModifiedIf-none-match/ETag If-modified-since/Last-modified
400 Bad Request 请求头语法错误
403 Forbidden 禁止访问权限
404 Not Found 找不到
500 服务器错误
- 第三遍 2025.2.26 - 
- 第二遍 2025.2.20 -
 // 1、GET /index HTTP/1.1
 // 2、400 Bad Request 请求有语法错误(格式不正确、缺少必要请求参数、请求体格式错误)
 // 3、403 Forbidden 请求资源禁止访问
 // 4、405 Method Not Allowed 使用了服务器不支持的 HTTP 请求方法
- 第一遍 2025.1.8  -
// 作业32:什么是队头阻塞?原因是什么?怎么解决?
- 第四遍 2025.2.28 -
队头阻塞: 
	由于 HTTP/1.1 的请求-应答模型一个请求的响应还没返回的时候会阻塞其他的请求响应返回一个包不回来就会影响其他包
	TCP 的丢包机制一个包发出后一直不返回会导致其他包即使返回了也会被存储在缓冲区无法被上层取出
解决
HTTP/1.1 优化方案域名分片并发连接
HTTP/2.0 使用二进制帧双向传输序列形成虚拟的流多路复用-一个连接可以处理多个请求响应多个请求响应之间没有先后顺序关系解决了应用层的队头阻塞传输层仍旧存在
HTTP/3.0 QUIC 协议使用 UDP没有丢包机制
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、队头阻塞的原因:请求-应答模式导致的
 // 2、HTTP/1.1 优化的方案:域名分片 + 并发连接
- 第一遍 2025.1.20 -
// 作业40:写出以下值
- 第四遍 2025.2.28 -
10 2NaN 3NaN undefined {} 
40 NaN NaN Number只要有非数字字符即返回NaNparseIntparseFloat第一个字符是非数字字符时返回NaN
5'122' '32' '02' 'NaN2' NaN 630 32 712 位操作符只能处理32位正整数所以会将左右处理为正整数
8true undefined 派生自 null相等性测试为 true
- 第三遍 2025.2.26 -
- 第二遍 2025.2.20 -
 // 1、isNaN 为 true 少写了 {}
 // 2、Number-含有非数字字符就NaN parseInt-非数字开头即NaN
- 第一遍 2025.1.20 -
// 2019.06 函数参数传值修改
- 第二遍 2025.2.28 -
arr = [1,2,3]
obj = {a:1}
100 100 100 err 55 100
- 第一遍 2025.1.7 -
// 2021.04 03 为什么很多网站第二次打开速度会很快
- 第二遍 2025.2.28 -
DNS 解析有了缓存
渲染进程可以复用相同域名
Http/1.1 后默认开启长连接TCP 连接直接复用
http get 类型的请求被默认缓存或者出现协商缓存服务端返回 304 使用缓存刷新缓存时间
- 第一遍 2025.1.7 -
// 2021.07 怎么判断数组类型?4种方法
- 第二遍 2025.2.28 -
Array.isArray - 准确
x instanceof Array 在多个上下文中可能会声明多个 Array 构造函数可能会有异常 // ⭐️ 无法识别原型链中带有Array.prototype,但实际不是数组的对象
Object.prototype.toString.call(x) '[object Array]' 可以被修改
x.constructor 可以被修改
- 第一遍 2025.1.7 -
// 2019.07 for循环+计时器,如何实现i按序输出
- 第二遍 2025.2.28 -
let词法环境
for(let i = 0; i < 10; i++) {
    setTimeout(() => console.log(i), 0)
}
第三个参数形成闭包
for(let i = 0; i < 10; i++) {
    setTimeout(console.log, 0, i);
}
立即执行函数
for(let i = 0; i < 10; i++) {
    (function (a) { setTimeout(() => console.log(a), 0) })(i)
}
- 第一遍 2025.1.7 -
// 2019.06 检测数据类型 + 布尔转换为0的值
- 第二遍 2025.2.28 -
typeof x 检测基本数据类型检测函数得到"function"检测对象/数组都会得到 "object"
x instanceof Ctor 检测被检测对象的原型链上是否有指定构造函数的原型对象
Object.prototype.toString.call(x) 得到"[object Array]"
x.contructor 指向构造函数
Array.isArray 检测数组
布尔0 NaN null undefined false ''
- 第一遍 2025.1.7 -
// 作业15:类由哪些组成,不同定义的内容在继承时有什么特点?类是怎么实现继承的?
- 第二遍 2025.2.28 -
constructor 构造函数-声明的内容所有子类都重新实例化不继承有独立的作用域链 // ⭐️ 不会在原型上共享
static 静态函数 - 声明的函数只有当前类能够访问子类无法访问不继承
函数声明 - 所有子类实例共享继承 // ⭐️ 原型方法
类的继承通过 extends 实现如果是继承的子类需要调用 super 调用来实例化父类的构造函数
- 第一遍 2025.1.7 -
// 2021.07 数组扁平化(一层、全部展开、指定深度)
- 第八遍 2025.2.28 -
一层arr.flat()[].concat(...arr)[].concat.call(null, ...arr)[].concat.apply(null, arr)
全部展开arr.flat(Infinity)arr.toString().spli(',')
function expense(arr, deep = 1) {
    return arr.reduce((pre, cur) => 
       pre.concat(Array.isArray(cur) && deep > 1 ? expense(cur, deep - 1) : cur ), [])
}
- 第七遍 2025.2.12 -
- 第六遍 2025.2.9 -
- 第五遍 2025.1.31 -
全部展开arr.flat(Infinity)arr.toString().split(',')JSON.parse(JSON.stringify(arr))
 // 1、JSON.parse(JSON.stringify(arr)) 是用于深拷贝的,不能展开
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、少写了 arr.flat(Infinity)
 // 2、全部展开递归函数中,pre 的值不要使用 push 来更新 - concat 既能处理数组,也能处理非数组
- 第二遍 2025.1.19 -
 // 1、deep 传递的时候要减一
// 作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情?
- 第九遍 2025.2.28 -
类组件 // ⭐️ 几个生命周期其实是有先后顺序的,下次可以写一下,明确一下
	挂载阶段componentDidMountconstructor初始化state和事件处理函数的thisgetStateFromPropsrender // ❌ 1、函数错误 static getDerivedStateFromProps
	更新阶段componentDidUpdate(prevProps, prevState)shouldComponentUpdaterendergetStateFromPropsrender // ❌ 2、函数错误 static getDerivedStateFromProp
	卸载阶段componentWillUmMount
函数组件
useEffect 模拟 componentDidMount componentDidUpdate componentWillUmMount
useLayoutEffect  commit 阶段之后同步执行阻塞重绘
- 第八遍 2025.2.12 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.31 -
 // 1、少写了 constructor - 初始化 state 和绑定事件处理函数的 this
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
 // 1、shouldComponentUpdate 写错在挂载阶段,实际应该归属于更新阶段
 // 2、阶段划分是:挂载-更新-卸载,其中更新阶段记错了 - 应该是当组件的 state/props 变化,进入更新阶段,重新执行 render 函数来更新 DOM
- 第三遍 2025.1.19 -
 // 2、除了类组件函数的生命周期,还要意识到函数组件也通过 hooks 模拟生命周期
// 作业100:写一个原生的 form ,写出两个提交表单的方式 + 阻止提交方式,表单字段的公共属性/方法,怎么校验表单
- 第一遍 2025.2.28 -
<form method='GET' action='/foo'></form>
<button type='submit'></button>
const form = document.form[0];
form.submit();
form.addEventListener('submit', (e) => e.preventDefault)
disable type value + focus blur change
form.checkValidity()
// 作业93:闭包的作用和原理
- 第五遍 2025.2.28 -
闭包原理内部函数中引用外部函数中的变量导致外部函数执行完后活动对象并没有随之清理 closure 保存
	- 核心原理是词法作用域只和代码所写的位置有关 // ⭐️ 在定义时确定作用域,而不是执行时
作用保存变量封装-私有变量函数工厂 // ❌ 1、少写了:函数回调
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
 // 1、原理少写了闭包依赖于「词法作用域」-作用域链-垃圾回收机制
 // 2、①保存状态:允许函数记住它被创建时的环境 + ②数据封装:创建私有变量 + ③回调函数:确保回调函数在调用时能访问到定义时的上下文 + ④函数工厂:创建定制化函数
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
// 作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法
- 第五遍 2025.2.28 -
序列化:将数据对象数组转换成方便传输存储的格式 // ⭐️ 将 数据结构/对象 转换
常见序列化JSON.stringify() 将对象转换成字符串无法处理 function且处理循环时报错
	new FromData(form) 将表内容转换成键值对
反序列化服务端返回的数据都是序列化的客户端需要反序列化解析得到 js 对象
JSON.parse() 解析为json对象 
res.json() Nextjs的响应值中包含响应头响应行响应体 // ⭐️ 将响应体中的 JSON 解析为 js 对象
const data = {"a":1}
const options = {
    data: JSON.stringfy(data),
    method: 'POST',
    Content-type: 'application/json' // ⭐️ headers: { 'Content-type': 'application/json' }
}
const res = await fetch('xxx', options).then(res => res.json())
- 第四遍 2025.2.26 -
- 第三遍 2025.2.19 -
 // 1、将「数据结构、对象」转换为可以「存储、传输」的格式的过程
- 第二遍 2025.2.10 -
 // 1、JSON.stringify 无法处理 Function、无法处理循环引用;new FormData 收集表单数据并序列化,转换成键值对格式
- 第一遍 2025.2.8 -

LTN①⑦

LTN 17 共 88题,错题 36题(2.19-2.24 6天,共计15h23m),工具推荐做题周期 2.15 - 2.25

LTN ①⑦ 错题重做 36错题/88题 - 10错题

做题时长 2.25 错题全梳理 2h19m + 6h8m

✅作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法 ✅作业93:闭包的作用和原理 ✅2024-11 第十九章 表单脚本 ❌2024-10 第十二章 BOM 小结 ✅2024-09 第七章 迭代器与生成器 小结 ✅2019-06 第五章 基本引用类型 小结 ✅2021-07 前端路由的两种模式 ✅作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码 ✅2019.07 引用css,link和@import的区别 ✅作业40:写出以下值

1Number(null)
2Number(undefined)
3isNaN() 检测那些值为 true?
4NumberparseIntparseFloat 检测 空字符串 得到?
51+'2'+'2'
1+ +'2'+'2'
1+ -'1'+'2'
'A'-'B'+'2'
'A'-'B'+ 2
6let i = 0, age = 30;
i++ + age;
++i + age;
7 12.34 | 0;
12.43 >> 0;
8null === undefined

✅作业32:什么是队头阻塞?原因是什么?怎么解决? ✅69.x 的平方根 ✅作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ✅作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置 ✅作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ✅作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ❌2025.02 Mysql 8.0 以上,Sequel报插件错 ✅2025.02 Nextjs 获取链接中的参数 ✅作业 89:mysql 中类型的区别:varchar 和 char,date datetime ✅作业1:写一个只能被迭代指定次数的类 ❌作业11:写出一个用 yield 实现的递归算法,从0数到指定n ✅作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅2023.07 clickhouse、mysql、mongodb异同 ✅2021.07 12-1 内存泄漏 ❌2021.06 flex布局 ❌作业99:渲染合成层定义、触发方式、优点、缺点 ❌2019.07 显示省略号 ✅2021.07 babel 作用+原理+补丁 ❌2021.05 27 HTTP/3 改进的点 ❌2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程 ❌2021.04 12 垃圾回收机制 ✅作业66:React如何处理错误 ✅2021.06 子盒子在父盒子中水平垂直居中有几种方法?

做题记录

// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第五遍 2025.2.27 -
1.father{ display: flex; align-iterms: center; justify-content: center; }
2.father{ display: table-cell; vertical-align: middle; text-align: center; }
.son { display: inline-block }
3.son{ position: absolute; top:0; right:0; bottom:0; left:0; margin: auto; }
4.son{ position:absolute; top:50%; left:50%; transform: translate(-50%, -50%); }
- 第四遍 2025.2.24 -
 // 1、table 少写了 .son{ display: inline-block }
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
- 第一遍 2025.1.30 -
 // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中 .father { display: table-cell; text-align: center; vertical-align: middle; } .son { display: inline-block; }
// 作业66:React如何处理错误
- 第十遍 2025.2.27 -
React 提供了两个错误处理函数处理 render commit 阶段的所有错误避免错误溢出应用影响 UI
static getDerivedStateFromError  componentDidCatch
class ErrorBoundaries extends React.component{
    constructor(props) {
        super(props);
        this.state = { hasError: false }
    }
    static getDerivedStateFromError() {
        return { hasError: true }
    }
    componentDidCatch(e, info) {
        console.log(e)
    }
    render() {
        if(this.state.hasError) {
            return <div> There are errs. </div>
        } else {
            return this.props.children;
        }
    }
}
<ErrorBoundaries><My /></ErrorBoundaries>
- 第九遍 2025.2.24 -
 // 1、静态函数名称错了 getDerivedStateFromError 
- 第八遍 2025.2.10 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
 // 1、静态函数名称错了 getDerivedStateFromError
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 2021.04 12 垃圾回收机制
- 第二遍 2025.2.27 -
 // 1、少写了 栈的清理方式 - 通过指针移动,不需要的数据的内存会被直接覆盖
v8 垃圾回收机制 基于代际假说分为新生代-副垃圾回收机制 老生代-主垃圾回收机制
新生代 - 占用内存空间小不产生内存碎片清理策略是 划分为空闲区和对象区清理2次都存活的变量升入老生代
	对象区快满时触发清理将有效对象复制并排列保存到空闲区执行完毕后两个区域对调  // 2、清理过程:先标记,再有序地复制到空闲区
老生代 - 占用内存空间大清理策略标记-清理标记是否还是活动对象清理无效数据向左移动清理内存碎片
 // 3、没有被引用就被标记为垃圾数据
- 第一遍 2025.2.24 -
 // 1、少写了栈的清理:指针移动 - 不需要的数据所在的内存被覆盖
 // 2、少写了:v8 引擎 基于代际假说
 // 3、划分区域名错误:新生代 - 副垃圾回收器,划分为 对象区域、空闲区域;清理时机错误:对象区域快满时;清理过程错误:标记 - 将存活的对象有序地复制到空闲区 - 复制完后翻转两个区域
 // 4、少写了算法:标记-清理,产生大量不连续的内存碎片
// 2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程
- 第二遍 2025.2.27 -
vue 更新数据后DOM是异步更新的在执行完当前事件循环后再更新DOMnextTick 提供了更新DOM后处理时机
vue2 先收集nextTick事件队列再根据当前环境使用合适的微任务宏任务实现异步执行
vue3 先收集nextTick事件队列再使用queueMicrotask实现微任务执行
 // 11、vue2 的 NextTick 根据回调函数判断是否返回 Promise;vue3 不论回调函数,都会返回 Promise
vue2
const callbacks=[];
let pending = false;
const flushJobs = () => {
    pending = false;
    const copy = callbacks.slice(0);
     // 4、 少写了清空原数组,避免重复执行回调 callbacks.length = 0;
    copy.forEach(it => it())
}

let timeFunc;
if(typeof Promise !== 'undefined'){
    timeFunc = () => {
        Promise.resolve.then(flushJobs);  // 1、Promise.resolve().then 少写了执行
    }
} else if(typeof MutationObserver !== 'undefined'){
	const ob = new MutationObserver(flushJobs);
    const dom = document.createElement('p');
    dom.nodeValue = '1'; 
     // 3、文本节点改用 createTextNode:const dom = document.createTextNode('1');
    ob.observe(dom,{ dataValue: true });  // 2、characterData 属性配置监听文本节点的变化
    timeFunc = () => {
        dom.nodeValue = '2';  // 5、赋值时使用 dom.textContent = '2'
    }
} else if(typeof setImmediate !== 'undefined'){
    timeFunc = () => {
        setImmediate(flushJobs)
    }
} else {
    timeFunc = () => {
        setTimeout(flushJobs, 0)
    }
}
 // 8、vue2 的 nextTick 根据是否传递回调函数,判断是否返回 Promise
function nextTick(fn, ctx) {
    if(fn) {
        callbacks.push(fn.bind(ctx));
    }
     /** 6、收集回调函数时,需要处理报错的情况 try catch
    let _resolve;
    callbacks.push(() => {
    	if(fn) {
    		try{ fn.call(ctx) } catch(e) { console.error(e) }
    	} else if(_resolve) { 
    		_resolve(ctx) 
    	}
    })
    */
    if(!pending) {
        pending = true;
        timeFunc();
    }
     /** 7、 少写了 支持 Promise 的调用
    if(!fn && typeof Promise !== 'undefined'){
    	return new Promise((resolve) => { _resolve = resolve })
    }
    */
}
// nextTick(() => { console.log('Next tick callback executed') });
// nextTick().then(() => { console.log('Next tick promise resolved') });

vue3
const callbacks=[];
let pending = false;
const flushJobs = () => {
    pending = false;
    const copy = callbacks.slice(0);
    copy.forEach(it => it())
     /** 9、callbacks 需要清空,避免回调函数被重复执行 - 换一种写法
    let job;
    while((job = callbacks.shift())) {
    	try{ job() }catch(e){ console.error(e) }
    }
    pending = false;
    */
}
function timeFunc() {
    queueMicrotask(flushJobs)
}
 // 10、vue3 中,在 nextTick 不论有没有传递回调,都会返回一个 pending 状态的 Promise
function nextTick(fn) {
	return new Promise((resolve) => {
        callbacks.push(() => { 
			if(fn) {
				try{ fn() }catch(e){ console.error(e) }
			}
			resolve(); // 解决 Promise,表示 nextTick 中的任务已完成
        });
        if(!pending){ 
            pending = true;
            timeFunc();
        } 
	})
}
- 第一遍 2025.2.24 -
 // 1、Vue 的数据更新是异步执行,修改数据后,Vue 不会立即更新 DOM,而是放到队列中等到下一个事件循环时再批量更新 - 需要 nextTick 来确保在 DOM 更新后再执行
 // 2、Vue2 的步骤:①回调函数收集到队列 ②异步执行方式选择:根据浏览器环境选择合适的异步执行方式,优先微任务 Promise/MutationObserver,如果不支持微任务则使用宏任务 setTimeout ③触发异步执行:在下次DOM 更新结束后执行
 // 3、Vue3:和Vue差异是,使用 queueMicrotask 统一处理微任务
 /* 4、Vue2 模拟实现
new Vue({
	data() { return {msg:'hello'} },
	mounted() { this.$nextTick(() => { console.log('DOM 已更新') }) }
})
**/
/* 5、Vue3
import { createApp, nextTick } from 'vue';
const app = createApp({
	data() { return {msg:'hello'} },
	mounted() { nextTick(() => { console.log('DOM 已经更新') }) }
})
app.mount('#app')
**
// 2021.05 27 HTTP/3 改进的点
- 第二遍 2025.2.27 -
 HTTP 的下层协议改为 QUIC  /** 1、详细比较对比
HTTP/3(应用层)			 HTTP/1.1 或 HTTP/2.0(应用层)  		 HTTP(应用层)
|						|									|
QUIC(传输层,基于 UDP)   									   TLS(安全层)
|						|									|
UDP(传输层)			 TCP(传输层)						  TCP(传输层)
|						|									|
IP(网络层)				 IP(网络层)						  IP(网络层)
|						|									|
数据链路层				 数据链路层							  数据链路层
|						|									|
物理层					  物理层								 物理层
**/
传输层 TCP 改为 UDP在应用层实现丢包重传的校验机制不需要三次挥手四次握手直接连接
完全解决了队头阻塞
- 第一遍 2025.2.24 -
 // 1、少写了 HTTP/2.0 存在的问题:HTTP/2.0 并没有完全解决队头阻塞,传输层的 TCP 有丢包重传机制 - TCP 为了保证可靠的传输,丢包必须等待重新传输确认,其他的包即使收到了也只能放在缓冲区,上层应用拿不出来,被丢的包不回来,大家都取不出来
 // 2、少写了:HTTP/3.0 UDP 无连接,HTTP/2.0 TCP 三次握手、四次挥手
// 2021.07 babel 作用+原理+补丁
- 第二遍 2025.2.27 -
作用 ES6 转换为 ES5 方便浏览器渲染
原理ES6转为ES6-ASTES6-AST转为ES5-ASTES5-AST转为ES5
	解析 @babel/parse 语法分析词法分析 得到ES6-AST // ⭐️ @babel/parser
	转换 @babel/transe ES6-AST转为ES5-AST // ⭐️ @babel/traverse
	生成 @babel/generate ES5-AST转为ES5 js 代码 // ⭐️ @babel/generator
补丁babel 只能处理 ES6 基础类型例如箭头函数补丁补充了其他处理能力 IteratorGeneratorPromiseasync/await、Symbol // ⭐️ Set Map Proxy Reflect Object.assign - @babel/polyfill
- 第一遍 2025.2.24 -
 // 1、原理:少写了解析过程:解析Parsing[词法分析+语法分析得到AST]-转换Transformation[遍历AST转换 ES6的AST转换成ES5的AST]-生成Code Generation[根据转换后的AST转换成ES5 js代码]
 // 2、不能转换的ES6语法:Iterator Generator Set Map Proxy Reflect Symbol 等全局对象 + Object.assign 全局对象上的方法;@babel/polyfill
// 2019.07 显示省略号
- 第五遍 2025.2.27 -
.line {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lines {
    overflow: hidden;
    display: -webkit-box-;  // 1、属性值 -webkit-box
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    
    padding: 10px;
    line-height: 30px;
    height: 30*3-10 px;
}
- 第四遍 2025.2.23 -
 // 1、属性名错误 -webkit-box-orient: vertical;
- 第三遍 2025.2.2 -
- 第一遍 -
 // 1、属性名错误:-webkit-box-orient: vertical;
// 作业99:渲染合成层定义、触发方式、优点、缺点
- 第二遍 2025.2.27 -
将页面划分成多个层每个层都有单独的渲染机制和布局互不影响 // ⭐️ 每个层都有自己独立的绘制和处理流程
优点分层渲染避免了很多不必要的回流重绘提高渲染性能
 // 1、优点少写了 合成层可以在 GPU 上进行加速
缺点每一个层都会占用内存渲染时会合并多个层也消耗性能
触发方式:①样式 will-change opacity transform
3D视频canvas
- 第一遍 2025.2.23 -
 // 1、少写了:渲染时,为了提高渲染效率和性能,将页面中不同元素划分成多个层,每层都有独立的绘制、处理流程 - 这就是渲染合成层
 // 2、少写了 3D 变化、视频/canvas
 // 3、少写了:减少不必要的回流重绘 + 实现流程流畅的动画效果,合成层可以在 GPU 上加速
// 2021.06 flex布局
- 第二遍 2025.2.27 -
布局 flex-direction 水平/水平翻转/垂直/垂直翻转多种
// ⭐️ row/row-reverse/column/column-reverse
排列 // ⭐️ 便捷的元素排列 + 空间分配
flex:flex-grow flex-shrink flex-basis + justify-content space-between space-around + align-iterms flex-start flex-end center 以主轴交叉轴处理 
⭐️ // flex-grow 正整数以 flex-basis 为基础
独立盒子 和外部浮动元素不重叠 默认从上至下垂直排列 内部计算高度时包含浮动元素
主轴默认不拉伸默认缩小 交叉轴默认拉伸 align-iterms:stretch
align-self 可以为子盒子单独设置模式覆盖 align-iterms // ⭐️ 允许单个项目与其他不一样的对齐方式
 // 1、少写了 flex 导致 float clear vertical-align 失效
- 第一遍 2025.2.23 -
 /** -	space-between 元素排列后,再分配 [a--b--c]
	-	space-around 每个元素左右空间都相等 [-a--b--c-]*/
// 2021.07 12-1 内存泄漏
- 第二遍 2025.2.27 -
不再使用的活动对象没有被清理一直占用内存 // ⭐️ 内存 Chrome Memory 录制查看
避免内存泄漏的方法:① 避免大量使用闭包修改全局变量  及时清理定时器 clearTimeout/clearInterval  及时移除监听器 removeEventListener/off  弱引用 weakMap weakSet m.delete(key) s.delete(v)
- 第一遍 2025.2.23 -
// 2023.07 clickhouse、mysql、mongodb异同
- 第七遍 2025.2.27 -
mysql 关系数据库存储结构型数据读写性能好高并发时有一致性问题提供事务操作
mongodb 文档类型数据库存储半结构类型非结构类型数据库读的性能好写的性能弱异步操作时会有数据一致性问题 // ⭐️ JSON、XML、HTML + 分布式操作
clickhouse 列式数据库读写性能都好不支持事务操作支持异步操作适合高吞吐低延迟的场景
- 第六遍 2025.2.23 -
 // 1、少写了:不支持事务,且支持分布式操作
- 第五遍 2025.2.18 -
 // 1、mysql 少写了:支持事务操作 + clickhouse 少写了:不支持事务
- 第四遍 2025.2.16 -
 // 1、mysql 少写了:支持事务操作;高并发时出现数据一致性问题
 // 2、mongodb 少写了:在分布式场景时出现数据一致性问题
 // 3、clickhouse 少写了:列式数据库 + 不支持事务,支持分布式查询 + 适合高吞吐、低延迟的场景
- 第三遍 2025.1.30 -
- 第二遍 2025.1.25 -
- 第一遍 2025.1.22 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第七遍 2025.2.27 -
使用 store 对象模型B-tree  来描述 react 的整个应用的 state只能通过 dispatch action 来修改 state
reducer - 使用旧state和action计算得到新的state
RTK - redux toolkit官方推荐工具包
import { createStore } from 'redux';
const reducer = (oldState={v:1}, action) => { if(action.type){ return {v:oldState.v+1} } }
const store = createStore(reducer);
store.dispatch({type: true});
store.subscribe(() => console.log(store.getState() + 1))
- 第六遍 2025.2.23 -
 // 1、 store 的函数名称错了:getState
- 第五遍 2025.2.18 -
 // 1、 store 的函数名称错了:getState
- 第四遍 2025.2.15 -
 // 1、注意 store 是对象树:Redux 将全局状态保存在 store 对象树中
⭐️ // reducer:返回新的 state 而不是改变
 // 2、reducer 参数顺序是 oldState,action
 // 3、 函数名称错了:getState,通过 subscribe 更新 UI
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 作业70:Vue 和 React 的 Diff 算法比较
- 第十遍 2025.2.27 -
Vue 1跳过静态节点 2双端指针比较算法找匹配 - 找不到时根据同类型同key 查找索引 - 列表组件 根据key
React 分层比较 同层级同类型优先比较 - 不同类型直接重新渲染同类型修改属性同类型同key 尝试复用移动
 // 1、Vue 是组件级别的更新 + React 是节点属性级别的更新
- 第九遍 2025.2.23 -
 // 1、Vue 少写:Diff 跳过静态节点 + Vue 是组件级别的更新
 // 2、分层比较 + 同类型优先比较 + React 是节点属性级别的更新 + DFS 深度优先遍历子节点,逐个比较
- 第八遍 2025.2.18 - 
- 第七遍 2025.2.16 -
 // 1、Vue 少写了:静态节点跳过;双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
- 第五遍 2025.1.25 -
 // 1、Vue 少写了:静态节点跳过 ;Vue 是基于组件级别的更新算法
 // 2、React 少写了:是基于单个节点属性级别的,适合大型动态复杂场景
- 第四遍 2025.1.24 -
- 第三遍 2025.1.18 -
- 第二遍 2025.1.13 -
- 第一遍 -
// 作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景
- 第三遍 2025.2.27 -
WeakMap/WeakSet 弱引用/值都是对象如果是空对象也就是没有引用时可以被清理
MapSet 强引用/值可以是任何值但只要 MapSet 存在垃圾清理程序就不会清理对象
场景节点为 DOM 节点时被删除后就可以被清理
- 第二遍 2025.2.23 -
 // 1、Map 和 Set 对存储的对象持有「强引用」,只要它们存在,对象就不会被回收 - 强引用意味着只要 Map 或 Set 存在,并且其中存储了某个对象,那么这个对象就不会被垃圾回收。即使在其他地方没有对该对象的引用,Map 或 Set 内部的引用会阻止垃圾回收器将其回收
 // 2、而 WeakMap 和 WeakSet 对存储的对象持有「弱引用」,不会阻止对象被垃圾回收
- 第一遍 2024.12.25 -
// 作业11:写出一个用 yield 实现的递归算法,从0数到指定n
- 第三遍 2025.2.27 -
function nTimes(n) {  // 1、少写了 *:yield 只能在生成器中使用 function *
    if(n>0) {
        yield * nTimes(n-1);
        yield n-1;
    }
}
for(let i of nTimes(3)){ console.log(i) }
- 第二遍 2025.2.22 -
 // 1、yield 只能在生成函数中使用 * -> 少写了*号
 // 2、的确是 n > 0 才需要继续递归 -> 但是这么写,不是 next 的{value,done}格式,调用迭代器调用拿不到 0 这个值
 // 3、少写了 yield n-1 -> 上面的 yields * 是把 nTimes 进行一一迭代,yield 才是传递的每个迭代里的具体值
- 第一遍 2024.12.25 -
// 作业1:写一个只能被迭代指定次数的类
- 第三遍 2025.2.27 -
class count {
    constructor(n) {
        this.limit = n;
    }
    [Symbol.iterator]() {
        const limit = this.limit;
        let count = 0;
        return{
            next() {
                if(count<limit) {
                    return {value: count++, done: false}
                } else {
                    return {value: undefined, done: true}
                }
            }
            return() {
                return {value: undefined, done: true}
            }
        }
    }
}
- 第二遍 2025.2.22 -
 // 1、写成 nTimes 没有读题
- 第一遍 2024.12.25 -
// 答题核心:迭代器返回的是迭代器对象(包含next()、return()) + next 返回一个 done+value组成的对象
// 作业 89:mysql 中类型的区别:varchar 和 char,date datetime
- 第三遍 2025.2.26 -
varchar 不限存储长度 // ⭐️ 可变长度存储
char 固定长度存储不够长度时空格补充
date YYYY-MM-DD 3字节 datetime YYYY-MM-DD HH-MM-SS 8字节
- 第二遍 2025.2.22 -
 // 1、varchar 可变长度存储;char 固定长度存储,不够则填充空格;tinytext 255字符;text 不限长度
- 第一遍 2025.2.8 -
// 2025.02 Nextjs 获取链接中的参数
- 第二遍 2025.2.26 -
import { useSearchParams } from 'next/navigation';
const params = useSearchParams();
或者
import { useSearchParams } from 'react-route-dom';
const [params, setParams] = useSearchParams();
--
const data = params.get('date');
需要和 <Suspense> 配合 layout.tsx
// ⭐️ export default function RootLayout({children}: ReadOnly<{children: React.ReactNode}>)
return <html>
    	<body>
    		<Supense> // ⭐️ <Suspense fallback={<div>Loading...</div>}>
    			{children}
    		</Suspense>
    	</body>
    </html>
- 第一遍 2025.2.22 -
 // 1、核心函数的名称记错:useSearchParams
 // 2、另一个依赖包是 react-route-dom + 且为 params 和 setParams
 // 3、使用 useSearchParams 需要配套 <Suspense> 在 layout.tsx 处理路由的异步加载
// 2025.02 Mysql 8.0 以上,Sequel报插件错
- 第二遍 2025.2.26 -
本地下载安装包安装的倒数第二部切换 插件
线上 root 账号下切换插件登录方式  // 1、认证插件
- 第一遍 2025.2.22 -
⭐️ // 本地:安装 Use Legacy Password Encryption
 // 1、将 root 用户的认证插件修改为 mysql_native_password
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第四遍 2025.2.26 -
A.hasMany(B, {
    foreignKey: B_id,
    sourceKey: A_id
})
B.belongsTo(A, {
    foreignKey: B_id,
    targetKey: A_id
})
await A.findAll({
    where,
    include: [
        {model: B}
    ]
})
- 第三遍 2025.2.22 -
 // 1、查询需要 await
 // 2、查询关键词是 include
 // 3、include 关联表是 model 来声明
- 第二遍 2025.2.17 -
 // 1、查询不会 await A.findAll({ include: [{ model:B }] })
- 第一遍 2025.2.13 -
 // 1、关键词记错了:belongsTo
 // 2、查询不会 await A.findAll({ include: [{ model: B }] })
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第四遍 2025.2.26 -
const [issue, created] = await table.findOrCreated({
    where,
    defaults: data
});
if(!created){
    issue.set(data);
    await issue.save()
}
// ⭐️ return NextResponse.json({ success: true, msg: 1111 })
- 第三遍 2025.2.22 -
 // 1、findOrCreated 参数错误 findOrCreated({ where, defaults: data })
 // 2、issue.set 不需要 await
- 第二遍 2025.2.17 -
 // 1、findOrCreate 的数据源属性名记错 defaults
 // 2、先set数据,再保存 issue.set(data); await issue.save();
- 第一遍 2025.2.13 -
 // 1、需要获取函数的返回值,获取是否为新创建 const [issue, created]
 // 2、数据源没有传入 defaults: data
 // 3、如果已经存在,更新描述需要单独处理
// 作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置
- 第四遍 2025.2.26 -
唯一索引组合索引唯一不会重复 ⭐️ // 确保索引字段的组合值唯一
sequelize.define(database, {}, {
    indexes:[{
        unique: true,
        fileds: ['date', 'sort']
    }]
})
配合 SQL
CREATE UNIQUE INDEX `idx_date_sort` ON `database` (`date`, `sort`)
或者 ADD UNIQUE KEY `idx_date_sort` (`date`, `sort`)
updateOnDuplicate 在批量添加数据时如果唯一索引重复时不抛出错误而是更新在这个属性内声明的才会被更新没有被声明的则保持不变
await table.bulkCreate(data, {
    updateOnDuplicate: [name, age]
})
define 中的第三个参数
underscored: true 驼峰映射为下划线
timestamps: false 默认不添加 created_at updated_at
// ⭐️ 少写了 tableName 指定数据库中表的名称
- 第三遍 2025.2.21 -
 // 1、CREATE UNIQUE INDEX - 少写了 UNIQUE,或者写 ADD UNIQUE KEY `dix_date_sort` (`date`, `sort`)
 // 2、属性名称 underScored、timeStamps
- 第二遍 2025.2.17 -
 // 1、创建的 SQL 关键词:CREATE UNIQUE INDEX idx_date_sort ON `table` (`date`, `sort`)
 // 2、ADD UNIQUE KEY `idx_date_sort` (`date`, `sort`)
 // 3、timestamps: false, tableName: 'users'
- 第一遍 2025.2.13 -
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第八遍 2025.2.26 -
mysql2  Node 环境提供的数据库驱动可以直连数据库需要写大量 SQL // ⭐️ 可以编写、执行原生 SQL
	- 提供了 Promise async/await 异步操作;同时管理多个连Sequelize 将数据库的表和行映射为对象模型 ORM直接可以通过函数操作
mysql2
import { createPool } from 'mysql2/promise';
const pool = createPool({host, user, password, database});
const con = await pool.getConnection();
const [rows] = await con.execute('SQL');
sequelize
import { Sequelize, DataTypes, Modal } from 'sequelize';
const se = new Sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql'
});
创建模型 法1
class Usr extends Modal {}
Usr.init({
    name: DataTypes.STRING
}, {
    sequelize: se,
    modalName: 'users'
})
创建模型 法2
sequelize.defimne(database,  {
    name: DataTypes.STRING
}, {
    underscored: true,
    timestamps: false
})
- 第七遍 2025.2.21 -
 // 1、mysql2:获取连接 函数错了 getConnection()
 // 2、mysql2:执行 SQL 需要 await
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、sequelize:实例化参数错误 + init 参数错误
 // 2、sequelize:define 第三个参数应该是一些配置
- 第四遍 2025.2.9 -
 // 1、sequelize:实例化的参数错误 + init 参数错误
- 第三遍 2025.1.31 -
 // 1、mysql2:导入依赖包错了 mysql2/promise + createPool的参数 少写了 database
 // 2、mysql2:获取连接的函数错了 await pool.getConnection()
 // 3、mysql2:需要使用 await 执行 SQL
 // 4、sequelize:有两种模型定义方法:一种是 define,另一种 extends Modal + init
- 第二遍 2025.1.26 -
 // 1、mysql2:依赖包是 mysql2/promise
 // 2、mysql2:创建连接池 createPool 不需要 await
 // 3、mysql2:获取连接时需要 await + 需要使用 await 执行 SQL
 // 4、sequelize:实例化的参数错误 + init 参数错误
- 第一遍 2025.1.22 -
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第八遍 2025.2.26 -
预渲染包含了服务端渲染 SSR getServerSideProps 在客户端请求时在服务端引入 data 动态生成 HTML 返回客户端直接渲染不需要使用客户端 js包含了静态生成 SSG  增量静态生成 ISR generateStaticParams 返回动态路由在构建运行时在设定的更新时间 revalidate 重新增量构建新的 HTML // ⭐️ revalidate 重新验证时间
SSG
export default Blog({slug}) {return <div>{slug}</div>} // ⭐️ 少写了 function
export const revalidate = 60;
export async function generateStaticParams() {
    return [{slug: 'first'}, {slug: 'second'}]
}
SSR
import { getServerSideProps } from 'next/server';
export default Blog({data}) {return <div>{data}</div>} // ⭐️ 少写了 function
export async function getServerSideProps() {
    const res = await fetch('xxx', options);
    const data = await res.json();
    return {props: {data}}
}
- 第七遍 2025.2.21 -
 // 1、静态生成 的函数名称是 generateStaticParams 中间不是 state 是 static
 // 2、SSG 函数名称错误:应该是 generateStaticParams
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、SSG + SSR:函数 都少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR + 客户端、服务端生成的结果都是 HTML
 // 2、SSR:函数名称、依赖包错误 getServerSideProps next/server
 // 3、SSG:函数名称错误 generateStaticParams
- 第三遍 2025.1.31 -
 // 1、SSG:记错函数名称 generateStaticParams,用于生成动态路由的参数列表 + 少写了 revalidate
 // 2、SSR:异步解析数据 需要 await - data.json() 是异步的,处理 json 数据转换为 js 对象       
- 第二遍 2025.1.26 -
 // 1、SSR: fetch 数据需要 await  +  异步解析数据需要 await
- 第一遍 2025.1.22 -
// 69.x 的平方根
- 第二遍 2025.2.26 -
function sqrt(n) {
    let left = 0;
    let right = n + 1;
    while(left + 1 < right) {
        const mid = Math.ceil((left + right) / 2)
        if(mid * mid > n) {
            right = mid
        } else {
            left = mid
        }
    }
    return left
}
function sqrt(n) {
    let res = 0;
    const mid = Math.ceil(n/2);
    while(res < mid){
        if(res*res <= n && (res+1)*(res+1) > n) {
            return res
        }
        res++;
    }
    return res
}s
- 第一遍 2025.2.21 -
 // 1、res 应该从 0 开始数,否则 n 为 0 时会返回 1
// 作业32:什么是队头阻塞?原因是什么?怎么解决?
- 第三遍 2025.2.26 -
队头阻塞请求响应是有顺序的一个响应不返回会影响后续的请求响应继续等待应用层和传输层都存在
原因HTTP/1.1 的请求-应答模型
HTTP/1.1 域名分片和并发连接
HTTP/2.0 使用二进制帧虚拟的流-双向传输序列多路复用一个连接可以处理多个请求响应多个请求响应之间没有顺序关系解决了队头阻塞但是传输层 TCP 的丢包重传机制还存在队头阻塞影响不返回时其他请求已经返回的包存储在缓存区上层拿不出来
HTTP/3.0 使用 QUIC 协议不再使用 TCP使用 UDP
- 第二遍 2025.2.20 -
 // 1、队头阻塞的原因:请求-应答模式导致的
 // 2、HTTP/1.1 优化的方案:域名分片 + 并发连接
- 第一遍 2025.1.20 -
// 作业40:写出以下值
- 第三遍 2025.2.26 -
10 2NaN 3NaN undefined {} 
4Number-0遇到非数字字符即返回NaN parseInt/parseFloat-NaN非数字开头即返回NaN
5'122' '32' '02' 'NaN2' NaN 630 32 712 位操作符只能操作32位所以计算前左边被转为整数
8true undefined 是从 null 衍生出来的规定相等性测试为 true ⭐️ // 派生
- 第二遍 2025.2.20 -
 // 1、isNaN 为 true 少写了 {}
 // 2、Number-含有非数字字符就NaN parseInt-非数字开头即NaN
- 第一遍 2025.1.20 -
// 2019.07 引用css,link和@import的区别
- 第三遍 2025.2.26 -
linkXML标签兼容性高渲染进程预解析提前下载除了css还可以处理rss事务js可以修改样式
@importcss2.0提出的标签需要写兼容代码遇到再下载只能加载cssjs难以修改样式因为加载顺序问题
- 第二遍 2025.2.20 -
 // 1、link 少写了:可以处理 rss 等其他事务
 // 2、@import 少写了:只能加载 css
- 第一遍 2025.1.8  -
// 作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码
- 第三遍 2025.2.26 -
请求头 GET /index HTTP/1.1
响应头 HTTP/1.1 200 OK
HOST
200 OK
204 No Content 只有响应头
301 Moved Permanently 永久重定向客户端从响应头中获取 Location 
302 Moved Temperatly 临时重定向客户端从响应头中获取 Location 
304 Not Modified 使用本地缓存并刷新缓存时间 ⭐️ // If-none-match/ETag If-modified-sine/Last-modified
401 Bad Request 请求头语法错误 ⭐️ // 是 400,不是 401
403 Forbidden 禁止访问没有权限
404 Not Found 找不到文件
500 服务器错误
- 第二遍 2025.2.20 -
 // 1、GET /index HTTP/1.1
 // 2、400 Bad Request 请求有语法错误(格式不正确、缺少必要请求参数、请求体格式错误)
 // 3、403 Forbidden 请求资源禁止访问
 // 4、405 Method Not Allowed 使用了服务器不支持的 HTTP 请求方法
- 第一遍 2025.1.8  -
// 2021-07 前端路由的两种模式
- 第二遍 2025.2.26 -
hash  history
hash: location.hash + window.onhashchange 修改后不会刷新页面不会向服务器发送请求 ⭐️ // #hash
history: pushState popState replaceState 修改后将新地址保存入 history但不触发服务器请求在前进后退history.go(n)history.forward()hostory.back() 触发 popState
const state = {page: 1111};
const title = '';
const url = '/new-page';
history.pushState(state, title, url);
window.addEventListener('popState', (e) => {
    console.log(e.state)
})
- 第一遍 2025.2.20 -
 // 1、少写了 replaceState + popstate
 // 1、少写了 location.hash + window.onhashchange
// 作业93:闭包的作用和原理
- 第四遍 2025.2.26 -
闭包原理内部函数引用外部函数的变量导致外部函数执行完毕后活动对象保存在 closure 中没有被清理
	- 核心是词法作用域变量的上下文只和代码所在位置有关 ⭐️ // 和执行时无关
闭包作用保存变量封装变量-私有变量工厂函数回调函数
- 第三遍 2025.2.19 -
 // 1、原理少写了闭包依赖于「词法作用域」-作用域链-垃圾回收机制
 // 2、①保存状态:允许函数记住它被创建时的环境 + ②数据封装:创建私有变量 + ③回调函数:确保回调函数在调用时能访问到定义时的上下文 + ④函数工厂:创建定制化函数
- 第二遍 2025.2.10 -
 // 1、少写了 函数工厂、回调函数
- 第一遍 2025.2.8 -
// 作业92:前端中的序列化是什么?常见序列化方法。反序列化是什么?有哪些方法
- 第四遍 2025.2.26 -
序列化将数据结构对象转换成可以存储传输的格式
 JSON.stringify 将对象/数组转换为 JSON 格式无法处理 Function处理循环引用对象时会报错 ⭐️ // 常用于 POST PUT 请求
 new FormData(form) 将表单中的对象转换成键值对 ⭐️ // 收集表单数据并序列化
反序列化服务端返回给客户端的是序列化后的数据客户端需要反序列化拿到其中的对象 ⭐️ // 将接收到的序列化数据转换回原始数据结构、对象的过程
 JSON.parse  res.json() ⭐️ // 将响应体中的 JSON 解析为 js 对象
const data = {'a':1};
const options = {
    ⭐️ // 少写了 method: 'POST',
    header:{'content-type': 'text'}, ⭐️ // Content-Type: application/json
    body: JSON.stringify(data),
}
fetch('xxx', options).then(response => response.json).then(res => console.log(res))
- 第三遍 2025.2.19 -
 // 1、将「数据结构、对象」转换为可以「存储、传输」的格式的过程
- 第二遍 2025.2.10 -
 // 1、JSON.stringify 无法处理 Function、无法处理循环引用;new FormData 收集表单数据并序列化,转换成键值对格式
- 第一遍 2025.2.8 -

LTN①⑦ 工具推荐做题周期 2.15 - 2.25 88题

(LTN1-49, LTN2-7, LTN3-17, LTN4-11, LTN5-7)共计:15h23m

LTN1 【推荐做题时间 02-15 - 7题 实际做题时间:2.19 】 ✅作业90:金玉满堂是什么意思?在花卉中的说法是什么? ✅作业91:三次握手的过程和具体包的作用 ❌作业92:前端中的序列化是什么?常见序列化方法 ❌作业93:闭包的作用和原理 ✅2021.07 事件流 + 事件模型 ✅2021.06 链表 141. 环形链表 ✅2021.09 get和post有什么区别

LTN3 【推荐做题时间 02-15 - 11题 实际做题时间: 2.19 + 2.20】 ✅2021-06 CSS性能优化 ✅2019-06 第一章-第三章 简介、基本概念 小结 ✅2024-11 第十八章 动画与Canvas图形 ❌2024-11 第十九章 表单脚本 ❌2024-10 第十二章 BOM 小结 ❌2024-09 第七章 迭代器与生成器 小结 ❌2019-06 第五章 基本引用类型 小结 ❌2021-07 前端路由的两种模式 ✅2021-07 给DOM元素绑定事件 ✅2021-04 实现给定时间切换状态 ✅2024-11 数组乱序 洗牌算法

LTN4 【推荐做题时间 02-15 - 6题 实际做题时间: 2.20】 ✅作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器 ❌作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码 ✅作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接? ✅作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义? ❌2019.07 引用css,link和@import的区别 ✅2019.07 隐式转换:{}+{}=?

LTN1 【推荐做题时间 02-17 - 2题 实际做题时间: 2.20】 ✅作业58:React和Vue是怎么描述UI的 ✅作业62:fiber架构是什么?优点和实现方式?

LTN4 【推荐做题时间 02-17 - 4题 实际做题时间: 2.20】 ❌作业40:写出以下值

1Number(null)
2Number(undefined)
3isNaN() 检测那些值为 true?
4NumberparseIntparseFloat 检测 空字符串 得到?
51+'2'+'2'
1+ +'2'+'2'
1+ -'1'+'2'
'A'-'B'+'2'
'A'-'B'+ 2
6let i = 0, age = 30;
i++ + age;
++i + age;
7 12.34 | 0;
12.43 >> 0;
8null === undefined

❌作业32:什么是队头阻塞?原因是什么?怎么解决? ✅作业16(1):箭头函数和普通函数的区别是什么?(3点) ✅2019.07 盒模型有哪些?有什么区别?

LTN1 【推荐做题时间 02-18 - 2题 实际做题时间: 2.20 + 2.21】 ✅作业64:Vue渲染怎么实现的? ❌69.x 的平方根

LTN1 【推荐做题时间 02-19 - 6题 实际做题时间: 2.21】 ✅作业67:React 怎么做的性能优化? ✅作业68:React hooks 的原理是什么?useEffect useState模拟实现 ❌作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据 ❌作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置

LTN1 【推荐做题时间 02-21 - 8题 实际做题时间: 2.21 + 2.22】 ✅2020.07 对象分类、Promise按序执行、实现map ✅2024.11 第十七章 事件 ✅2021-07 常见Webpack问题 ✅作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高? ❌作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ❌作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ❌2025.02 Mysql 8.0 以上,Sequel报插件错 ❌2025.02 Nextjs 获取链接中的参数

LTN1 【推荐做题时间 02-22 - 6题 实际做题时间: 2.22】 ✅作业2:支持迭代器的原生语言特性有哪些? ✅2019.07 为什么要清除浮动?怎么清除浮动? ✅作业45:原生+canvas 显示一张png格式的图片 ✅2024.10 第十四章-第十五章 DOM、DOM扩展 ✅2024.12 模拟实现instanceof ✅2019.07 h5新标签和语义化,块/行内元素

LTN2 【推荐做题时间 02-22 - 2题 实际做题时间: 2.22】 ✅作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现? ❌作业 89:mysql 中类型的区别:varchar 和 char,date datetime

LTN4 【推荐做题时间 02-22 - 1题 实际做题时间: 2.22】 ✅2024-09 第六章 集合引用类型 小结

LTN5 【推荐做题时间 02-22 - 7题 实际做题时间: 2.22 + 2.23】 ❌作业1:写一个只能被迭代指定次数的类 ✅作业7:原始值包装类型和原始类型在使用 typeof 和 instanceof 进行类型判断时,有什么异同? ✅作业9:数组自带排序 sort() 为什么经常要传比较函数?写一个降序的比较函数。如果数组元素全是数值,请给出简写形式 ❌作业11:写出一个用 yield 实现的递归算法,从0数到指定n ✅作业12:画出一个原型链,到 null 为止 ✅作业8:ES6新增了两个创建数组的静态方法:Array.from()和Array.of()两个有什么区别? ❌作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景

LTN1 【推荐做题时间 02-23 - 10题 实际做题时间: 2.23】 ✅2021.07 防抖节流 ✅2024.12 模拟实现jsonp ❌作业70:Vue 和 React 的 Diff 算法比较 ❌作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ❌2023.07 clickhouse、mysql、mongodb异同 ✅作业4:什么是生成器?有什么作用? ❌2021.07 12-1 内存泄漏 ❌2021.06 flex布局 ❌作业99:渲染合成层定义、触发方式、优点、缺点

LTN3 【推荐做题时间 02-23 - 6题 实际做题时间: 2.23】 ✅作业49:牛顿三大定律、热力学两大定律 ✅作业54:举杯邀明月,对影成三人。的上一句 ✅作业77:重绘和重排是什么?有什么区别? ✅作业79:地球四季的成因是什么? ❌2019.07 显示省略号 ✅2021.04 25 HTTP/2 特性

LTN1 【推荐做题时间 02-24 - 5题 实际做题时间: 2.24】 ✅作业23:写出js 代码的执行顺序。词法作用域的特点是什么?

function bar(){
    console.log(myName);
}
function foo(){
    var myName = 'hi 坑';
    bar();
}
var myName = 'bye 坑';
foo();

❌ 2021.07 babel 作用+原理+补丁 ❌ 2021.05 27 HTTP/3 改进的点 ❌2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程 ❌2021.04 12 垃圾回收机制

LTN2 【推荐做题时间 02-24 - 2题 实际做题时间: 2.24】 ✅作业6:new操作符实例化一个对象的过程 ❌作业66:React如何处理错误

LTN2 【推荐做题时间 02-25 - 3题 实际做题时间: 2.24】 ✅作业3:迭代器原理 ✅作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果

Promise.resolve().then(() => {
    console.log(1);
    Promise.resolve().then(() => {
        console.log(2);
    }).then(() => {
        console.log(3);
    }).then(() => {
        console.log(4);
    })
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
}) 
    ------
async1 = async () => {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
function async2(){
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
})
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
    return new Promise(function(resolve) {
        resolve();
    })
}).then(() => {
    console.log('promise3');
})
console.log('script end');

❌2021.06 子盒子在父盒子中水平垂直居中有几种方法?


做题记录

// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第四遍 2025.2.24 -
1flex .father{ display:flex; justify-content:center; align-iterms:center; }
2position .son{ position:absolute; top:0; right:0; botton:0; left:0; margin:auto; }
3position .son{ position:absolute; top:50%; left:50%; transform:translate(-50%, -50%); }
4table .father{ display:table-cell; vertical-align: middle; text-align: center; }
 // 1、table 少写了 .son{ display: inline-block }
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
- 第一遍 2025.1.30 -
 .son { display: table-cell; text-align: center; vertial-align: middle; } 
 // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中 .father { display: table-cell; text-align: center; vertical-align: middle; } .son { display: inline-block; }
// 作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果
- 第四遍 2025.2.24 -
事件循环系统 - 同步 - 主线程 - 调用栈清空前查询事件队列函数进行执行 - 循环事件队列执行完毕
		   - 异步 - Event Table - 异步事件有结果后将回调函数放入事件队列
宏任务IO下载交互UI渲染setTimeout/setIntervaljs代码执行
微任务Promise.thenasync/await、MutationObserver
125364
script start - async1 start - async2 - promise1 - script end
    - async1 end - promise2 - promise3
    - setTimeout
- 第三遍 2025.2.11 -
- 第二遍 2025.2.9 -
⭐️ // Event Queue + 触发时机 - 等异步任务有了结果,将回调函数投入事件队列
- 第一遍 2025.1.30 -
⭐️ // Event Queue,等异步任务有了结果,将回调函数投入事件队列
⭐️ // IO表示下载和交互
1 5 2 6 3 4  // 125364 
// 作业3:迭代器原理
- 第五遍 2025.2.24 -
使用 next 遍历可迭代对象不关心变量类型
- 第四遍 2025.2.11 -
- 第三遍 2025.2.9 -
- 第二遍 2025.1.30 -
提供暂停和恢复代码执行能力[Symbol.iterater] 默认迭代器
yield 暂停next 恢复执行 {done, value}
 // 迭代器使用 next 方法在可迭代对象中遍历数据
- 第一遍 2024.12.25 -
// 作业66:React如何处理错误
- 第九遍 2025.2.24 -
React 提供了两个错误函数用于处理 render  commit 中的错误避免错误溢出应用程序影响 UI 展示
static getStateFromError + componentDidCatch
 // 1、函数名称错误:getDerivedStateFromError 
错误边界
class Boundaries extends React.component {
    constructor(props){
        super(props);
        this.state = {hasError: false};
    }
    static getStateFromError() {  // 1、函数名称错误:getDerivedStateFromError
        return {hasError: true};
    }
    componentDidCatch(e, info){
		console.log(e)
    }
    render() {
        if(this.state.hasError) {
            return <div> There are errs. </div>
        } else {
            return this.props.children;
        }
    }
}
⭐️ // <ErrorBoundaries> <My /> </ErrorBoundaries>
- 第八遍 2025.2.10 -
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
React 提供两个函数处理错误 static getStateFromError()componentDidCatch用于捕获 
 // 1、静态函数名称错了 getDerivedStateFromError
render+commit 阶段的错误阻止错误蔓延到整个应用影响 UI 展示
几个情况不会捕获异步回调ssr错误边界内部
class Boundaries extends React.Component{
    constructor() {
        super(props);
        this.state = { hasError: false }
    }
    static getStateFromError() {  // 2、getDerivedStateFromError
        return { hasError: true }
    }
    componentDidCatch(e, info) {
        console.log(e)
    }
    render() {
        if(this.state.hasError) {
            return <p> There are errs. </p>
        } else {
            return this.props.children;
        }
    }
}
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 作业6:new操作符实例化一个对象的过程
- 第一遍 2025.2.24 -
function newF() {
	const ctor = [].shift.call(arguments);
    const obj = new Object();
    obj.__proto__ = ctor.prototype;
    const res = ctor.call(obj, ...arguments);
    return typeof res === 'object' ? res : obj
}
- 第四遍 2025.2.10 -
- 第三遍 2025.2.9 -
- 第二遍 1.28 -
function newFun () {
    let obj = new Object();
    const ctor = [].shift.call(arguments);
    obj.__proto__ = ctor;  // 1、不是构造函数,是构造函数的原型对象 obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
	return typeof res === 'object' ? res : obj;
}
- 第一遍 1.20 -
 // 1、不是调整 constructor 的指向
// 2021.04 12 垃圾回收机制
- 第一遍 2025.2.24 -
 // 1、少写了栈的清理:指针移动 - 不需要的数据所在的内存被覆盖
v8 垃圾回收机制标记清除之前使用循环计数无法清理循环调用
 // 2、少写了:v8 引擎 基于代际假说
分为新生区和老生区 ⭐️ // 新生代 - 副垃圾回收机制,老生代 - 主垃圾回收机制(不产生内存碎片)
- 新生区内存占用小新生区清理两次都没有被清理的变量提升到老生区
	划分为两部分前和后后存储到一半后触发清理 - 先清理清理的过程中已经包含了移动清理完交换
 // 3、划分区域名错误:新生代 - 副垃圾回收器,划分为 对象区域、空闲区域;清理时机错误:对象区域快满时;清理过程错误:标记 - 将存活的对象有序地复制到空闲区 - 复制完后翻转两个区域
- 老生区内存占用空间大
	先清理再移动
 // 4、少写了算法:标记-清理,产生大量不连续的内存碎片
// 2025.02 nextTick 原理,在Vue2、Vue3中分别是什么步骤实现的,简单模拟实现流程
- 第一遍 2025.2.24 -
nextTick 原理有些操作需要在 DOM 更新后操作 ⭐️ // 在下次 DOM 更新循环结束之后,执行延迟回调
Vue2将回调函数放到事件队列-微任务执行完一次 DOM 元素的更新后执行事件队列
 // 1、Vue 的数据更新是异步执行,修改数据后,Vue 不会立即更新 DOM,而是放到队列中等到下一个事件循环时再批量更新 - 需要 nextTick 来确保在 DOM 更新后再执行
 // 2、Vue2 的步骤:①回调函数收集到队列 ②异步执行方式选择:根据浏览器环境选择合适的异步执行方式,优先微任务 Promise/MutationObserver,如果不支持微任务则使用宏任务 setTimeout ③触发异步执行:在下次DOM 更新结束后执行
Vue3将回调函数使用 MicroQueue 实现
 // 3、Vue3:和Vue差异是,使用 queueMicrotask 统一处理微任务
模拟
function nextTick(fn){
    if(typeof MicroEvent === 'function') {
        return new MircoEvent(fn);
    }
    if(typeof setTimeout === 'function') {
        return setTimeout(fn, 0)
    }
}
 /* 4、Vue2
const callbacks = []; 		// 异步函数收集队列
let pending = false;  		// 标记是否正在执行回调
function flushCallbacks() { // 执行所有回调函数 flushCallbacks
	pending = false;
	const copies = callbacks.slice(0);
	copies.forEach(it => it());
}

let timeFunc; // step2、根据浏览器支持情况,选择异步执行方式
if(typeof Promise !== 'undefined') { // 浏览器支持 Promise 微任务
	const p = Promise.resolve();
	timeFunc = () => { p.then(flushCallbacks) };
} esle if(typeof MutationObserver !== 'undefined'){ // 浏览器支持 MutationObserver 微任务
	const obs = new MutationObserver(flushCallbacks);
	const textNode = document.createTextNode('1');
	obs.observe(textNode, { characterData: true }); // 监听目标节点的文本内容变化
	timeFunc = () => { textNode.data = '2' }
} else if(typeof setImmediate !== 'undefined'){ // 浏览器支持 setImmediate 宏任务
	timeFunc = () => { setImmediate(flushCallbacks) }
} else {
	timeFunc = () => { setTimeout(flushCallbacks, 0) } // 浏览器默认 setTimeout 宏任务
}

funtion nextTick(cb, ctx) {
	callbacks.push(() => { // step1、收集到异步函数收集队列
		if(cb) { 
			try{ fn.call(ctx) }catch(e){ console.log(e) } 
		}
	})
	if(!pending) {
		pending = true;
		timeFuc();  // step3、执行异步
	}
}

new Vue({
	data() { return {msg:'hello'} },
	mounted() { this.$nextTick(() => { console.log('DOM 已更新') }) }
})
**/
/* 5、Vue3
const queue = [];			// 异步函数收集队列
let isFlushing = false;		// 标记是否正在执行回调
function flushJobs() {
	isFlushing = true;
	let job;
	while((job=queue.shift())){
		job();
	}
	isFlushing = false;
}
function nextTick(fn) {
	return new Promise((resolve) => {
		const runner = () => { 
			if(fn) {
				try{ fn() }catch(e){ console.error(e) }
			}
			resolve();
        }
        queue.push(runner); // step1、收集到异步函数收集队列
        if(!isFlushing){ queueMicrotask(flushJobs) } // step3、执行异步
	})
}

import { createApp, nextTick } from 'vue';
const app = createApp({
	data() { return {msg:'hello'} },
	mounted() { nextTick(() => { console.log('DOM 已经更新') }) }
})
app.mount('#app')
**
// 2021.05 27 HTTP/3 改进的点
- 第一遍 2025.2.24 -
不再使用 HTTP/2.0  TCP 协议改为 UDP并使用 QUIC协议 ⭐️ // 完全解决了队头阻塞
 // 1、少写了 HTTP/2.0 存在的问题:HTTP/2.0 并没有完全解决队头阻塞,传输层的 TCP 有丢包重传机制 - TCP 为了保证可靠的传输,丢包必须等待重新传输确认,其他的包即使收到了也只能放在缓冲区,上层应用拿不出来,被丢的包不回来,大家都取不出来
 // 2、少写了:HTTP/3.0 UDP 无连接,HTTP/2.0 TCP 三次握手、四次挥手
// 2021.07 babel  作用+原理+补丁
- 第一遍 2025.2.24 -
作用babel  ES6 代码转换成 ES5 代码方便引擎解析执行 
原理babel 先将 ES6 代码解析成 ES6 AST 再在 AST 层面转换成 ES5 AST再转换成 ES5 代码
 // 1、原理:少写了解析过程:解析Parsing[词法分析+语法分析得到AST]-转换Transformation[遍历AST转换 ES6的AST转换成ES5的AST]-生成Code Generation[根据转换后的AST转换成ES5 js代码]
补丁babel 只能将 ES6 的常见语法转换例如箭头函数等其他的例如 Promiseasync/await 等需要通过其他补丁完成,babel 的强大功能都是由补丁来完成的 // 2、不能转换的ES6语法:Iterator Generator Set Map Proxy Reflect Symbol 等全局对象 + Object.assign 全局对象上的方法;@babel/polyfill
// 作业23:写出js 代码的执行顺序。词法作用域的特点是什么?
- 第二遍 2025.2.24 -
bye 
js  可执行代码 AST
	执行上下文 - this 在函数环境中先创建 arguments再函数提升再变量提升
			 - 变量环境 var 作用域链由多个执行上下文的变量环境组成的 ⭐️ // 链表
			 - 词法环境 let/const
词法作用域只和代码所在位置有关
- 第一遍 2025.1.20 -
// 2021.04 25 HTTP/2 特性
- 第四遍 2025.2.23 -
 HTTP/1.1 语义层面保留语法层面大力改造
1压缩头部信息 ⭐️ // HPACK字典表算法
2不再使用报文使用二进制帧 - 双向传输序列形成流多路复用-在一个连接中可以处理多个请求响应多个请求响应之间不再有顺序关系解决队头阻塞
3服务器主动推送
4设置请求优先级
5采用 HTTPS
- 第三遍 2025.2.2 -
- 第一遍 -
 // 1、少写一个特性:服务器推送
// 2019.07 显示省略号
- 第四遍 2025.2.23 -
.line {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.lines {
    overflow: hidden;
    display: -webkit-box; 
    -webkit-box-align: vertical;  // 1、-webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    
    padding: 10px;
    line-height: 20px;
    line: 20*3-10px;
}
- 第三遍 2025.2.2 -
- 第一遍 -
 // 1、css 属性名错误:-webkit-box-orient: vertical;
// 作业79:地球四季的成因是什么?
- 第三遍 2025.2.23 -
和地球与太阳的距离无关只和太阳与地球表面的夹角-太阳光穿过的大气层厚度  地面照射的面积
- 第二遍 2025.2.2 -
⭐️ // 直射点的移动 + 太阳照射时长 - 夹角和地面角度的改变,改变了太阳辐射面积和经过大气层的路程衰减的辐射能量强度
// 作业77:重绘和重排是什么?有什么区别?
- 第三遍 2025.2.23 -
重绘只修改样式颜色不影响布局只需要重新渲染 ⭐️ // 只需要修改 分层+合成
重排修改字体大小影响布局 ⭐️ // 修改 布局+分层+合成,字体大小、padding/margin、激活伪类、style样式的修改、resize、页面初始渲染、DOM增删、位置内容的修改、DOM布局查询
- 第二遍 2025.2.2 -
// 作业54:举杯邀明月,对影成三人。的上一句
- 第四遍 2025.2.23 -
花间一壶酒独酌无相亲
- 第三遍 2025.2.2 -
// 作业49:牛顿三大定律、热力学两大定律
- 第四遍 2025.2.23 -
牛顿三大定律 惯性定律加速度定律 a=F/m作用力与反作用力
热力学两大定律 能量守恒定律 熵增原理(自然系统会自动失序)
- 第三遍 2025.2.2 -
- 第一遍 -
 // 1、少写了惯性定律 - 公交车记忆:刹车-加速度、乘客-惯性、地上痕迹-摩擦
// 作业99:渲染合成层定义、触发方式、优点、缺点
- 第一遍 2025.2.23 -
定义DOM-CSS-布局-分层here-绘制-栅格化-合成 将CSS样式分成不同层级 
 // 1、少写了:渲染时,为了提高渲染效率和性能,将页面中不同元素划分成多个层,每层都有独立的绘制、处理流程 - 这就是渲染合成层
触发方式will-change opacity tranform  创建一层  // 2、少写了 3D 变化、视频/canvas
优点修改部分样式的时候只需要修改当前层渲染效率更高
 // 3、少写了:减少不必要的回流重绘 + 实现流程流畅的动画效果,合成层可以在 GPU 上加速
缺点分层越多内存占用越大合成时也会消耗性能
// 2021.06 flex布局
- 第一遍 2025.2.23 -
独立容器 从上至下垂直部署 计算高度时包含浮动元素 不和外部浮动元素重叠
 主轴  交叉轴
flex: flex-grow 在主轴放大 flex-shrink 在主轴缩小 flex-basis auto-元素本身大小0-根据整体 ⭐️ // flex-grow 正整数以 flex-basis 为基础
 /**
	1、优势:从 排列 + 空间分配 + 布局 描述
		便捷的元素排列 flex-direction - 水平、垂直、水平反转、垂直反转
		空间分配 flex 三个简写属性 + 主轴对齐方式 justify-Content + 交叉轴对齐方式 align-iterms
	2、常见默认属性
		主轴默认不拉伸,但默认缩小;交叉轴默认拉伸(align-items:stretch)
		-	space-between 元素排列后,再分配 [a--b--c]
		-	space-around 每个元素左右空间都相等 [-a--b--c-]
		flex 导致 float clear vertical-align 失效
		align-self 允许单个项目与其他不一样的对齐方式,可覆盖 align-items
*/
// 2021.07 12-1 内存泄漏
- 第一遍 2025.2.23 -
内存泄漏没有被使用的活动对象却没有被清理
内存 Chrome Memory 录制查看
常见内存泄漏闭包引用全局变量监听器定时任务 ⭐️ // 少写了 弱引用 WeakMap/WeakSet
 /** 1、 如何避免内存泄漏:
	①尽可能少创建 闭包、全局变量 
	②手动清理定时器 clearInterval/clearTimeout 
	③手动移除监听器 removeEventListener .off() 
	④使用弱引用 WeakMap/WeakSet 或者 手动删除 Map、Set 的键/值 s.delete(value) m.delete(key)*/
// 作业4:什么是生成器?有什么作用?
- 第四遍 2025.2.23 -
生成器 可以使用yield暂停和恢复代码执行函数 Function * 声明使用 next:{done, value} 遍历可迭代对象
模拟协程自定义可迭代对象
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
 // 1、少写了:暂停、恢复代码执行,* 声明
- 第一遍 2025.1.30 -
// 2023.07 clickhouse、mysql、mongodb异同
- 第六遍 2025.2.23 -
mysql 关联数据库读写性能好并发操作时会出现数据一致性问题支持事务操作 ⭐️ // 高并发
mongodb 文档型数据库读的性能好写的性能弱分布式操作时会出现数据一致性问题支持非结构型类型/半结构型数据 XMLHTML ⭐️ // JSON
clickhouse 列式数据库读写性能好适合高吞吐低延迟的场景  // 1、少写了:不支持事务,且支持分布式操作
- 第五遍 2025.2.18 -
 // 1、mysql 少写了:支持事务操作
 // 2、clickhouse 少写了:不支持事务
- 第四遍 2025.2.16 -
 // 1、mysql 少写了:支持事务操作;高并发时出现数据一致性问题
 // 2、mongodb 少写了:在分布式场景时出现数据一致性问题
 // 3、clickhouse 少写了:列式数据库 + 不支持事务,支持分布式查询 + 适合高吞吐、低延迟的场景
- 第三遍 2025.1.30 -
⭐️ // clickhouse:高吞吐、低延迟,支持复制、分布查询 + mongodb:分布式场景下一致性问题
- 第二遍 2025.1.25 -
 // 1、mongodb:在分布式场景下可能出现一致性问题 ;适合半结构化/非结构化数据
- 第一遍 2025.1.22 -
// 作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询
- 第六遍 2025.2.23 -
INNER JOIN 内联表取AB表交集
LEFT OUTER JOIN A完全满足的条件行 + 补充所有相关的B的数据
RIGHT OUTER JOIN B 完全满足的条件行 + 补充所有相关A的数据
UNION 组合两次查询 ⭐️ // 拼接
封装 CREATE VIEW my AS SELECT...
SELECT * FROM my
- 第五遍 2025.2.18 -
封装 CREATE VIEW my ON SELECT * FROM table  // 1、封装:关键词不是 ON 是 AS
- 第四遍 2025.2.16 -
 // 1、INNER JOIN 不是合集,是交集,表达错误 - AB都满足的行,内连接
CREATE VIEW my ON SELECT * FROM table  // 2、封装:关键词不是 ON 是 AS
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
 // 1、少了一个 AS:封装 CREATE VIEW ... AS
- 第一遍 2025.1.22 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第六遍 2025.2.23 -
Redux  react 的状态保存在 store 的对象树中只能通过 dispatch action 改变 state
reducer 通过 oldState  action 计算出新的 state
RTK react toolkit - 官方推荐工具包
import { createStore } from 'redux';
const reducer = (oldState={v:1}, action) => {if(action.type) { return {v: oldState.v+1} }}
const store = createStore(reducer);  // 1、 函数名称错了:getState
store.dispatch({type: true});
store.subscribe(() => console.log(store.getValue()))
- 第五遍 2025.2.18 -
store.subscribe(() => {console.log(store.getValue())})  // 1、 函数名称错了:getState
- 第四遍 2025.2.15 -
 // 1、注意 store 是对象树:Redux 将全局状态保存在 store 对象树中
⭐️ // reducer:返回新的 state 而不是改变
 // 2、reducer 参数顺序是 oldState,action
 // 3、 函数名称错了:getState,通过 subscribe 更新 UI
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
// 作业70:Vue 和 React 的 Diff 算法比较
- 第九遍 2025.2.23 -
Vue 双端指针比较算法 -> 匹配不到查找 同key+同标签 -> 索引是否一致一致则复用不一致 -> 再修改标签移动或者重新渲染  // 1、Diff 跳过静态节点 + Vue 是组件级别的更新
React 分层同标签优先比较 -> 不同标签直接重新渲染 -> 同标签修改属性复用 ->  key 移动
 // 2、分层比较 + 同类型优先比较 + React 是节点属性级别的更新 + DFS 深度优先遍历子节点,逐个比较
- 第八遍 2025.2.18 - 
- 第七遍 2025.2.16 -
 // 1、Vue 少写了:静态节点跳过;双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
- 第五遍 2025.1.25 -
 // 1、Vue 少写了:静态节点跳过 ;Vue 是基于组件级别的更新算法
 // 2、React 少写了:是基于单个节点属性级别的,适合大型动态复杂场景
- 第四遍 2025.1.24 -
 // 1、Vue:找不到匹配的点后,再找到同类型+同 key的节点,进行索引比较,一致则复用,不一致则移动到新位置修改使用
- 第三遍 2025.1.18 -
 // 1、Vue:静态节点跳过比较 - 不要遗漏了,是个很重要的性能优化
 // 2、Vue:双端指针比较后,还有找不到的 -> 找同类型+同key来比较
- 第二遍 2025.1.13 -
⭐️ // React:「列表组件,根据 key 确定新老节点关系」
 // 1、Vue:双端比较算法是直接找匹配节点,不进行 标签修改
 // 2、Vue:key 是第三层比较逻辑,第二层比较逻辑是「索引」 - 若索引没有变化,则不更新当前节点;如果索引有变化,判断是否「同类型 + 同key」,移动到新位置
 // 3、Vue:性能优化,编译时标记的静态节点跳过 - 虽然老生常谈,但是这其实是一大优化
- 第一遍 -
// 2024.12 模拟实现jsonp
- 第五遍 2025.2.23 -
function jsonp(url, params, callback) {
    const suffix = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&') + `&callback=${callback}`;
    const totalUrl = url + url.includes('?')?'&':'?' + suffix;
	const dom = document.createElement('script');
    window[callback] = function(data) {
        callback(data);
		document.body.removeChild(dom);
    }
    dom.src = totalUrl;
    document.body.appendChild(dom);
}
- 第四遍 2025.2.18 -
- 第三遍 2025.2.16 -
const suffix = Object.keys(params).reduce((pre, cur) => pre + `${cur}=${params[cur]}`, ''); 
 // 1、计算 url 时少了 '&' -> 使用 map + join 更简洁:Object.keys(params).map(it => `${it}=${params[it]}`).join('&') -> 使用 entries 也可以 Object.entries(params).map(([key, vlaue]) => `${key}=${value}`).join('&')
- 第二遍 2025.1.21 -
- 第一遍 -
// 2021.07 防抖节流
- 第五遍 2025.2.23 -
function debounce(fn, delay) {
    let timeId;
    return (...args) => {
        if(timeId) {
            clearTimeout(timeId);
        }
        timeId = setTimeout(() => {
            fn(...args);
        }, delay);
    }
}
function throttle(fn, delay) {
    let flag = false;
    return (...args) => {
        if(flag) {
            return;
        }
        flag = true;
        fn(...args);
        setTimeout(() => {
            flag = false;
        }, delay)
    }
}
- 第四遍 2025.2.18 -
- 第三遍 2025.2.16 -
 // 1、debounce 遗漏赋值:timeId = setTimeout()
⭐️ // throttle:间隔多少秒执行一次
 // 2、throttle:判断条件应该是 if(flag){return}:flag 为 true 时,证明这段时间内已经执行了
- 第二遍 2025.1.21 -
- 第一遍 -
// 作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景
- 第二遍 2025.2.23 -
WeakMap/WeakSet 的属性是对象没有引用时垃圾回收机制可以清理空对象的引用 ⭐️ // 键/值
MapSet 不被垃圾回收机制清理 ⭐️ // 避免内存泄漏
场景私有变量例如属性引用 DOM一旦DOM被删除相关 WeakMap/WeakSet 就会被删除
 // 1、Map 和 Set 对存储的对象持有「强引用」,只要它们存在,对象就不会被回收 - 强引用意味着只要 Map 或 Set 存在,并且其中存储了某个对象,那么这个对象就不会被垃圾回收。即使在其他地方没有对该对象的引用,Map 或 Set 内部的引用会阻止垃圾回收器将其回收
 // 2、而 WeakMap 和 WeakSet 对存储的对象持有「弱引用」,不会阻止对象被垃圾回收
- 第一遍 2024.12.25 -
// 作业8:ES6新增了两个创建数组的静态方法:Array.from()和Array.of()两个有什么区别?
- 第二遍 2025.2.23 -
Array.from 类数组arguments+有indexlength属性的对象 NodeList)、可迭代对象 转换成数组
	- 浅拷贝
Array.of 一系列参数 转换成数组 ⭐️ // Array.prototype.slice.call(arguments)
- 第一遍 2024.12.25 -
// 作业12:画出一个原型链,到 null 为止
- 第二遍 2025.2.23 -
const person = new Person();
person.__proto__ -> Person.prototype
	Person.prototype.constructor -> Person
	Person.prototype.__proto__ -> Object.prototype
Object.prototype.constructor -> Object
Object.prototype.__proto__ -> null
- 第一遍 2024.12.25 -
// 作业11:写出一个用 yield 实现的递归算法,从0数到指定n
- 第二遍 2025.2.22 -
function nTimes(n){  // 1、yield 只能在生成函数中使用 * -> 少写了*号
    if(n===0){  // 2、的确是 n > 0 才需要继续递归 -> 但是这么写,不是 next 的{value,done}格式,调用迭代器调用拿不到 0 这个值
        return n
    }
    yield * nTimes(n-1)
	 // 3、少写了 yield n-1 -> 上面的 yields * 是把 nTimes 进行一一迭代,yield 才是传递的每个迭代里的具体值
}
for(let i of nTimes(3)){
    console.log(i)
}
- 第一遍 2024.12.25 -
function * nTimes(n) {
    if(n>0) {
        yield* nTimes(n-1);
        yield (n-1);
    }
}
// 作业9:数组自带排序 sort() 为什么经常要传比较函数?写一个降序的比较函数。如果数组元素全是数值,请给出简写形式
- 第二遍 2025.2.22 -
sort 默认会把数组的每一项转换成字符串后比较会出现结果 [1,10,15,2,3,4]
sort((a, b) => return a>b ? -1 : a<b ? 1 : 0)
sort((a, b) => b-a)
- 第一遍 2024.12.25 -
// 作业7:原始值包装类型和原始类型在使用 typeof 和 instanceof 进行类型判断时,有什么异同?
- 第二遍 2025.2.22 -
原始值包装类型 typeof x - 一直得到 "object" x instanceof String/Boolean/Number - true
原始类型 typeof x - 'strting'/'number'/'boolean' x instanceof String/Boolean/Number 一直false
- 第一遍 2024.12.25 -
// 作业1:写一个只能被迭代指定次数的类
- 第二遍 2025.2.22 -
 // 1、写成 nTimes 没有读题
class count {
    constructor(n) {
        this.limit = n;
    }
    [Symbol.iterator]() {
        let limit = this.limit;
        let current = 0;
        return {
            next() {
				if(current < limit) {
                    return {value: current++, done: false}
                } else {
                    return {value: undefined, done: true}
                }
            }
            return() {
                return {value: undefined, done: true}
            }
        }
    }
}
- 第一遍 2024.12.25 -
// 答题核心:迭代器返回的是迭代器对象(包含next()、return()) + next 返回一个 done+value组成的对象
// 作业 89:mysql 中类型的区别:varchar 和 char,date datetime
- 第二遍 2025.2.22 -
varchar(255) 固定长度存储即使不固定也会补全 8字节
char(255) 按照内容长度存储
 // 1、varchar 可变长度存储;char 固定长度存储,不够则填充空格;tinytext 255字符;text 不限长度
date YYYY-MM-DD ⭐️ // 3字节
datetime YYYY-MM-DD HH-MM-SS ⭐️ // 8字节
- 第一遍 2025.2.8 -
⭐️ // char 内容长度不够时,使用空格填充到指定长度
// 作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?
- 第四遍 2025.2.22 -
arguments 的值默认同步命名参数但是不同步设置了默认值的命名参数
数组arguments = [[1,2,3]] -> 扩展运算符 (...arr) -> arguments = [1,2,3]
独立参数 (first, ...rest) => ...rest 作为最后一项
对象字面量 {...obj} 创建一个新对象复制所有可枚举属性
数组字面量 [...arr] 一个一个解析可迭代对象
- 第三遍 2025.2.8 - 
- 第一遍 -
 // 1、数组直接传入的 arguments 错了,应该是 [[1,2,3]]
 // 2、{...obj} 作用描述有问题,复制的是 可枚举属性
// 2019.07 h5新标签和语义化,块/行内元素
- 第四遍 2025.2.22 -
1SEO 优化 2标签样式不同
新标签header footer aside address article detail hgroup time datalist filedset section nav 
input 新属性placeholder required multiple autocomplete autofocus ⭐️ // accesskey
input type 新属性tel url number date week month datetime search range email ⭐️ // time
块元素 h1-h6 div hr p section table table-caption ul li dd dt ol ⭐️ // td tr address 
行内元素 br span select textarea button ⭐️ // i u a strong em b label
行内块元素 td img input
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
1SEO 优化 2标签样式不一样
 // 1、新标签少了:datalist detail time hgroup 
 // 2、input 属性少了:accesskey 
 // 3、input type 属性少了:url range search date time datetime week month 
 // 4、br - 错了,不是块元素,而是行内元素 + 块元素少了 h1-h6 td ol table-caption tr address hr
 // 5、行内元素少了:br i u a strong em button b label
- 第一遍 2025.1.9 -
// 2024.12 模拟实现instanceof
- 第四遍 2025.2.22 -
function fakeInstanceOf(target, ctor) {
    let proto = target.__proto__; // Object.getPrototype(target)
    while(proto) {
        if(proto === ctor.prototype) {
            return true
        }
        proto = proto.__proto__;
    }
    return false
}
- 第三遍 2025.2.18 -
- 第二遍 2025.2.15 -
 // 1、获取 target 的原型链->应该是 target.__proto__ 或者 Object.getPrototype(target)
 // 2、判断原型链上是否和构造函数的原型对象相等,而不是和构造函数相等 proto===Ctor.prototype
 // 3、不停往上找原型链 -> 通过 __proto__或者getPrototype找,而不是 prototype 
- 第一遍 2025.1.9 -
// 作业45:原生+canvas 显示一张png格式的图片
- 第四遍 2025.2.22 -
<canvas id='drawing' width=200 height=200></canvas>
const drawing = document.getElementById('drawing');
if(drawing.getContext){
    const imgData = drawing.toDataUrl('image/png');
    const dom = document.createElement('img');
    dom.src = imgData;
    document.body.appendChild(dom)
}
- 第三遍 2025.2.18 -
- 第二遍 2025.2.14 -
 // 1、需要判断是否存在 context 作为大前提:if(drawing.getContext){}
 // 2、image 标签添加 url 的属性是 src:image.src = imageData;
- 第一遍 2025.1.9 -
// 2019.07 为什么要清除浮动?怎么清除浮动?
- 第六遍 2025.2.22 -
块元素把浮动元素当做不存在行内元素围绕浮动元素子元素浮动时父元素高度塌陷无法撑开背景
清除给父元素添加 高度 父元素最后一个子盒子后添加 box .box{clear:both} 父盒子伪元素 .father::after{ content:''; display: block; clear: both; } BFC .father{overflow: hidden}⭐️ // BFC 的盒子计算高度时包含浮动元素
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、父元素+伪元素 应该是 display: block,否则撑不开父级全部宽度
- 第三遍 1.28 -
- 第二遍 1.25 -
 // 1、高度塌陷的描述:背景无法撑开 + padding、border 无法正常展示
 // 2、给父元素添加高度:宽度不对,高度+padding+border
- 第一遍 1.20 -
// 作业2:支持迭代器的原生语言特性有哪些?
- 第六遍 2025.2.22 -
for-of Array.from ...扩展运算符 数组解构赋值 new Map/new Set yield * Promise.race/all
- 第五遍 2025.2.18 -
- 第四遍 2025.2.15 -
 // 1、少写了 yield * 只能在生成器中使用
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
// 2025.02 Nextjs 获取链接中的参数
- 第一遍 2025.2.22 -
import { useParamsUrl } from 'next/navigation';
 // 1、核心函数的名称记错:useSearchParams
const date = useParamsUrl().get('date');

import { useParamsUrl } from 'next/route';  // 2、另一个依赖包是 react-route-dom
const [params, useParams] = useParamsUrl();
 // 3、应该是 params 和 setParams
const date  = params.get('date');
 // 4、使用 useSearchParams 需要配套 <Suspense> 在 layout.tsx 处理路由的异步加载
export default function RootLayout({children}:ReadOnly<{
                                    children: React.ReactNode
                                    }>){
    return (<html lang='en'>
            	<body>
            		<Suspense fallback={<div>Loading...</div>}>
            			{children}
            		</Suspense>
            	</body>
            </html>)
}
// 2025.02 Mysql 8.0 以上,Sequel报插件错
- 第一遍 2025.2.22 -
本地使用 mysql 安装包下载dmg 安装时选择插件 ⭐️ // Use Legacy Password Encryption
线上指令切换 mysql   // 1、将 root 用户的认证插件修改为 mysql_native_password
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第三遍 2025.2.22 -
先给两张表创建关联关系再在查询的时候使用 includes
A.hasMany(B, {
    foreignKey: B_id,
    sourceKey: A_id
})
B.belongsTo(A, {
    foreignKey: B_id,
    targetKey: A_id
})
const res = A.findAll({  // 1、查询需要 await
    where1,
    includes: [{  // 2、查询关键词是 include
        table: B,  // 3、include 关联表是 model 来声明
        where2
    }]
})
- 第二遍 2025.2.17 -
 // 1、查询不会 await A.findAll({ include: [{ model:B }] })
- 第一遍 2025.2.13 -
 // 1、关键词记错了:belongsTo
 // 2、查询不会 await A.findAll({ include: [{ model: B }] })
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第三遍 2025.2.22 -
const [issue, created] = await table.findOrCreated(data, where);
 // 1、findOrCreated 参数错误 findOrCreated({ where, defaults: data })
if(!created){
    await issue.set(data);  // 2、set 不需要 await
    await issue.save()
}
- 第二遍 2025.2.17 -
 // 1、findOrCreate 的数据源属性名记错 defaults
 // 2、先set数据,再保存 issue.set(data); await issue.save();
- 第一遍 2025.2.13 -
 // 1、需要获取函数的返回值,获取是否为新创建 const [issue, created]
 // 2、数据源没有传入 defaults: data
 // 3、如果已经存在,更新描述需要单独处理
// 作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?
- 第三遍 2025.2.22 -
法1
const start = dayjs.utc(new Date(time)).startOf('day').toDate();
const end = dayjs.utc(new Date(time)).endOf('day').toDate();
const res = await table.findAll({
    where: { date: {
        [op.betwwen]: [start, end]
    } }
})
法2
const where = sequelize.where(
	sequelize.fn('DATE', sequelize.col('date')),
    ⭐️ // sequelize.fn('DATE_FORMAT', sequelize.col('date'), '%Y-%M-%D')
    '=',
    time
)
索引数据表用来快速检索的树结构单独存储于表之外法1可以借用索引优化索引就是查找的时候不是全表遍历而是根据索引先查找
⭐️ // 索引可以快速定位到查询条件的数据位置,从而减少 IO 操作
- 第二遍 2025.2.17 -
 // 1、函数式写法,少写了 sequelize.where 
- 第一遍 2025.2.13 -
 // 1、方法一:需要先将传入的 string 转换成 date + 还需要处理成 Date 类型查询
 // 2、方法二:sequelize.fn 两个函数不清晰
 // 3、索引不清晰
// 2021-07 常见Webpack问题
/** 1、webpack的几个核心概念理解:Chunk、Module、plugin
2、常见配置项:entry、output、module、resolve等
自定义 loader 怎么配置?
3、Code Splitting 和 Tree Shaking 的区别?懒加载怎么实现?
4、html-webpack-plugin 作用?
5、sourceMap不同环境的区别?怎么开启配置?
6、热更新怎么实现?
7、webpack原理/执行过程?
开发插件的桥梁?*/
- 第四遍 2025.2.21 -
1Chunk 由多个模块组成由entry分组Module万物皆模块plugin插件在构建的特定时机注入代码
2entry:'main.ts'/{main:'main.ts', sub:'sub.ts'}
output:{filename:'[name]-[hash].js', path: path.resolve(_dirname, 'dist')}
module:模块解析规则{rules: [
    {test:/.\js/, use:['my-loader']},
    {test:/.\css/, use: ['style-loader', 'css-loader', 'less-loader']}
]}
resolve: 模块查找规则 {
    alias: {'@c': './src/component'},
	extensions: ['ts', 'js', 'json'],
	modules: ['node_modules', path.resolve(_dirname, './src/myLoader')]
}
自定义 loader 
  module.rules use 时直接使用具体地址 use:[path.resolve(_dirname, './src/myLoader/my-loader')]
  resolve.modules 查找配置中写明地址 +  module.rules 直接使用 loader如上
3Code Splitting按需加载 entry 分组加载页面时只加载当前需要使用的部分提高渲染效率 ⭐️ // splitChunk 插件开启配置
Tree Shaking按需打包 entry 分组只打包相关代码减小包体积 ⭐️ // 默认开启
懒加载利用 code splitting首次渲染的时候按需加载提高渲染效率
4html-webpack-plugin  webpack 打包的 js css 等引入 html 文件可以直接访问
5sourceMap
devtool: eval-cheap-source-map; 本地 ⭐️ // cheap-eval-source-map
devtool: hidden-source-map; 在线
6热更新  webpack 启动时dev-server 在内存中启动服务并注入 HMR 代码
 监听 Compilor  done 事件得到 mainfest.json  chunk.js本地服务通过 Websocket  mainfest 推送到浏览器浏览器拿到文件中的 chunkId发起 ajax 获取模块代码
 拿到模块代码后执行 window.updateWebpack -> hotApply 热更新当前模块不刷新页面只替换 ⭐️ // window.webpackHotUpdate
 webpack.__require__ 执行模块代码
7webpack原理  合并配置shell 脚本  配置文件 ⭐️ // 初始化参数配置
 利用配置实例化 Compiler加载所有 plugin 插件执行 .run() 开始构建
  entry 为入口依次遍历所有文件执行所有的 loader 转换
 得到文件关系 和转换后的文件按照 entry 分组输出到下载列表
 按照 output 输出下载到指定路径
开发插件的桥梁Compiler - webpack 的生命周期唯一
Compilication - 文件每更新一次就创建一次编译
- 第三遍 2025.2.18 -
- 第二遍 2025.2.14 -
 // 1、entry: 配置方式记错 - 不识别 path 这个属性 + output 配置属性记错
 // 2、resolve 的作用不是解析,是「依赖查找配置规则」+ module 的作用是:依赖解析、转换配置
 // 3、resolve 的属性 modules + extensions 错误
 // 4、开启 Code Splitting 配置的方式:splitChunk 插件配置
 // 5、devtool:cheap-eval-source-map; (不提供列信息)方便代码调试
 // 6、 监听到 compiler 的 done 事件 得到的文件是 chunk.js 文件,浏览器从 mainfest.json 文件中得到了 ChunkId,获取 chunk.js
 // 7、window.webpackHotUpdate -> hotApply 热更新函数,替换原有模块代码
- 第一遍 2024.12.26 -
// 2020.07 对象分类、Promise按序执行、实现map
- 第六遍 2025.2.21 -
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]) {
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre;
    }, {})
}
function list(arr, init) {
    return arr.reduce((pre, cur) => pre.then(cur), Promise.resolve(init));
}
Array.prototype.fakeMap = function(callback, thisArg) {
    return this.reduce((pre, cur, index, array) => {
        pre[index] = callback.call(thisArg, cur, index, array);
        return pre;
    }, [])
}
- 第五遍 2025.2.18 -
 // 1、classify分类函数:reduce 的初始值应该是 {} 而不是 []
 // 2、map 是 Array 上的高阶函数,之前写错成 Function(想成bind了),重写了
- 第四遍 2025.2.14 -
 // 1、again,map 接收两个参数,一个 callback 函数,另一个是 this 指向
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
 // 1、map 函数接收两个参数,第一个是 callback,第二个是 thisArg - 用来指定 this
⭐️ // reduce filter map forEach 会跳过 empty 的数组项( map 和 reduce 都跳过)
- 第一遍 -
// 作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置
- 第三遍 2025.2.21 -
唯一索引声明后保证组合唯一不会重复 ⭐️ // 确保索引字段的组合值
sequelize.define('database', {
    name: DataTypes.STRING
}, {
    indexes:[{
        unique: true,
        fields: ['date', 'sort']
    }]
})
SQL: CREATE INDEX dix_date_sort ON `database` (`date`, `sort`)
 // 1、CREATE UNIQUE INDEX - 少写了 UNIQUE,或者写 ADD UNIQUE KEY `dix_date_sort` (`date`, `sort`)
bulkCreate(data, { updateOnDuplicate: [name, age] }) 批量添加数据的时候如果唯一索引有重复项不添加数据而是修改 updateOnDuplicate 中声明的 field没声明的不修改 ⭐️ // 执行更新而不是抛出错误,被指定的字段才会被更新,未指定的字段保持不变
define 的第三个参数 
underScore - 驼峰映射下划线timeStamp: false - 不自动创建created_atupdated_at  // 2、属性名称 underScores、timeStamps
- 第二遍 2025.2.17 -
 // 1、创建的 SQL 关键词:CREATE UNIQUE INDEX idx_date_sort ON `table` (`date`, `sort`)
 // 2、ADD UNIQUE KEY `idx_date_sort` (`date`, `sort`)
 // 3、timestamps: false, tableName: 'users'
- 第一遍 2025.2.13 -
// 作业94:Nextjs fetch 为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?sequelize findAll/findOne 可以通过传递参数获取 js 格式的数据
- 第三遍 2025.2.21 -
Nextjs fetch 查到 Response 类型包含了响应头响应体状态码等
.json() 用于将数据转换成 json 再转成 js 对象 ⭐️ // await res.json()
findOne({ where, plain: true})
findOne/findAll({ where, raw: true})
plain: 拍平最外层嵌套结构保留
raw: 多层级直接拍平 ⭐️ // 关联数据会直接合并到主对象
- 第二遍 2025.2.17 -
 // 1、状态码、响应头、响应体
 // 2、await res.json()
- 第一遍 2025.2.12 -
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第七遍 2025.2.21 -
mysql2: 为了 node 环境的数据库驱动可以直连数据库需要写很多 SQL
	- 提供 mysql2/promise 支持 async await 异步操作可以管理多个数据库连接
Sequelize将数据库的表和行映射为对象模型 ORM适用于函数式编程 ⭐️ // 直接通过 js 操作
mysql2
import { createPool } from 'mysql2/promise'
const pool = createPool({host, user, password, database});
const con = await pool.getConnect();  // 1、获取连接 函数错了 getConnection()
consr [rows] = con.execute('SELECT * FROM table');  // 2、执行 SQL 需要 await
Sequelize
import { Sequelize, Modal, DataTypes } from 'sequelize';
const se = new Sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql2'
});
定义 mysql 模型 法1
class User extends Modal{}
User.init({
    name: DataTypes.STRING
}, {
    sequelize: se,
    modelName: 'users'
})
法2
sequelize.define('users', { ⭐️ // const User = ...
    name: DataTypes.STRING
}, {
    unserScore: true,
    timeStamp: false
})
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、sequelize:实例化参数错误 + init 参数错误
 // 2、sequelize:define 第三个参数应该是一些配置
- 第四遍 2025.2.9 -
 // 1、sequelize:实例化的参数错误 + init 参数错误
- 第三遍 2025.1.31 -
 // 1、mysql2:导入依赖包错了 mysql2/promise + createPool的参数 少写了 database
 // 2、mysql2:获取连接的函数错了 await pool.getConnection()
 // 3、mysql2:需要使用 await 执行 SQL
 // 4、sequelize:有两种模型定义方法:一种是 define,另一种 extends Modal + init
- 第二遍 2025.1.26 -
 // 1、mysql2:依赖包是 mysql2/promise
 // 2、mysql2:创建连接池 createPool 不需要 await
 // 3、mysql2:获取连接时需要 await + 需要使用 await 执行 SQL
 // 4、sequelize:实例化的参数错误 + init 参数错误
- 第一遍 2025.1.22 -
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第七遍 2025.2.21 -
预渲染服务端渲染 SSR getServerSideProps静态生成 SSG generateStateParams增量静态生成 ISR revalidate  // 1、静态生成 的函数名称是 generateStaticParams 中间不是 state 是 static
⭐️ // 服务端渲染 - 在请求时,服务端直接引入数据生成 HTML,客户端直接渲染,不需要使用客户端js
⭐️ // 静态生成 - 构建、运行时,间隔更新时间,自动增量式创建新的 HTML
SSG
export default function Blog({slug}) { return <div>{slug}</div> }
export const revalidate = 60; // 页面重新验证时间
export async function generateStateParams() {  // 2、generateStaticParams
    return [{slug:'first'}, {slug:'second'}]
}
SSR
import { getServerSideProps } from 'next/server';
export default function Blog({data}) { return <div>{data}</div> }
export async function getServerSideProps(){
    const res = await fetch('xxx');
    const data = await res.json();
    return {props: {data}}
}
- 第六遍 2025.2.17 -
- 第五遍 2025.2.12 -
 // 1、SSG:函数 少写了 async
 // 2、SSR:函数 少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR + 客户端、服务端生成的结果都是 HTML
 // 2、SSR:函数名称、依赖包错误 getServerSideProps next/server
 // 3、SSG:函数名称错误 generateStaticParams
- 第三遍 2025.1.31 -
 // 1、SSG:记错函数名称 generateStaticParams,用于生成动态路由的参数列表 + 少写了 revalidate
 // 2、SSR:异步解析数据 需要 await - data.json() 是异步的,处理 json 数据转换为 js 对象       
- 第二遍 2025.1.26 -
 // 1、SSR: fetch 数据需要 await  +  异步解析数据需要 await
- 第一遍 2025.1.22 -
// 作业68:React hooks 的原理是什么?useEffect useState模拟实现
- 第八遍 2025.2.21 -
hooks 闭包+链表目的是在不使用类组件的情况下依旧可以使用 state状态管理模拟生命周期
useEffect
const render = () => { 
    ReactDOM.render(<App />, document.getElementById('id'));
    cursor = 0;
};
const memorized = {};
let cursor = 0;
function fakeUseEffect(callback, array) {
    const lastDep = memorized[cursor];
    const hasChanged = lastDep && lastDep.some((it, index) => Object.is(it, array[index]))
    if(!lastDep || hasChanged){
        callback();
        memorized[cursor] = array;
        render();
    }
    cursor++;
}
useState
function fakeUseState(init){
    const current = cursor;
    memorized[current] = memorized[current] ?? init;
    const useValue = (newV) => {
        memorized[current] = newV;
        render();
    }
    cursor++;
    return [memorized[current], useValue]
}
- 第七遍 2025.2.17 -
- 第六遍 2025.2.12 -
 // 1、原理:没有事件委托,主要是 闭包和链表 + 少写了 hooks 的创建目的
 // 2、render:ReactDOM.render(<App />, document.getElementById('root')); 
 // 3、useEffect:使用 find 得到的是值,应该使用 some + 判断条件应该是 !Object.is
 // 4、useState:少写了 值 的初始化 memorized[current] = memorized[current] ?? init;
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
 // 1、render:ReactDOM.render(<App />, document.getElementById('root'));
 // 2、UseState:cursor++ 忘记写游标自增了
- 第三遍 2025.1.13 -
 // 1、不是描述 hooks 的执行过程,先描述【目的】state、状态管理、模拟生命周期,再描述【实现上述目的的方法】闭包 + 链表
 // 2、UseEffect:some 应该是判断「不等于」- 有不相等的,代表依赖数组变化,需要更新 !Object.is
 // 3、UseEffect:少写了「更新依赖数组」的逻辑:memorized[count] = array;
function fakeUseState(init) {
    const currentCount = count;
    let value = memorized[currentCount] ?? init;
     // 4、使用 value 会导致返回后,value 不会自动更新,和 memorized[currentCount] 失去了联系,本行改为:memorized[currentCount] = memorized[currentCount] ?? init;
    const setValue = (newValue) => {
        memorized[currentCount] = newValue;
         // 5、调用 useValue 必须触发 render:render();
    }
    count++;
    return [value, setValue]
     // 4.1、延续问题4,本行修改为:return [memorized[currentCount], setValue]
}
- 第二遍 -
 // 1、hooks 目的:不只是「生命周期替代能力」,还有「状态管理能力」、「逻辑复用能力」,还包括 state(没有 hooks 的函数组件主要接收 props 来展示 UI)
 // 2、hooks + 副作用链表:hooks 在组件更新时是「全部按序重新执行一遍」,副作用链表是「在 hooks 执行过程中,如果 useEffect 依赖有更新」,就更新到副作用链表
 // 3、UseEffect:cursor应该使用其他变量保存,和下文中 setValue 形成闭包 - 在异步情况下,cursor 可能会变化,导致索引错误
 // 4、UseState:要触发一次 render() 
- 第一遍 -
// 作业67:React 怎么做的性能优化?
- 第七遍 2025.2.21 -
1fiber 架构优先级调度增量式Diff时间分片渐进式渲染
2跳过重新渲染策略 eagerState策略 - 如果 state 的新值和旧值无关可以直接渲染而不需要再去查询旧值
    bailout策略 - 比较组件的 state  props不变化则跳过重新渲染
类组件 shouldComponentUpdate(nextProps, nextState) 自定义布尔值表示是否重新渲染
函数组件 React.memo((props) => return <div>{ props.text }</div>) 浅比较 props,不变则跳过
③useCallback((v) => v*2, [v]); useMemo((v) => v+1, [v])
- 第六遍 2025.2.17 -                                
- 第五遍 2025.2.12 -
 // 1、少写了 函数组件 React.memo - 浅比较props,不变则直接复用 
// 记忆法:shouldComponentUpdate(类组件)/React.memo(函数组件) + 2个hook:useMemo/useCallback
- 第四遍 2025.1.23 -
- 第三遍 2025.1.18 -
 ⭐️ // useCallback:每次渲染,函数都是重新生成的,用了这个就缓存住了
- 第二遍 2025.1.12 -
 // 1、React.memo:对于「函数组件」,React 提供了 React.memo 这个高阶函数来处理 -> 对组件的 props 进行浅比较,如果不变则跳过重新渲染
- 第一遍 -
// 69.x 的平方根
- 第一遍 2025.2.21 -
function sqrt(n){
    const half = Math.ceil(n / 2);
    let res = 1;  // 1、res 应该从 0 开始数,否则 n 为 0 时会返回 1
    while (res <= half) {
        const next = res + 1;
        if (res * res <= n && next * next > n) {
            return res
        }
        res++;
    }
    return res
}
⭐️ // 另一种空间换时间的写法
function sqrt2(n) {
    let left = 0;
    let right = n + 1;
    while (left + 1 !== right) {
        const mid = Math.ceil((left + right) / 2);
        if (mid * mid > n) {
            right = mid;
        } else {
            left = mid;
        }
    }
    return left
}
// 作业64:Vue渲染怎么实现的?
- 第八遍 2025.2.20 -
AOT 预编译 + 模版编译
	parse 词法分析语法分析得到 AST
	optimize 标记静态节点方便后续 diff/patch 跳过静态节点
	generate 生成 render 函数
数据双向绑定
	借用发布订阅数据劫持实现双向绑定 Object.defineProperty(obj, 属性, {get, set})/Proxy
	在组件初始化实例化 Watcher时访问 set 触发组件的 Dep 收集当前正在执行的 Watcher ⭐️ // getter
    在渲染更新组件时get 触发更新所有的 Watcher - dep.notify() ⭐️ // setter
执行 render得到 VDOM
执行 patch/diff 计算得到最小 UI 更新量应用到真实 DOM
- 第七遍 2025.2.17 -
- 第六遍 2025.2.11 -
 // 1、模版编译的阶段名称错误:parse - optimize - generate
 // 2、函数使用方法错误:Object.defineProperty(obj, 属性, { get, set })
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 -
 // 1、数据劫持之后,触发订阅的时机是「渲染组件,实例化 Watcher 时,执行到 getter 触发对应的 Dep 收集正在执行的 Watcher」
- 第二遍 -
 // 1、模版编译执行步骤的最后一步是 generate
 // 2、AOT 预编译功能是针对 template 的,不是针对 js 的
- 第一遍 -
// 2019.07 盒模型有哪些?有什么区别?
- 第二遍 2025.2.20 -
box-sizing: border-box; Width  Content+border+padding 的组合大小IE 盒模型
box-sizing: content-box; Width  Content 内容大小W3C 标准盒模型
- 第一遍 2025.1.20 -
// 作业16(1):箭头函数和普通函数的区别是什么?(3点)
- 第二遍 2025.2.20 -
没有 thisthis 指向定义时的上下文没有 argumentnew.targetsuper没有原型不能用作构造函数
- 第一遍 2025.1.20 -
// 作业32:什么是队头阻塞?原因是什么?怎么解决?
- 第二遍 2025.2.20 -
队头阻塞一个请求没有返回其他请求排队等待返回后再发起
原因TCP 有丢包机制拿不到响应就挂着一个包不回来影响其他  // 1、请求-应答模式导致的
解决
 // 2、HTTP/1.1 优化的方案:域名分片 + 并发连接
升级到 Http/2.0 在应用层解决了队头阻塞问题采用二进制流-多路复用多个请求响应之间没有顺序关系但是 TCP 传输层还没解决
HTTP/3.0 采用 UDP彻底解决队头阻塞
- 第一遍 2025.1.20 -
⭐️ // HTTP/3 QUIC 协议
// 作业40:写出以下值
- 第二遍 2025.2.20 -
1 0  2NaN 
3NaN undefined  // 1、少写了 {}
4Number - 0 parseIntparseFloat - NaN  // 2、Number-含有非数字字符就NaN parseInt-非数字开头即NaN
5'122' '32' '02' 'NaN2' NaN 630 32
712 位操作符会把左右两边都转换成32位的值即转换为整数 ⭐️ // 位操作符只能操作 32 位的整数
8undefined 其实是从 null 继承而来规定相等测试为 true ⭐️ // 派生
- 第一遍 2025.1.20 -
// 作业62:fiber架构是什么?优点和实现方式?
- 第八遍 2025.2.20 -
三个主流程
Scheduler 调度器主要用于优先级调度在整个渲染流程中主要用于宏观调控
Reconciler 协调器主要用于在重绘/空闲时间判断是否要执行单元渲染任务根据 shouldYield 判断对应 render 阶段
Render 渲染器对应 commit 阶段更新真实 DOM
	SchedulerReconciler这两个流程都是在内存中执行的异步随时可能被以下几种情况打断报错时间分片不够优先级更高
	Render同步更新真实 DOM不能暂停
优点
1优先级调度优先渲染用户交互视口等提高用户体验非视口优先级延后
2渐进式渲染 + 时间分片之前 react 的渲染是需要对比更新整棵 VDOM 在主线程同步执行会阻塞重绘使用 fiber 之后会将整个渲染任务分为一个个小的渲染任务单元在空闲时间重绘之前进行判断是否有足够时间执行一个渲染任务单元
3增量式 Diff不再比较一整颗树而是在逐步执行渲染单元任务时比较更新真实 DOM提高 diff 算法效率
- 第七遍 2025.2.17 -
- 第六遍 2025.2.10 -
 // 1、时间切片:之前 react 会对整棵树 对比+更新,同步执行,会长时间占用主线程,阻塞重绘 ; 描述优化:小的单元任务,在空闲 + 重绘判断执行
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
 // 1、忘记中文:Scheduelor 是调度器
 // 2、时间切片:应该先说明 react 每次更新都要递归构建整棵 fiber 树+比较+更新,长时间占用主线程,会阻塞重绘 
 // 3、增量式 Diff:不是一次性比较整棵树,而是根据任务执行进度,逐步比较、更新部分节点 - 减少了每次比较的范围,提高了效率 
- 第三遍 -
 // 1、少写了 Reconciler 协调器,作用是「调用 shouldYield 判断当前 Time Slice 是否有剩余时间」
 // 2、Scheduler + Reconciler 不是因为「异步」才可以被打断,是以为「他们都在内存中执行,不会更新宿主环境 UI」
 // 3、写错了 Render「渲染器」
 // 4、优点:渐进式渲染、优先级调度、增量式 Diff、时间分片
- 第二遍 -
 // 1、少一个 增量式 Diff
- 第一遍 -
// 作业58:React和Vue是怎么描述UI的
- 第七遍 2025.2.20 -
React: JSX=HTML+JS类组件 函数组件state 组件状态 props 传入组件属性{}
Vue: 模版语法 + 指令 v-if v-if-else v-else v-for v-show v-model + 
- 第六遍 2025.2.17 -
- 第五遍 2025.2.10 -
 // 1、React 少写了 :类组件+函数组件
 // 2、Vue 少写了:模版语法 + v-show
- 第四遍 2025.1.22 -
- 第三遍 2025.1.18 -
- 第二遍 -
 // 1、React 少写了 ①自定义组件-函数组件、类组件 ②state、props
- 第一遍 -
// ❌ 1、React 少写了: 可以自定义组件 + 使用花括号{}将js嵌入JSX {note} + state、props
// ❌ 2、Vue 少写了:模版语法 + 使用双大括号展示数据 + 指令
// 2019.07 隐式转换:{}+{}=?
- 第二遍 2025.2.20 -
'[object Object][object Object]'
- 第一遍 2025.1.8  -
// 2019.07 引用css,link和@import的区别
- 第二遍 2025.2.20 -
link XML标签浏览器都兼容渲染进程 预解析线程提前下载js 可获取修改  // 1、少写了:可以处理 rss 等其他事务
import css2.0提出低版本有兼容问题遇到再下载由于下载时机js难以获取修改  // 2、少写了:只能加载 css
- 第一遍 2025.1.8  -
// 作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义?
- 第二遍 2025.2.20 -
Cache-control: max-age=2000;[优先级高]
Expires: deadline;
HttpOnly仅限 HTTP 协议访问浏览器禁用 js 访问 cookie例如 document.cookie
- 第一遍 2025.1.8  -
// 作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接?
- 第二遍 2025.2.20 -
客户端和服务端建立连接不关闭之前每次请求都要重新建立连接 ⭐️ // 长连接后,一个连接可以处理多个请求响应
keep-alive ⭐️ //  Connection: keep-alive
 HTTP/1.1 开始启用
- 第一遍 2025.1.8  -
// 作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码
- 第二遍 2025.2.20 -
请求头 GET index.html   // 1、GET /index HTTP/1.1
响应头 HTTP/1.1 200 OK
HTTP/1.1中唯一要求 HOST
常见状态码 
200 OK 正常请求
204 No Content 只有响应头没有响应体
301 Moved Permanently 永久重定向使用响应体中的 Location
302 Moved Temperaily 临时重定向使用响应体中的 Location
304 Not Modified 使用本地缓存刷新缓存时间 ⭐️ // If-modified-since/Last-modified + If-none-match/ETag
404 Not Found 资源不存在
401 请求错误语法问题  // 2、400 Bad Request 请求有语法错误(格式不正确、缺少必要请求参数、请求体格式错误)
403 请求方法错误不支持  // 3、403 Forbidden 请求资源禁止访问
 // 4、405 Method Not Allowed 使用了服务器不支持的 HTTP 请求方法
500 服务端错误
- 第一遍 2025.1.8  -
// 作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器
- 第二遍 2025.2.20 -
Proxy 的原型是 undefined ⭐️ // 所以不能使用 instanceof 查询,会报错
const {proxy, revoke} = Proxy.revocable(target, handle);
revoke(); // 不可逆
const handle = {
    get(...args) { return Reflect.get(...args) },
    set/has/constructor ⭐️ // apply 对应执行函数
}
- 第一遍 2025.1.8  -
// 2024-11 数组乱序 洗牌算法
- 第二遍 2025.2.20 -
// 从最后一个元素开始,从数组中随机选出一个位置,交换,直到第一个元素
function chaos(arr){
    let n = arr.length - 1;
    while(n >= 0){
        const random = Math.floor(Math.random() * arr.length);
        [arr[random], arr[n]] = [arr[n], arr[random]];
        n = n-1; ⭐️ //  n--;
    }
    return arr
}
- 第一遍 2024.12.25 -
// 2021-04 实现给定时间切换状态
- 第一遍 2025.2.20 -
async function promisesetTimeout(promise, delay){
    return await Promise.race([promise, new Promise((resolve, reject) => { 
        setTimeout(() => { reject(new Error('promise time out')) }, delay) 
        ⭐️ // setTimeout(reject, delay, new Error('promise time out'))
    })])
}
// 2021-07 给DOM元素绑定事件
- 第一遍 2025.2.20 -
onclick 属性
dom.addEventListener('click', () => {})
// 2021-07 前端路由的两种模式
- 第一遍 2025.2.20 -
historyhistory.go(n)/forward()/back()/pushState() - 更新浏览器的历史记录但是没有请求服务器通过返回/前进访问时向服务器发起请求
 // 1、少写了 replaceState + popstate
/** 1、pushState - 向浏览器历史记录栈中添加一个新的历史记录条目,需要手动调用
	const title = 'New Page';
	const url = '/new-page';
	history.pushState({ page: 'newPage' }, title, url);
2、replaceState - 修改当前的历史记录条目,而不是添加新的条目(也不会触发页面刷新
	// 当前链接:https://www.mozilla.org/foo.html
	history.replaceState({ foo: "bar" }, '', url);
3、popstate - 在浏览器的历史记录发生改变时触发,例如点击前进/后退按钮、go/forward/back,pushState和replaceState不会触发这个事件
	window.addEventListener('popstate', (event) => {
		if (event.state) {
			console.log('State:', event.state); // 根据 event.state 更新页面内容
		}
	});
*/
hashurl 链接中 #hash使用 前端来切换模块和服务器无关
 // 1、少写了 location.hash + window.onhashchange
// 2021-06 CSS性能优化
- 第一遍 2025.2.19 -
减少层级因为 css 从右向左解析的
开启 GPU 加速使用 transformopacity ⭐️ // 减少使用昂贵属性
减少布局大小调整减少回流重绘带来的性能消耗
code splitting 按需加载懒加载 ⭐️ // 模块化
使用 link 而不是 import使用渲染进程的预解析线程 预加载css文件
// 2021.09 get和post有什么区别
- 第三遍 2025.2.19 -
get 参数携带在链接上大小2kb浏览器默认会缓存回退无害参数依旧正确 ⭐️ // 参数在请求行上
post 参数在请求体上大小不受限默认不缓存可配置回退有害参数不保留
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、从四个方面描述:数据参数 + 参数长度 + 回退安全性 + 缓存
// 2021.06 链表 141. 环形链表
- 第三遍 2025.2.19 -
function isCircle1(head){
    try{
        JSON.stringify(head);
        ⭐️ // 可以写在这儿: return false
    }catch(e){
        return true
    }
    return false
}
function isCircle2(head){
    while(head) {
        if(head.isCircle){
            return true;
        }
        head.isCircle = true;
        head = head.next;
    }
    return false
}
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -
 // 1、判断条件是 while(head) 
// 2021.07 事件流 + 事件模型
- 第三遍 2025.2.19 -
事件流
	先捕获 - 从上至下 window-document-html-body-. 触发事件处理函数再冒泡 - 从外向内 ⭐️ // 冒泡:从具体节点逐渐向上传播到 DOM 最高层父节点
事件模型
	DOM2 事件模型 先捕获执行事件处理函数再冒泡
	现代浏览器 事件模型利用事件冒泡通过事件委托管理
- 第二遍 2025.2.10 -
 // 1、现代事件模型:是事件委托 - 利用事件冒泡,将事件处理程序添加到父元素;DOM2级事件模型:捕获+处理+冒泡
- 第一遍 2025.2.8 -
// 作业93:闭包的作用和原理
- 第三遍 2025.2.19 -
闭包原理内部函数引用外部函数变量外部函数执行结束后原本应该清理活动对象但是被引用的函数保存在 closure 保留下来  // 1、原理少写了闭包依赖于「词法作用域」-作用域链-垃圾回收机制
闭包作用保存变量的持续值  // 2、①保存状态:允许函数记住它被创建时的环境 + ②数据封装:创建私有变量 + ③回调函数:确保回调函数在调用时能访问到定义时的上下文 + ④函数工厂:创建定制化函数
- 第二遍 2025.2.10 -
 // 1、少写了 函数工厂、回调函数
- 第一遍 2025.2.8 -
// 作业92:前端中的序列化是什么?常见序列化方法
- 第三遍 2025.2.19 -
序列化把前端参数json 转换成js对象
 // 1、将「数据结构、对象」转换为可以「存储、传输」的格式的过程
常见序列化 new formData(from)JSON.stringify(obj); JSON.parse()
⭐️ // JSON.stringify 无法处理 Function、无法处理循环引用
⭐️ // new FormData 转换成「键值对」格式
- 第二遍 2025.2.10 -
 // 1、JSON.stringify 无法处理 Function、无法处理循环引用;new FormData 收集表单数据并序列化,转换成键值对格式
- 第一遍 2025.2.8 -
// 作业91:三次握手的过程和具体包的作用
- 第三遍 2025.2.19 -
客户端 SYN 服务端
服务端 SYN + ACK 客户端(标志位)
客户端 ACK 服务端
SYN 序列包 + ACK 确认包
- 第二遍 2025.2.10 -
客户端和服务端建立 TCP 连接SYN 序列编号包ACK 确认包标志位)⭐️ // SYN 同步序列编号包
- 第一遍 2025.2.8 -
// 作业90:金玉满堂是什么意思?在花卉中的说法是什么?
- 第三遍 2025.2.19 -
形容很多财富比喻有人学识很丰富
玉堂春富贵 玉兰 海棠 迎春 牡丹 桂花
- 第二遍 2025.2.10 -
- 第一遍 2025.2.8 -

LTN①⑥ 错题重做

错题 ①⑥ 9继续错题/28错题/89题

✅作业58:React和Vue是怎么描述UI的 ✅作业62:fiber架构是什么?优点和实现方式? ✅作业64:Vue渲染怎么实现的? ✅作业67:React 怎么做的性能优化? ✅作业68:React hooks 的原理是什么?useEffect useState模拟实现 ✅作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ✅作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ❌作业94:为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回? ❌作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置 ❌作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高? ❌作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ❌作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date ❌2020.07 对象分类、Promise按序执行、实现map ✅2024.11 第十七章 事件 ✅2021-07 常见Webpack问题 ✅作业45:原生+canvas 显示一张png格式的图片 ✅2024.10 第十四章-第十五章 DOM、DOM扩展 ✅2024.12 模拟实现instanceof ✅2019.07 h5新标签和语义化,块/行内元素 ✅作业2:支持迭代器的原生语言特性有哪些? ✅2019.07 为什么要清除浮动?怎么清除浮动? ❌作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅作业4:什么是生成器?有什么作用? ✅2021.07 防抖节流 ✅2024.12 模拟实现jsonp ❌作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ❌2023.07 clickhouse、mysql、mongodb异同 ✅作业70:Vue 和 React 的 Diff 算法比较

// 作业70:Vue 和 React 的 Diff 算法比较
- 第八遍 2025.2.18 - 
Vue 双端指针算法没找到 ⭐️ // 组件级别的更新
    - 同标签+同key 先查询索引是否一致
    - 索引不一致列表移动修改使用
	- 跳过静态节点
react 分层级比较 + 同类型有限比较 ⭐️ // 同层级优先比较 + 节点属性级别的更新
	- 不同标签直接重新渲染
	- 同标签修改属性使用
	- 列表同标签+同key
- 第七遍 2025.2.16 -
 // 1、Vue 少写了:静态节点跳过;双端指针比较算法找不到时,要判断 同类型+同key 节点是否同索引
- 第六遍 2025.1.31 -
⭐️ // React 是精确到节点属性级别的 Diff 计算,适合大型动态复杂场景;Vue 基于组件级别的 Diff 计算
- 第五遍 2025.1.25 -
 // 1、Vue 少写了:静态节点跳过 ;是基于组件级别的更新算法
 // 2、React 少写了:是基于单个节点属性级别的,适合大型动态复杂场景
- 第四遍 2025.1.24 -
 // 1、Vue:找不到匹配的点后,再找到同类型+同 key的节点,进行索引比较,一致则复用,不一致则移动到新位置修改使用
- 第三遍 2025.1.18 -
 // 1、Vue:静态节点跳过比较 - 不要遗漏了,是个很重要的性能优化
 // 2、Vue:双端指针比较后,还有找不到的 -> 找同类型+同key来比较
- 第二遍 2025.1.13 -
⭐️ // React:「列表组件,根据 key 确定新老节点关系」
 // 1、Vue:双端比较算法是直接找匹配节点,不进行 标签修改
 // 2、Vue:key 是第三层比较逻辑,第二层比较逻辑是「索引」 - 若索引没有变化,则不更新当前节点;如果索引有变化,判断是否「同类型 + 同key」,移动到新位置
 // 3、Vue:性能优化,编译时标记的静态节点跳过 - 虽然老生常谈,但是这其实是一大优化
- 第一遍 -
 // 1、简单描述下 React 的 Diff 算法是「精确到单个节点的属性级别」,适合「大型复杂场景」,例如大量动态组件和频繁更新的场景
 // 2、React:这里可以标题记忆 「① 分层比较 + 同类型比较优先策略」
 // 3、React:这里主要是指:「列表组件」中,根据 key 确定新老树子节点的关系
 // 4、React:少写了「② DFS 深度优先遍历子节点」
 // 5、描述:「更新基于组件级别」
 // 6、双指针其实很不明确,「双端比较算法-双向指针」
 // 7、是会复用,但是场景是「双端比较法没有找到足够匹配的节点,就根据节点的索引进行比较」
// 8、检索「同类型+同key 的节点」,「索引位置」没有变化,就认为这个节点不需要更新;若有变化,就「移动到新位置」,「复用旧列表」
// 2023.07 clickhouse、mysql、mongodb异同 
- 第五遍 2025.2.18 -
mysql 关联数据库读写性能都优但高并发时会有数据不一致适合小型应用  // 1、少写了:支持事务操作
mongodb 文档型数据库读性能优写性能差在分布式操作时会有数据不一致适合非结构式/半结构式数据 HTMLXML等
clickhouse 列式数据库读写性能都好支持异步/分布式操作适合高吞吐低延迟场景  // 2、少写了:不支持事务
- 第四遍 2025.2.16 -
 // 1、mysql 少写了:支持事务操作;出现数据一致性问题:高并发时
 // 2、mongodb 出现数据一致性问题:在分布式场景时
 // 3、少写了:列式数据库;少写了:不支持事务,支持分布式查询;少写了适合高吞吐、低延迟的场景
- 第三遍 2025.1.30 -
⭐️ // clickhouse:高吞吐、低延迟,支持复制、分布查询 + mongodb:分布式场景下一致性问题
- 第二遍 2025.1.25 -
 // 1、mongodb:在分布式场景下可能出现一致性问题 ;适合半结构化/非结构化数据
- 第一遍 2025.1.22 -
 // 1、mysql 是关系型数据库-适合事务处理场景;MongoDB 是文档型数据库-半结构化/非结构化数据;ClickHouse 是列式数据库-适合高吞吐、低延迟的分析场景
 // 2、mysql:一致性描述错误:高并发时可能出现一致性问题
 // 3、mongdb:性能描述错误 - 写的性能弱,读的性能好;适合数据少写了:半结构化数据,类似 XML HTML JSON 等,以及非结构化数据;一致性描述:在分布式场景下可能出现数据一致性问题
 // 4、clickhouse:少写了:读写性能都高,场景适合少了归类:高吞吐、低延迟场景,例如日志;操作少写了:支持复制和分布查询
// 作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询
- 第五遍 2025.2.18 -
INNER JOIN AB表的交集内联查询
LEFT OUTER JOIN A 表的所有满足条件行 + 与A表条件行匹配的B表数据
RIGHT OUTER JOIN B 表的所有满足条件行 + 与B表条件行匹配的A表数据
UNION 拼接两次查询
封装 CREATE VIEW my ON SELECT * FROM table  // 1、封装:关键词不是 ON 是 AS: CREATE VIEW my AS SELECT * FROM table
- 第四遍 2025.2.16 -
 // 1、INNER JOIN 不是合集,是交集,表达错误 - AB都满足的行,内连接
⭐️ // UNION 拼接
CREATE VIEW my ON SELECT * FROM table  // 2、封装:关键词不是 ON 是 AS
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
 // 1、少了一个 AS:封装 CREATE VIEW ... AS
- 第一遍 2025.1.22 -
 // 1、create View MY AS 关键词:Create View ... AS
// 2024.12 模拟实现jsonp
- 第四遍 2025.2.18 -
function jsonp(url, params, callback){
    let suffix = Object.keys(params).map(it => `${it}=${params[it]}`).join('&');
    let totalUrl = url + url.includes('?') ? '&' : '?' + suffix + `&callback=${callback}`;
    
    let dom = document.createElement('script');
    window[callback] = function(data){
        callback(data);
        document.body.removeChild(dom);
    }
    dom.src = totalUrl;
    document.body.appendChild(dom);
}
- 第三遍 2025.2.16 -
const suffix = Object.keys(params).reduce((pre, cur) => pre + `${cur}=${params[cur]}`, ''); 
 // 1、计算 url 时少了 '&' -> 使用 map + join 更简洁:Object.keys(params).map(it => `${it}=${params[it]}`).join('&') -> 使用 entries 也可以 Object.entries(params).map(([key, vlaue]) => `${key}=${value}`).join('&')
- 第二遍 2025.1.21 -
- 第一遍 -
// 2021.07 防抖节流
- 第四遍 2025.2.18 -
function debounce(fn, delay){
    let timeId;
    return (...args) => {
        if(timeId) {
            clearTimeout(timeId);
        }
		timeId = setTimeout(() => {
            fn(...args)
        }, delay)
    }
}
function throttle(fn, delay) {
    let flag = false;
    return (...args) => {
        if(flag) {
            return
        }
        flag = true;
        fn(...args);
        setTimeout(() => {
            flag = false;
        }, delay)
        
    }
}
- 第三遍 2025.2.16 -
 // 1、debounce 遗漏赋值:timeId = setTimeout()
⭐️ // throttle:间隔多少秒执行一次
 // 2、throttle:判断条件应该是 if(flag){return}:flag 为 true 时,证明这段时间内已经执行了
- 第二遍 2025.1.21 -
- 第一遍 -
// 作业4:什么是生成器?有什么作用?
- 第三遍 2025.2.18 -
* 声明恢复暂停代码yield + next 实现next 返回{done, value}实现遍历可迭代对象
作用实现协程自定义可迭代对象
- 第二遍 2025.2.15 -
 // 1、少写了:暂停、恢复代码执行,* 声明
- 第一遍 2025.1.30 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第五遍 2025.2.18 -
redux  react 的全局状态存储在 store 对象树中
reducer 使用 oldState + action 生成 新State只能通过 store  dispatch 来更新状态 state
RTK redux ToolKit 官方推荐工具包
import { createStore } from 'redux'
const reducer = (oldState = {v:1}, action) => { 
    if(action.s === 'new') { return {v: oldState.v + 1} } 
}
const store = createStore(reducer); (dispatch+subscribe+getValue)  // getState
store.dispatch({s: 'new'});
store.subscribe(() => {console.log(store.getValue())})  // 1、 函数名称错了:getState
- 第四遍 2025.2.15 -
 // 1、注意 store 是对象树:Redux 将全局状态保存在 store 对象树中
⭐️ // reducer:返回新的 state 而不是改变
 // 2、reducer 参数顺序是 oldState,action
 // 3、 函数名称错了:getState,通过 subscribe 更新 UI
- 第三遍 2025.1.29 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
 // dispatch getState  1、少一个 subscribe
 // 2、通过subscribe更新UI来响应 state - store.subcribe(()=>{console.log(store.getState())})
// 2019.07 为什么要清除浮动?怎么清除浮动?
- 第五遍 2025.2.18 -
原因块级元素把浮动元素当做不存在行内元素环绕浮动元素布局父盒子中子盒子浮动会导致高度塌陷无法撑开背景
解决:①父内最后一个元素后添加 box .box{clear:both} .father::after{ content:''; display: block; clear: both; } BFC .father { overflow: hidden }  设置高度
- 第四遍 2025.2.15 -
 // 1、父元素+伪元素 应该是 display: block,否则撑不开父级全部宽度
⭐️ // BFC 的盒子计算高度时包含浮动元素
- 第三遍 1.28 -
- 第二遍 1.25 -
 // 1、高度塌陷的描述:背景无法撑开 + padding、border 无法正常展示
 // 2、给父元素添加高度:宽度不对,高度+padding+border
- 第一遍 1.20 -
 // 1、写错了:是给父元素添加BFC + 父盒子设置高度(宽度不行
// 作业2:支持迭代器的原生语言特性有哪些?
- 第五遍 2025.2.18 -
Array.fromfor...of...扩展运算符数组结构new Mapnew Setyield *Promise.all/race
- 第四遍 2025.2.15 -
 // 1、少写了 yield * 只能在生成器中使用
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
 // 1、少写了:yield * 操作符 - 只能在生成器中使用;不是对象解构,是「数组解构」
// 2019.07 h5新标签和语义化,块/行内元素
- 第三遍 2025.2.18 -
1SEO 优化 2标签样式优化
h5新标签header footer article address nav aside time filedSet/legend section hgroup datalist ⭐️ // detail
input 新属性 required multiple autocomplete autofocus placeholder accesskey
input type 新属性 tel url email range search number date month time datetime week
行内块 img td input
行内 span br select textarea button ⭐️ // i u a strong em b label
 p div table caption li dd dt dl ul h1-h6 section ⭐️ // td ol tr address hr
- 第二遍 2025.2.15 -
1SEO 优化 2标签样式不一样
 // 1、新标签少了:datalist detail time hgroup 
 // 2、input 属性少了:accesskey 
 // 3、input type 属性少了:url range search date time datetime week month 
 // 4、br - 错了,不是块元素,而是行内元素 + 块元素少了 h1-h6 td ol table-caption tr address hr
 // 5、行内元素少了:br i u a strong em button b label
- 第一遍 2025.1.9 -
// 2024.12 模拟实现instanceof
- 第三遍 2025.2.18 -
function fakeInstanceOf(target, Ctor){
    let proto = target.__proto__; ⭐️ // Object.getPrototype(target)
    while(proto) {
        if(proto === Ctor.protorype){
            return true
        }
        proto = proto.__proto__;
    }
    return false
}
- 第二遍 2025.2.15 -
 // 1、获取 target 的原型链->应该是 target.__proto__ 或者 Object.getPrototype(target)
 // 2、判断原型链上是否和构造函数的原型对象相等,而不是和构造函数相等proto===Ctor.prototype
 // 3、不停往上找原型链 -> 通过 __proto__或者getPrototype找,而不是 prototype 
- 第一遍 2025.1.9 -
// 作业45:原生+canvas 显示一张png格式的图片
- 第三遍 2025.2.18 -
<canvas id='drawing' width=200 height=200 ></canvas>
const drawing = document.getElementbyId('drawing');
if(drawing.getContext){
    const imgData = drawing.toDataUrl('image/png');
    const dom = document.createElement('img');
    dom.src = imgData;
    document.body.appenChild(dom);
}
- 第二遍 2025.2.14 -
 // 1、需要判断是否存在 context 作为大前提:if(drawing.getContext){}
 // 2、image 标签添加 url 的属性是 src:image.src = imageData;
- 第一遍 2025.1.9 -
// 2021-07 常见Webpack问题
/** 1、webpack的几个核心概念理解:Chunk、Module、plugin
2、常见配置项:entry、output、module、resolve等
自定义 loader 怎么配置?
3、Code Splitting 和 Tree Shaking 的区别?懒加载怎么实现?
4、html-webpack-plugin 作用?
5、sourceMap不同环境的区别?怎么开启配置?
6、热更新怎么实现?
7、webpack原理/执行过程?
开发插件的桥梁?*/
- 第三遍 2025.2.18 -
1Chunk 由多个模块组成Module 万物皆模块plugin 插件在构建时指定时机注入代码
2entry'main.js'/{main:'main.js', sub:'sub.js'}
output:{fileName: '[name]-[hash].js', path: path.resolve(_dirname, 'dist')}
module 模块解析规则 {rules: [{ test: '/\.js/',  use: ['my-loader']}, {test: '/\.css/', use: ['style-loader', 'css-loader', 'less-loader']}]}
resolve 模块查找规则 {
    alias: {'@c': '/src/component'},
	extensions: ['jsx', 'js', 'json'],
	modules: ['node_modules', path.resolve(_dirname, 'src/myL')]
}
自定义 loader:①module.rules use 时直接使用具体地址下的loader use:[path.resolve(_dirname, 'src/myL/my-loader.js')]
resolve.modules 配置时添加指定路径如上 + module.rules use 时直接使用 loader 如上
3Code Splitting 按需加载根据 entry 配置分组在访问页面时加载需要的模块提高渲染效率通过 splitChunk 插件开启配置
Tree Shaking 按需打包减少包体积根据 entry 入口打包关联代码不使用的代码不打包webpack 默认开启
懒加载 使用 code splitting 开启在首页初始渲染时只加载需要渲染的模块
4html-webpack-plugin  webpack 打包出来的 js  css 插入 html
5sourceMap devtool: cheap-eval-source-map; 本地
devtool: hidden-source-map; 生产
6热更新:①启动 webpack dev-server 在内存中启动服务和浏览器 websocket 实时通讯 ⭐️ // 同时在服务中注入了 HMR 热更新代码
有代码修改时通过监听 Compiler  done 事件得到 mainfest.json 配置文件  chunk.js 模块文件通过 websocket 推送给浏览器
浏览器从中获取到 ChunkId 等信息通过 ajax 获取 chunk.js
替换模块代码 window.webpackUpdate -> hotApply 热更新替换指定模块而不需要刷新整个页面 ⭐️ // window.webpackHotUpdate
执行模块代码进行渲染 webpack.__require__
7webpack原理 整合参数 shell 指令传入的配置和配置文件整合成配置参数 ⭐️ // 初始化配置参数
通过配置实例化 webpack Compiler引入所有插件执行.run开始构建
根据 entry 配置依次遍历所有文件按序执行相关的所有依赖
得到转换后的文件和文件关系将文件输出到下载列表 ⭐️ // 以 entry 为分组依据打包为多个 chunk
根据 output 配置输出到指定路径
开发插件的桥梁: Compiler webpack 唯一实例代表 webpack 的生命周期Compilication 更新一次创建一次构建的实例
- 第二遍 2025.2.14 -
 // 1、entry: 不识别 path 这个属性,entry: 'main.js'/entry: { main: 'src/main.js', sub: 'src/sub.js' }
 // 2、output: { filename: '[name]-[hash].js', path: path.resolve(_dirname, 'dist') }
 // 3、resolve 的作用不是解析,是「依赖查找配置规则」+ module 的作用是:依赖解析、转换配置
 // 4、resolve.modules :查找 module 的时候,需要传递具体地址 modules: [path.resolve(_dirname, 'src/myC'), 'node_modules']
 // 5、resolve.extensions: 少写了常见的对扩展的处理 extensions:['jsx', 'js', 'json']
 // 6、开启 Code Splitting 配置的方式:splitChunk 插件配置
⭐️ // 懒加载:首次渲染
 // 7、devtool:cheap-eval-source-map; (不提供列信息)方便代码调试
⭐️ // 启动 webpack 启动的内存中的服务:dev-server
 // 8、 监听到 compiler 的 done 事件 得到的文件是 chunk.js 文件,浏览器从 mainfest.json 文件中得到了 ChunkId,获取 chunk.js
 // 9、window.webpackHotUpdate -> hotApply 热更新函数,替换原有模块代码
⭐️ // webpack 原理:step1 初始化配置参数
⭐️ // webpack 原理:step2 执行 Compiler.run 开始构建
- 第一遍 2024.12.26 -
⭐️ // 1、coding splitting:开启方式是 splitChunk 插件配置 - 加快响应速度
// 2020.07 对象分类、Promise按序执行、实现map
- 第五遍 2025.2.18 -
const a = [{name:'1', age:1}, {name:'1', age:2}, {name: '2', age:1}]
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]){
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre
    }, [])  // 1、reduce 的初始值应该是 {} 而不是 []
}
function listP(arr, init) {
    return arr.reduce((pre, cur) => pre.then(cur), Promise.resolve(init))
}
 // 2、map 是 Array 上的高阶函数,之前写错成 Function(想成bind了),重写了
Array.prototype.fakeMap = function(callback, thisArg) {
    return this.reduce((pre, cur, index, array) => {
		pre[index] = callback.call(thisArg, cur, index, array);
        return pre
    }, [])
}
- 第四遍 2025.2.14 -
 // 1、again,map 接收两个参数,一个 callback 函数,另一个是 this 指向
/** Array.prototype.fakeMap = (callback, thisArg) => {
	return this.reduce((pre, cur, index, array) => {
		pre[index] = callback.call(thisArg, cur, index, array);
		return pre
	}, [])
}*/
- 第三遍 2025.1.24 -
⭐️ // classify:少写了 reduce 的初始值 - 一个空对象 {}
- 第二遍 2025.1.19 -
 // 1、map 函数接收两个参数,第一个是 callback,第二个是 thisArg - 用来指定 this -> 配合 cb.call(thisArg, cur, index, array);
⭐️ // reduce filter map forEach 会跳过 empty 的数组项( map 和 reduce 都跳过)
- 第一遍 -
 // 1、注意 map 除了 callback,第二个参数是可选的的 thisArg,用于指定前面函数的 this
// 作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date
- 第二遍 2025.2.17 -
A.hasMany(B, {
    foreignKey: 'B_id', // 多的id
    sourceKey: 'A_id', // 一的id
})
B.belongsTo(A, {
    foreignKey: 'B_id', // 多的id
    targetKey: 'A_id' // 一的id
})
 // 1、await A.findAll({ include: [{ model:B }] })
- 第一遍 2025.2.13 -
A.hasMany(B, {
    foreignKey: B_id,
    sourceKey: A_id
})
B.belong(A, {  // 1、belongsTo
    foreignKey: B_id,
    targetKey: A_id
})
 // 2、await A.findAll({ include: [{ model: B }] })
// 作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条
- 第二遍 2025.2.17 -
const [issue, created] = await table.findOrCreate({
    where,
    default: data  // 1、数据源属性 defaults
})
if(!created) {
    issue.save();  // 2、先set数据,再保存 issue.set(data); await issue.save();
}
- 第一遍 2025.2.13 -
await Modal.findOrCreate({  // 1、需要获取函数的返回值,获取是否为新创建 const [issue, created]
    where
     // 2、数据源没有传入 defaults: data
})
 /** 3、如果已经存在,更新描述
	if(!created) {
		issue.set(data);
		await issue.save();
	} */
// 作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高?
- 第二遍 2025.2.17 -
索引是数据库表为了快速搜索单独为表创建的树结构
搜索时直接通过索引查询数据而不是遍历整个表查询
方法一
const start = dayjs(new Date(time)).startOf('day').toDate();
const end = dayjs(new Date(time)).endOf('day').toDate();
table.findAll({
    where: { date:{
            [op.between]: [start, end]
        }}})
方法二
table.findAll({
    where: { date: { sequelize.fn(DATE, sequelize.cols('date'), '=', time)}}})
 /** 1、const options = {
	where: Sequelize.where(
		Sequelize.fn('DATE', Sequelize.col('date')),
		'=',
		time
	)}
	或者函数是:Sequelize.fn('DATE_FORMAT', Sequelize.col('date'), '%Y-%M-%D')
*/
- 第一遍 2025.2.13 -
 // 1、方法一:需要先将传入的 string 转换成 date:const searchTime = new Date(string)
 // 2、还需要处理 .toDate(),因为 date 字段存储的也是 Date 类型
 /** 3、方法二:转换成 Date 再匹配
const options = {
	where: Sequelize.where(
		Sequelize.fn('DATE', Sequelize.col('date')),
		// 或 Sequelize.fn('DATE_FORMAT', Sequelize.col('date'), '%Y-%M-%D')
		'=',
		string
	)
}*/
 // 4、方法一性能高,可以有效利用索引优化 - 索引本质是为了表单独存储的数据结构,可以快速定位到查询条件的数据位置,从而减少IO操作,提高性能
// 作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置
- 第二遍 2025.2.17 -
唯一索引保证唯一索引的组合唯一不重复
se.define(database, {
    name: DataTypes.STRING
},{
    underScore: true,
    timeStamp: false,
    indexes: [{
        unique: true,
        fields: ['date', 'sort']
    }]
})
SQL: CREATE UNIQUE KEY 'date','sort' 
 // 1、CREATE UNIQUE INDEX idx_date_sort ON `table` (`date`, `sort`)
ALTER TABLE table
ADD UNIQUE KEY 'date','sort'
 // 2、ADD UNIQUE KEY `table` (`date`, `sort`)
updateOnDuplicate: buldCreate(data, { ⭐️ // 索引冲突时,执行更新,而不是抛出错误 - 被指定的字段才会被更新,未指定的字段保持不变
    updateOnDuplicate: ['name', 'age']
})
se.define(database, {
    name: DataTypes.STRING
},{
    underScore: true,
    timeStamp: false,  // 3、timestamps: false, tableName: 'users'
    indexes:[{}]
})
- 第一遍 2025.2.13 -
 // 1、唯一索引的作用:确保索引字段的组合在表中是唯一的,不允许重复
 // 2、hasMany/belongsTo 这是关联表的写法,唯一索引不是这种写法 - indexes + unique + fields
/** sequelize.define(database, {...定义...}, {
	indexes: [
		{
			unique: true,
			fileds: ['date', 'day_sort']
		}
	]
}) */
 // 3、CREATE UNIQUE INDEX idx_date_day_sort ON 'daily' ('date', 'day_sort')
 // 4、updateOnDuplicate 用于保证在批量插入数据时,若冲突,执行更新而不是抛出错误 - 在 updateOnDuplicate 中配置的字段会被更新,未指定的保持不变
/** await Daily.bulkCreate(data, {
	updateOnDuplicate: ['value', 'weekday']
})*/
 // 5、属性名称错了 underscored: true - 将驼峰映射为下划线 + tableName:'daily' 指定数据库中表名 + timestamps:false 禁用时间戳字段
// 作业94:为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回?
- 第二遍 2025.2.17 -
NextResponse 包含了响应头响应体数据  // 1、状态码、响应头、响应体
.json() 可以将 NextResponse 转换成 json 数据再转成 js 对象方便数据操作  // 2、await res.json()
- 第一遍 2025.2.12 -
 // 1、(Nextjs) 接口返回的是 Response 对象,包含了服务器返回的所有信息 - 状态码、响应头、响应体,其中响应体 默认是只读的 ReadableSream,不能直接使用
 // 2、对的,详细是:先解析成 json 格式,然后转成 js 对象 - Response.json() 将响应体解析为 json 格式的数据,并将其转换为 js 对象:await res.json()
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第六遍 2025.2.17 -
mysql2为了 node 创建的数据库驱动可以直连数据库但是需要写比较多的 SQL 查询
	提供了 mysql2/promise支持 async/await 异步查询,管理多个数据库连SequelizeORM 将数据库表和行映射为对象模型比较好的函数编程操作
mysql2
import { createPool } from 'mysql2/promise'
const pool = createPool({ host, user, password, database });
const con = await pool.getConnect();
const [rows] = await con.execute('SELECT * FROM table');
sequelize
import { Sequelize, Modal, DataTypes } from 'sequelize';
const se = new Sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql2'
});
// 方法1
class Usr extends Modal {}
Usr.init({
    name: DataTypes.STRING
}, {
    sequelize: se,
    modalName: 'Usr'
});
// 方法2
se.define(database, {
    name: DataTypes.STRING
}, {
    underScore: true,
    timeStamp: false,
    indexes: [{}]
})
- 第五遍 2025.2.12 -
⭐️ // mysql2:execute
 // 1、sequelize:实例化参数错误:new Sequelize(database, user, password, { host: 'localhost', dialect: 'mysql2' })
 // 2、sequelize:init 参数错误 init({ name: DataTypes.STRING }, { sequelize: se, modalName: 'User' })
 // 3、sequelize:define 第三个参数应该是一些配置,例如 underscore: false ,这里把 init 和 define 的参数 弄混了
- 第四遍 2025.2.9 -
 // 2、sequelize:实例化的参数错误 new Sequelize(database, user, password, { host: 'localhost', dialect: 'mysql2'})
 // 3、sequelize:init 还需要第二个参数传递 连接实例、模型名称
- 第三遍 2025.1.31 -
⭐️ // mysql2:提供了 mysql2/promise 模块,允许 async/await 管理多个数据库连接;没有内置的对象关系映射ORM
 // 1、mysql2:导入依赖包错了 mysql2/promise
 // 2、mysql2:createPool的参数 少写了 database
 // 3、mysql2:获取连接的函数错了 await pool.getConnect()
 // 4、mysql2:需要使用 await const [rows] = await connection.execute('SELECT * FROM table')
⭐️ // ORM 把数据库的表和行映射为对象模型
 // 5、sequelize:有两种模型定义方法:一种是 define,另一种 extends Modal + init
/** 方法一 :const User = se.define('table', { id: Datatypes.number}, {
    underScore: false;
})
方法二:class User extends Modal {}
User.init({ id: Datatypes.number }, {
	sequelize: se, // 传递连接实例
	modalName: 'User' // 模型名称
})
最后调用: User.findAll({ attribute: ['id', 'name'] })
*/
- 第二遍 2025.1.26 -
 // 1、mysql2:依赖包是 mysql2/promise
 // 2、mysql2:创建连接池不需要 await - const pool = createPool({host, user, password, database})
 // 3、mysql2:获取连接时需要 await - const connection = await pool.getConnect()
 // 4、mysql2:查询数据时需要 await - const [rows] = await connectiom.execute('SQL')
 // 5、sequelize:实例化创建连接 const s = new sequelize(database, user, password, { host, dialect })
 // 6、sequelize 模型定义的底层 - 先扩展,再init定义
- 第一遍 2025.1.22 -
 // 1、mysql2:提供的连接的两个特点 - mysql2/promise 支持 async/await + 管理多个数据库连接池
 // 2、sequelize:将数据库的表和行映射为对象模型,直接通过 js 操作
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第六遍 2025.2.17 -
预渲染服务端渲染SSR-在请求时服务端直接将数据引入生成HTML客户端直接渲染不需要再使用客户端js 静态生成SSG 增量静态生成ISR-构建运行时间隔更新时间后端自动增量式创建新的HTML
SSG
export function Blog({slug})){ return <p>{slug}</p>}
export async function generateParams() {
    return [{slug:'first'}, {slug: 'second'}]
}
export const revalidate = 60; 
SSR
import { getServerSideProps } from 'next' ⭐️ // from 'next/server'
export function Blog({data})){ return <p>{data}</p>}
export async function getServerSideProps() {
    const res = await fetch('xxx');
    const data = await res.json();
    return {
        props: {data}
    }
}
- 第五遍 2025.2.12 -
⭐️ // SSR 在请求时,在服务端直接将数据引入,生成的 HTML 直接返回给客户端渲染,不需要客户端js执行
⭐️ // SSG 在客户端构建时,将页面提前渲染完成,使用 generateStateParams 返回动态路由参数列表,使用 revalidate 配置重新验证时间,增量更新
export function generateStateParams() {  // 1、SSG:少写了 async
export function getServerSideProps() {  // 2、SSR:少写了 async
- 第四遍 2025.2.9 -
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR
 // 2、缺少说明一下结果:客户端、服务端生成的结果都是 HTML,得说明一下
 // 3、SSR:函数名称、依赖包错误:import { getServerSideProps } from 'next/server'
 // 4、SSR:核心是在服务端提前获取数据 -> 传入组件
 // 5、SSG:函数名称错误 generateStaticParams
- 第三遍 2025.1.31 -
 // 1、记错函数名称,13.x 之前是 getStaticProps,现在已经改为 generateStaticParams,用于生成动态路由的参数列表
 // 2、SSR:data.json() 是异步的,异步解析数据,处理 json 数据转换为 js 对象 const res = await data.json();
 // 3、SSG:return 的应该是个路由列表 例如 return [{slug: 'first'}, {slug: 'second'}]
 // 4、SSG:export const revalidate = 60; // 页面重新验证时间       
- 第二遍 2025.1.26 -
 // 1、SSR getServerSideProps: fetch 数据需要 await: const data = await fetch('xxx') +  异步解析数据需要 await: const res = await data.json()
- 第一遍 2025.1.22 -
 // 1、SSG generateStaticParams 用于生成动态路由的参数列表
 // 2、SSG revalidate 用于定义页面重新验证时间 - 页面将重新生成,运行时生成/更新静态页面以增量更新
// 作业68:React hooks 的原理是什么?useEffect useState模拟实现
- 第七遍 2025.2.17 -
闭包+链表目的在不使用类组件的情况下也可以使用 state状态管理模拟生命周期
useEffect
const memorized = {};
const cursor = 0;
const render = () => { ReactDOM.render(<App />, document.getElementById('root')) };
function fakeUseEffect(cb, arr){
	const lastArr = memorized[cursor];
    const hasChanged = lastArr && lastArr.some((it, index) => !Object.is(it, arr[index]));
    if(!lastArr || hasChanged) {
        memorized[cursor] = arr;
        cb();
        render();
    }
    cursor++;
}
useState
function fakeUseState(init){
    const current = cursor;
    memorized[current] = memorized[current] ?? init;
    const useValue = (newV) => {
        memorized[current] = newV;
        render();
    }
    cursor++;
    return [memorized[current], useValue]
}
- 第六遍 2025.2.12 -
 // 1、原理:没有事件委托,主要是 闭包和链表 + 少写了 hooks 的创建目的:在不使用类组件也可以使用 state、状态管理、模拟生命周期
/**
	原理:闭包-每次渲染都拿到最新值;
	hooks按序存储在 fierNode.memorizedState,更新时按序执行全部 hooks,使用副作用链表收集副作用,在 commit 阶段挂载后执行副作用链表
    */
 // 2、ReactDOM.render(<App />, document.getElementById('root')); 
 // 3、使用 find 得到的是值,应该使用 some + 判断条件不是 Object.is 应该是 !Object.is
 // 4、少写了 值 的初始化: memorized[current] = memorized[current] ?? init;
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
 // 1、fakeUseEffect:ReactDOM.render(<App />, document.getElementById('root'));
 // 2、fakeUseState:cursor++; 忘记写游标自增了
- 第三遍 2025.1.13 -
 // 1、这里描述的是 hooks 的执行过程 - 大虐,第一遍/第二遍的时候都记得是用于给函数组件添加功能用的,这一次反而完全没想起来
/** 记忆 React hooks原理
	先描述【目的】:16.8的新增功能,可以在不编写类组件的情况下「使用 state」及其他特性,「状态管理能力」、「生命周期替代能力」、逻辑复用能力
	再描述【实现上述目的的方法】,也就是【原理】:怎么用来「使用 state + 状态管理能力」 - ①主要是利用「闭包」,组件每次渲染时,使用闭包保持其状态;怎么用来「生命周期替代」 - ②使用链表保存一系列hooks,存储在 fiberNode 上,「在 Fiber 树更新时,能从 hook 中计算出最终输出的状态和需要执行的相关副作用」
*/
/** 注意其实要写下 render 函数,因为 useEffect 触发 render 后,对应的游标要置0
*/
 // 2、fakeUseEffect:这里应该是判断「不等于」,有不相等的,代表依赖数组变化,需要更新:!Object.is(it, lastArray[index])
 // 3、fakeUseEffect:少写了「更新依赖数组」的逻辑:memorized[count] = array;
function fakeUseState(init) {
    const currentCount = count;
    let value = memorized[currentCount] ?? init;
     // 4、使用 value 会导致返回后,value 不会自动更新,和 memorized[currentCount] 失去了联系,本行改为:memorized[currentCount] = memorized[currentCount] ?? init;
    const setValue = (newValue) => {
        memorized[currentCount] = newValue;
         // 5、调用 useValue 必须触发 render:render();
    }
    count++;
    return [value, setValue]
     // 4、延续问题4,本行修改为:return [memorized[currentCount], setValue]
}
- 第二遍 -
❌❌ // 1、和第一遍的逻辑是一样的,都是想到了「函数组件」缺失「类组件的生命周期」,但是 hooks 不只是「生命周期替代能力」,还有「状态管理能力」、「逻辑复用能力」,函数组件也不止缺失「生命周期」,还包括 state(没有 hooks 的函数组件主要接收 props 来展示 UI)
❌❌ // 2、质疑是对的: hooks 在组件更新时是「全部按序重新执行一遍」,副作用链表是「在 hooks 执行过程中,如果 useEffect 依赖有更新」,就更新到副作用链表
// 和第一遍一样想到链表:第一遍想到存储位置,第二遍描述收集、执行过程 - 而不是原理
// 原理是从「链表+闭包」描述的:利用闭包保存状态,使用链表保存一系列的 hooks 与 Fiber关联。在 Fiber 更新时,就能从 hooks 中计算出最终输出的状态 和执行相关的副作用。
 // 5、fakeUseEffect:cursor应该使用其他变量保存,和下文中 setValue 形成了闭包 - 在异步情况下,cursor 可能会变化,导致索引错误 const currentCursor = cursor; 这里使用 currentCursor 进行索引
 // 6、更新使用闭包保存的 memorizedState[currentCursor] = newValue;
 // 7、fakeUseState:这里还要触发一次 render() 函数执行
- 第一遍 -
 // 1、描述得不准确:hooks 可以在不编写类组件的情况下使用 state 及React其他特性
 // 2、hooks 链表说得不错,链表是为了在更新时,以稳定的顺序执行,输出最终的状态和相关副作用
 // 3、还有一个 hooks 利用闭包保存状态,在组件每次渲染时通过闭包保持其状态
⭐️ // 注意 hooks:1、不要在循环、条件、嵌套、try/catch/finally 中使用 - 无法保证顺序的稳定
⭐️ // 2、只有在 函数组件和hooks 中能够调用 hooks
 // 5、fakeUseEffect:当前定义时的 cursor 需要被闭包保存 let currentCursor = cursor;
 // 4、fakeUseEffect:少处理了游标自增: cursor++;
// 作业67:React 怎么做的性能优化?
- 第六遍 2025.2.17 -
fiber 架构优先级渐进式渲染时间分片增量式 Diff
避免重新渲染的策略 - eagerState策略bailout策略
	eagerState策略如果新的 state 值和旧的无关可以直接渲染而不是再获取旧值再重新渲染
	bailout策略比较 state props 一致时跳过重新渲染
bailout策略的相关函数+hooks
	类组件的shouldComponentUpdate(nextProps, nextState)函数组件的 React.memo((props) => { return <div>{props.text}</div> })、useMemo+useCallback 
⭐️ //  React.memo 是浅比较 props,如果不变则直接使用                                  
- 第五遍 2025.2.12 -
⭐️ // 避免组件重新渲染
 // 1、少写了 函数组件 const MyMemo = React.memo(props => (<div>{props.text}</div>)) - 浅比较props,不变则直接复用 
// 记忆法:shouldComponentUpdate(类组件)/React.memo(函数组件) + 2个hook:useMemo/useCallback
- 第四遍 2025.1.23 -
- 第三遍 2025.1.18 -
useCallback ⭐️ // 每次渲染,函数都是重新生成的,用了这个就缓存住了
- 第二遍 2025.1.12 -
⭐️ // 主要是避免组件重新渲染
⭐️ // shouldComponentUpdate 是为了「类组件」的性能优化设计的生命周期
/**
	❌❌ 1、对于「函数组件」,React 提供了 React.memo 这个高阶函数来处理 -> 对组件的 props 进行浅比较,如果不变则跳过重新渲染
	const MyMemo = React.memo((props) => { return <div>{props.time}</div> })
*/
⭐️ // const expensiveMemoValue = useMemo(() => { return count * 2 }, [count])
// const expensiveCallback = useCallback(() => { setCount(count + 1) }, [count])
// 这里的记忆方法:shouldComponentUpdate(类组件)/React.memo(函数组件) 都是用于通过比较前后 props;2个函数组件的性能 hook:useMemo/useCallback
- 第一遍 -
 // 1、对,在运行时优化,但是这个「架构级别」的属于渲染层面的优化,性能方便还是两个优化策略
 // 2、一般不叫做「是否复用组件」,而是「避免组件重新渲染」
 // 3、eagerState:在useState 更新过程中,如果新值不依赖于旧状态,则直接使用新状态来更新组件
 // 4、只需要组件的 props 不变,即可避免重新渲染[state可能会影响子组件的 props,还是要考虑的]
 // 5、两个策略中,bailout 策略可以通过调用 API 来实现
 // 6、React.memo - 针对函数组件,对组件的 props 浅比较,不变则跳过重新渲染
 // 7、shouldComponentUpdate - 针对类组件,在组件更新前调用,自定义是否需要重新渲染
 // 8、useMemo/useCallback - 依赖项不变,返回之前的缓存
 // 9、除了两个策略,还有 VDOM + Diff - 减少对真实 DOM 的操作,避免不必要的回流重绘
// 作业64:Vue渲染怎么实现的?
- 第七遍 2025.2.17 -
AOT 预编译参与全程
模版编译
	parse 词法分析语法分析得到 AST
    optimize 静态节点标记优化 Diff 时方便跳过
    generate 生成 render 函数
双向数据绑定
	数据劫持 + 发布订阅模式实现 Object.defineProperty(obj,属性,{get, set})/Proxy
	初始化组件实例 Watcherset 属性变量时将正在运行的 Watcher 收集到组件的 Dep ⭐️ // getter
    更新/初次渲染组件时访问 get触发所有订阅的 Watcher 执行 - dep.notify() ⭐️ // setter
执行 render 函数组合成 VDOM
执行 patch/diff得到最小 UI 更新量应用到真实 DOM
- 第六遍 2025.2.11 -
 // 1、模版编译的阶段名称错误:parse - optimize - generate
 // 2、函数使用方法错误:Object.defineProperty(obj, 属性, { get, set })
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 -
1模板编译 ⭐️ // AOT 参与全程 
 // 1、getter 被劫持是结果,「劫持方式是 Object.defineProperty(target, property, {get:()=>{}, set:()=>{}})」/Proxy
 // 2、「每一个响应式对象属性都会创建一个对应的 Dep 类的实例」 -> 我写的这里理解有误,同时数据劫持之后,触发订阅的时机是「渲染组件,实例化 Watcher 时,执行到 getter 触发对应的 Dep 收集正在执行的 Watcher」
- 第二遍 -
 // 1、执行步骤的最后一步是 generate
// 三个步骤分别作用:① parse 词法解析+语法解析 template 得到 AST ② optimize 对AST静态内容优化 ③ generate 递归转换 AST 得到 render 函数
 // 2、AOT 预编译功能是针对 template 的,不是针对 js 的(第一次做的时候知道,这次做的时候误以为预编译是针对 js 的 -> 其实通过 AOT 和 JIT 的区别就可以推断,AOT 既然依赖的是静态编译,就是需要像模版语法这种,方便标记静态节点 - js 那么动态肯定是不行🚫的)
⭐️ // Dep 用于依赖收集,作为容器管理多个订阅者 Watcher
- 第一遍 -
 // 1、整个渲染「不止是 render + patch」,应该分成4步,①html:先「模版编译,得到render函数」 ②js:「实例化组件,同时收集依赖」(也就是数据双向绑定-实现响应式数据) ③html+js:「执行渲染函数/更新,得到 VDOM」 ④Diff/patch:「计算最小更新,应用到真实 DOM」
// 作业62:fiber架构是什么?优点和实现方式?
- 第七遍 2025.2.17 -
Schedueler调度器优先级调度不直接参与渲染宏观调控
Reconciler架构器时间分片在重绘前根据 shouldYield 判断是否够时间分片 ⭐️ // 协调器
Render渲染器对应 commit 阶段
Schedueler Reconciler异步因为这两个在内存中计算随时可能被以下情况打断 报错时间分片时间不够有更高优先级
Render同步将最小更新 UI 量应用到真实 UI无法被打断
优点
1时间分片之前 react 是同步渲染的因为要更新整棵 VDOM 对比+计算较长时间占用主线程阻塞重绘使用 fiber架构后利用中断恢复机制将渲染分成多个小的渲染任务单元 ⭐️ // 在重绘和空闲时间执行
2渐进式渲染 + 优先级调度优先渲染视口部分的视图或者用户交互
3增量式 Diff不再整棵树比较实现 Diff而是根据渲染任务的执行逐步计算 diff 得到最小更新 UI提高 diff 效率
- 第六遍 2025.2.10 -
 // 1、时间切片的优点 - 少写了:之前 react 会对整棵树 对比+更新,同步执行,会长时间占用主线程,阻塞重绘 ; 描述优化:小的单元任务,在空闲 + 重绘判断执行
- 第五遍 2025.1.22 -
// ⭐️ 增量式 Diff- 根据任务的执行进度,逐步比较和更新部分节点
- 第四遍 2025.1.18 -
 // 1、忘记中文:Scheduelor 是调度器
 // 2、少:应该先说明 react 每次更新都要递归构建整棵 fiber 树+比较+更新,长时间占用主线程,会阻塞重绘 + 改:[Scheduelor+中断恢复机制]
 // 3、改:[Scheduelor+优先级调度机制]
 // 4、修改描述:不是一次性比较整棵树,而是根据任务执行进度,逐步比较、更新部分节点 - 减少了每次比较的范围,提高了效率
- 第三遍 -
// ⭐️ 不是「三大部分」,是「三个主流程」
 // 1、不是「Commit」,是「Reconciler」,叫做「协调器」,作用是「调用 shouldYield 判断当前 Time Slice 是否有剩余时间」
 // 2、不是因为「异步」才可以被打断,是以为「他们都在内存中执行,不会更新宿主环境 UI」
 // 3、不是「Reconciliator」,是「Render」「渲染器」
 /** 上述三个主流程记忆法:
	Scheduler 调度器 - 优先级宏观调控 
	Reconciler 协调器 - render 阶段,用于判断时间切片剩余时间
	Render 渲染器 - commit 阶段(更新UI)*/
 /** 1、第一个优点的关键词是「渐进式渲染」。
	描述内容和上述很像,但是「占用主线程」不是重点,是「长时间」占用主线程。
	不是「react 的解析、渲染」在占用,是「react 每次更新都要递归构建整棵 fiber 树、比较、更新」,重点要突出导致访问卡顿感的原因是渲染卡住了。
	实现方式:Scheduler调度器 + 中断恢复机制*/
 // 2、第二个优点的关键词是「优先级调度」。实现方式:Scheduler 调度器 + 优先级调度机制
 /** 3、第三个优点,现在描述的是怎么实现 diff,但是 fiber 对 diff 的优化主要是「增量式Diff」。
	「不是一次性比较整棵树,而是根据任务的执行进度,逐步比较和更新部分节点,根据时间和优先级安排,逐步更新,减少了每次比较的范围,提高了效率」。
	实现方式:Scheduler调度器 + 中断恢复机制 + Reconciler协调器 + 双缓存机制*/
 /** 4、少一个优点「时间分片」。
	「有效利用浏览器空闲时间,在每帧开始之前检查剩余时间,是否执行一个小的渲染任务单元」。
	实现方式:Reconciler协调器 + 中断恢复机制*/
- 第二遍 -
⭐️ // 总结:「渐进式渲染」、「暂停恢复渲染能力」、「优先级调度」、「时间分片管理」
⭐️ // 「优先级调度器」其实是两样,「任务调度」 + 「优先级管理」
 // 1、这里缺少一个 Diff 算法的执行优化:增量式 
- 第一遍 -
 // 1、不是「连续的 js 执行」会长时间占用主线程,是「React递归构建整颗 DOM 树、比较、更新」
 // 2、明确 React 由于每次构建的都是整颗树 + 之前是同步渲染 - fiber 做到了异步渲染
 // 3、分成小模块的自然也不只是「代码执行」,是「渲染工作」被分解成了「小任务单元」 + 同时可以「暂停 + 恢复」 -> 使得 在每帧开始前检查剩余时间,能否执行的也就是「小渲染任务单元」
 // 4、少写了:渐进式渲染
 // 上述写的都是原理,并不是具体实现 1、双缓存机制 current tree 和 work-in-progress tree 2、任务调度和优先级管理 3、Diff 算法优化为增量式 Diff
// 作业58:React和Vue是怎么描述UI的
- 第六遍 2025.2.17 -
React:JSX=HTML+JS类组件+函数组件state组件状态+props传入的属性花括号{}
Vue:模版语法 + 指令 v-modelv-if/v-else-if/v-elsev-showv-for + 双花括号
- 第五遍 2025.2.10 -
 // 1、React 少写了 :类组件+函数组件
 // 2、Vue 少写了:模版语法 + v-show
- 第四遍 2025.1.22 -
⭐️ // Vue 少写了:v-show
- 第三遍 2025.1.18 -
- 第二遍 -
 // 1、React 少写了 ①自定义组件-函数组件+类组件 ②state-状态,组件内部自变量;props-属性,外部传入自变量
⭐️ // Vue 指令还有 v-for v-bind v-else/v-else-if v-on v-model等
- 第一遍 -
// ❌ 1、React 少写了: 可以自定义组件 + 使用花括号{}将js嵌入JSX {note} + state-状态,组件内部自变量、props-属性,其他组件传入的自变量
// ❌ 2、Vue 少写了:模拟语法 - 结合数据+预先定义的模版 + 使用双大括号展示数据 + 指令v-if="show"/v-for="item in items"

LTN①⑥

LTN①⑥ 工具推荐做题周期 2.4 - 2.14 89题(LTN1-23, LTN2-45, LTN3-22)

LTN1 【推荐做题时间 02-04 - 1题 实际做题时间: 2.10】 ✅作业6:new操作符实例化一个对象的过程

LTN1 【推荐做题时间 02-05 - 1题 实际做题时间: 2.10】 ✅作业66:React如何处理错误

LTN2 【推荐做题时间 02-05 - 11题 实际做题时间: 2.10 + 2.11】 ❌作业58:React和Vue是怎么描述UI的 ❌作业62:fiber架构是什么?优点和实现方式? ✅作业71:React为什么要合成事件?和原生事件有什么不同? ✅作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口) ✅作业53:胜利者一无所获 说的是什么 ✅作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的 ✅作业85:Lambdas是什么 ✅2024.12 模拟实现promise ✅2024.05 rem是基于什么原理进行适配的? ✅2021.04 05-3 白屏优化? ✅作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?

LTN1 【推荐做题时间 02-06 - 3题 实际做题时间: 2.11】 ✅作业3:迭代器原理 ✅作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果 ✅2021.06 子盒子在父盒子中水平垂直居中有几种方法?

LTN2 【推荐做题时间 02-06 - 6题 实际做题时间: 2.11 + 2.12】 ✅作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 ❌作业64:Vue渲染怎么实现的? ✅作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期 ❌作业67:React 怎么做的性能优化? ❌作业68:React hooks 的原理是什么?useEffect useState模拟实现 ✅作业78:使一个标签看不见的几种样式及其区别

LTN1 【推荐做题时间 02-07 - 9题 实际做题时间: 2.12 + 2.13】 ✅作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ❌作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅2021.07 数组扁平化(一层、全部展开、指定深度) ❌作业94:为什么接口查询之后,一般都要使用 .json() 来转化一次数据后再返回? ❌作业95:唯一索引什么作用?Sequelize 中 define modal 的时候,怎么创建组合字段唯一索引?SQL怎么写?updateOnDuplicate 是什么作用?怎么配合唯一索引。 define 中的第三个参数中常见配置 ❌作业 96:Sequelize 中 Date 的怪问题 - POST 存储 DATE 类型,GET 传参数2025-01-27 STRING,无法直接查询,有那些性能高的查询方法?索引是什么,为什么性能高? ❌作业97:Sequelize 中查找某条数据,如果存在就更新;不存在就创建一条 ❌作业98:sequelize 怎么写关联表的查询,指定关联的 column 是date

LTN2 【推荐做题时间 02-07 - 8题 实际做题时间:2.14】 ✅2024.11 简单的发布 - 订阅模式实现 EventEmitter ✅2024.10 怎么实现跨域 ✅2022.08 包管理工具 ❌2020.07 对象分类、Promise按序执行、实现map ✅2019.10 两栏,左边固定,右边自适应的布局 ✅2019.10 三栏,两边固定,中间自适应 ❌2024.11 第十七章 事件 ✅2024.09 第八章 对象、类和面向对象编程 小结

LTN2 【推荐做题时间 02-08 - 3题 实际做题时间:2.14】 ✅2021-06 Position属性 - 占位?相对什么定位? ❌2021-07 常见Webpack问题 ✅2019-06 第四章 变量、作用域和内存问题 小结

LTN3 【推荐做题时间 02-08 - 14题 实际做题时间: 2.14 + 2.15】 ✅作业10:分别用两种方式新建一个映射实例、一个集合实例 ✅作业29:Promise的进度通知 ✅作业44:怎么能够不把所有事情、语言往心里去 ❌作业45:原生+canvas 显示一张png格式的图片 ✅作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置 ✅作业50:学习的感觉是? ✅作业51: 如何停止过度思考?如何停止焦虑 ✅作业52:道可道,非常道 翻译一下,有什么含义 ✅作业56:对那些没有 deadline 的事情,怎么解决拖延症 ❌2024.10 第十四章-第十五章 DOM、DOM扩展 ✅2024.11 第十六章 DOM2和DOM3 ❌2024.12 模拟实现instanceof ✅2021.04 02 网络协议 - IP/UDP/TCP协议 ❌2019.07 h5新标签和语义化,块/行内元素

LTN1 【推荐做题时间 02-09 - 7题 实际做题时间: 2.15】 ✅作业25:简单写下 请求创建过程 ✅作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? ✅作业55:flex 常见缩写 ✅作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ✅作业61:常见的数据结构有哪些 ✅作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅作业76:显卡的作用?

LTN2 【推荐做题时间 02-11 - 8题 实际做题时间: 2.15 + 2.16】 ❌作业2:支持迭代器的原生语言特性有哪些? ✅作业43:TED 怎么提升自信 ✅作业42:TED 如何和大脑正确交流沟通 ✅作业39:shim 和 polyfill 区别 ❌2019.07 为什么要清除浮动?怎么清除浮动? ✅2021.06 CSS 选择器 - 权重/匹配方式 ❌作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅2021.07 bind、apply/call三者异同+apply/call实现bind ❌作业4:什么是生成器?有什么作用? ✅作业20:浏览器打开一个 URL 发生了什么 ✅2021.09 Object属性排序、与Map的区别

LTN3 【推荐做题时间 02-11 - 8题 实际做题时间: 2.16】 ✅作业17:对斐波那切数列进行尾调用优化 ✅作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现 ✅作业34:写出强缓存、协商缓存的流程及相关属性 ✅作业57:“假装成功”的意义是什么 ✅2021.06 BFC特点、触发、存在/解决的问题 ✅2020.03 改变原数组+结束循环+性能排序 ❌2021.07 防抖节流 ❌2024.12 模拟实现jsonp

LTN2 【推荐做题时间 02-12 - 3题 实际做题时间: 2.16】 ❌作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ✅作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?

LTN2 【推荐做题时间 02-13 - 2题 实际做题时间: 2.16】 ✅作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ❌2023.07 clickhouse、mysql、mongodb异同

LTN2 【推荐做题时间 02-14 - 4题 实际做题时间: 2.16】 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业80:对DOM 树的理解 - 定义和作用 ✅作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?


做题记录

// 作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?
- 第四遍 2025.2.16 -
13.x Link 之后都直接渲染为 a 标签了所以 Link 添加属性 href 即可不需要在里面写 a 标签
路由路由管理系统改为 src/app/pages.tsx + layout.tsx + global.css
api 也改为 src/app/api 管理后端接口路由
- 第三遍 2025.1.31 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.22 -
 // 1、只写了「文件路由系统」的变更,还少写了「后端路由系统」的变更 从 pages/api 变更为 src/app/api
// 作业80:对DOM 树的理解 - 定义和作用
- 第四遍 2025.2.16 -
DOM 是对 HTML 的对象模型表示 HTML 的结构 ⭐️ // 对象化表示 - 树形结构的对象模型
浏览器是浏览器渲染页面的基础按照 DOM 结构渲染 ⭐️ // 浏览器根据 DOM 结构构建页面布局
框架可以通过 VDOM 模拟真实 DOM计算出最小更新量 UI 再应用到真实 DOM提高性能 ⭐️ // 提高页面更新性能
js提供了可以和 HTML 交互的接口可以访问
- 第三遍 2025.1.31 -
 // 1、少写了浏览器视角-渲染页面的依据 和 框架视角-VDOM Diff 计算提高更新性能
- 第二遍 2025.1.25 -
- 第一遍 2025.1.24 -
⭐️ // DOM 是 HTML 文档的「对象化表示」,是按照 HTML 结构转换的树形结构的对象模型
⭐️ // 提供了一个编程接口 - DOM 是 js 可以访问、修改 HTML 的基础
 // 1、除了 js,还少写了 浏览器 + 框架 视角
/**
	对浏览器而言,DOM 是「渲染页面的依据」,浏览器根据 DOM 结构构建页面布局
	对框架而言,框架通过比较 虚拟DOM 和实际 DOM 树的差异,以最小 UI 更新量,提高页面更新性能
*/
// 作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序)
- 第四遍 2025.2.16 -
SELECT DISTINCT t.name, 
    CONCAT('(', t.age, ')') AS t_age, 
    UPPER(t.sex) AS t_sex, 
    COUNT(*) AS num_with_null,
	COUNT(t.nums) AS num_without_null,
    SUM(t.age) AS sum_age, (MAX MIN AVG)
FROM table AS t, serials AS s
WHERE t.id = 1 AND
	(t.price > 10 OR
    t.price IS NULL OR
    t.price BETWEEN 5 AND 10 OR
     t.price IN (1, 2) OR
	YEAR(t.time) = 2025
)
GROUP BY t.age ⭐️ // 优先于 SORT 执行
SORT BY t.age DESC ⭐️ // LIMIT 在 SORT 之后
- 第三遍 2025.1.30 -
- 第二遍 2025.1.26 -
- 第一遍 2025.1.23 -
 // 1、由于 from 中有两张表,如果查询的 column 名在两张表都存在,有重名就会报错 - 所以查询的时候最好声明表名 t.name1 t.age
SELECT constinct name1, age,   // 2、关键词错了 distinct
    concat(sex, height) as answer2, // ⭐️ concat(t.sex, '(', t.height, ')') as answer2
    toUpperCase(weight3) as weight3,  // 3、函数名错了 upper
    count(name) as name42, count() as name41,  // 4、关键词参数错了 包含 null - 查询时传递 count(*) as name41_with_null;不包含 null - 查询时包含 column 名是对的
    sum(age) as age5, max(age) as max5, min(age) as min5, avg(age) as avg5
from table as t, sex as s
where t.id = 1 
And 
(t.price > 10 
 or t.price = Null  // 5、关键词错了:IS 操作符主要用于判断某个值是否为 NULL,且 t.price = null 的比较结果始终是 UNKNOWN, 因为 NULL 表示未知的值,无法确定它是否和另一个值相等 - t.price is null
 or t.price between 5 to 10  // 6、关键词错了 between-and:t.price between 5 and 10
 or t.price in [1,2]  // 7、关键词错了,应该是():t.price in (1,2)
or year(s.time) = 2025)
group by age
sort age desc  // 8、关键词错了 sort by
// 作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?
- 第四遍 2025.2.16 -
path.join() 纯拼接路径
path.resolve() 解析绝对路径默认绝对路径为 /
process.cwd() 当前 node 执行所在环境路径
__dirname 当前文件所属路径
- 第二遍 2025.1.29 -
⭐️ // path.resolve():绝对路径解析
- 第二遍 1.26 -
⭐️ // path.resolve():把 / 当做根路径
- 第一遍 2025.1.22 -
⭐️ // path.join(a,b):纯拼接
 // 1、path.resolve(a,b):是「绝对路径解析」,把 / 当做根目录 -> /a/b
 // 2、process.cwd():node 执行时所在目录
// 2020.03 改变原数组+结束循环+性能排序
- 第三遍 2025.2.16 -
改变原数组: pop shift push unshift sort reverse splice fill
结束循环:使用 return break 无法退出循环 forEach map reduce filter
性能排序: for > for...of > forEach > map > for...in
- 第二遍 2025.1.21 -
- 第一遍 -
// 2021.06 BFC特点、触发、存在/解决的问题
- 第三遍 2025.2.16 -
BFC:①独立容器 不和外部的 float 重叠 从上至下垂直排列 计算盒子空间时包含浮动元素
触发:①float: 不为 none position: absolute/fixed 根元素 
overflow: hidden/scroll/auto ⭐️ // overflow不为visible
display: flex/table-cell/inline-block ⭐️ // 少写了 table-captoin
存在的问题 BFC 内部的上下盒子 margin 会重叠 解决方案给父元素 display: flex ⭐️ // 外边距重叠
- 第二遍 2025.1.21 - 
- 第一遍 -
// 作业57:“假装成功”的意义是什么
- 第三遍 2025.2.16 -
心态能够改变行为 -> 行为同时也能够改变心态扩展占用空间的姿势可以提升控制激素的分泌抑制压力激素的分泌
⭐️ // 控制激素-睾丸酮,压力激素-皮质醇
Fake it until you become it! You deserved to be here.
- 第二遍 2025.1.21 -
⭐️ // 提高睾丸酮,降低皮质醇 - 提高自信心,降低压力 fake it until you become it
- 第一遍 -
// 作业34:写出强缓存、协商缓存的流程及相关属性
- 第三遍 2025.2.16 -
请求链接 url 有缓存时直接使用缓存强缓存生效
如果本地有缓存但缓存失效 - 服务器状态码为 304 使用缓存并刷新缓存有效时间协商缓存生效
If-modified-since/Last-modified If-none-match/Etag
Cache-control:max-age=2000; [优先级高]
Expires deadline 事件
- 第二遍 2025.1.21 -
⭐️ // 刷新本地缓存时间
- 第一遍 -
// 作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现
- 第三遍 2025.2.16 -
Promise.all([A(), B()]).then(C);
(async function () {
    await Promise.all([A(), B()]);
    C();
})()
- 第二遍 2025.1.21 -
- 第一遍 -
// 作业17:对斐波那切数列进行尾调用优化
- 第三遍 2025.2.16 -
function fib(n) {
    if(n<2){
        return n
    }
    return fib(n-1)+fib(n-2)
} 0 1 1 2 3 5
function optiFib(a = 0, b = 1, n) {
    if(n===0) {
        return a
    }
    return optiFib(b, a+b, n-1)
}
- 第二遍 2025.1.20 -
- 第一遍 -
// 2021.09 Object属性排序、与Map的区别
- 第五遍 2025.2.16 -
Object属性排序: 正整数>字符串>负数>浮点数>Symbol
Map区别:①任意值 VS Object只有字符串/Symbol 记录插入顺序 VS Object 无法记录 Map 可迭代对象  数据量大的时候使用 Map内存小增删快  数量计算方便 m.size() VS Object.keys(obj).length
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.21 -
Object属性排序: 正整数>字符串>负数>Symbol   // 1、少写了浮点数
- 第一遍 -
// 作业20:浏览器打开一个 URL 发生了什么
- 第五遍 2025.2.16 -
1浏览器进程 UI线程 - 将用户输入内容链接转换成完整的 url
2IPC 协议传输给 网络线程 查询是否有缓存 - 有缓存且有效直接返回 ⭐️ // 强缓存生效
	- 无缓存/有缓存但失效进行 DNS 解析得到 ip
    - TCP 队列排队三次握手建立连接
    - 组装请求头包括 cookie 等信息
    - 发送请求 301/302 Moved Permanently/Temperaily 重定向使用响应头中的 Location 重新导航
    	 	  304 使用缓存并刷新缓存有效时间
              200 若是 o-stream 下载类型触发下载结束导航若是 html  渲染进程建立管道
3渲染进程主线程预解析线程合成线程
	- 建立管道后边下载边解析并使用预解析线程遇到 jscss提前下载
    - 下载后解析还没完成出现 解析白屏
    - 下载后会通知浏览器进程更新 页面安全锁url前进后退按钮
主线程解析 DOMDOM树 - 样式解析CSS样式表 - 布局布局树 - 分层分层树 - 合成合成指令 ⭐️ // 绘制(绘制指令列表
合成线程栅格化图块-位图GPU 进程加速
浏览器进程 UI线程合成
- 第四遍 2025.1.28 -
- 第三遍 2025.1.25 -
- 第二遍 2025.1.20 -
 // 1、协议名错了:IPC - 进程通信协议
 // 2、少了:有缓存但缓存失效
 // 3、应该是先判断状态码,200的状态码下再判断类型
 // 4、少写了 304,使用本地缓存,并刷新缓存有效时间
- 第一遍 -
// 2021.07 bind、apply/call三者异同+apply/call实现bind
- 第五遍 2025.2.15 -
改变函数中 this 指向不传第一个参数时this 指向 window
bind 改变 this 指向后返回函数副本applycall 改变 this 指向后立即执行apply 的第二个参数是 类数组/数组call 的第二个参数是接收多个参数
fun.bind(thisArg,...args)
Function.prototype.fakeBind = function(thisArg, ...args1) {
    const func = this;
    return function F(...args2) {
        if(func instanceof F) {
            return new func(...args1, ...args2);
        }
        func.call(thisArg, ...args1, ...args2);
    }
}
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、bind 实现应该绑定在原型对象上实现: Function.prototype.fakeBind - 这样 this 才是指向调用 bind 的函数
- 第二遍 2025.1.19 -
 // 1、「bind 有多个参数,第一个是 this 指向,后面接收多个参数 」...arg1
 // 2、return new cb(...arg1, ...arg)
 // 3、cb.apply(thisArg, [...arg1, ...arg])
- 第一遍 -
 // 1、遗漏了判断是否为 new 调用
/**
return function F(...arg2) {
	if(func instanceof F) {
		return new func(...arg1, ...arg2);
	}
	func.call(thisArg, ...arg1, ...arg2);
}*/
// 2021.06 CSS 选择器 - 权重/匹配方式
- 第四遍 2025.2.15 -
#id > .red a[href] :hover LVHA> div ::after> *
从右向左 解析
!important > 行内 > 外联/内联只和下载完顺序有关
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
!important > 内联 > 行内   // 1、行内 > 内联/外联
// 作业39:shim 和 polyfill 区别
- 第四遍 2025.2.15 -
shim 垫片提供新的 API解决兼容性问题利用浏览器原生功能实现功能优雅降级
polyfill 补丁不提供新的 API利用原生功能实现浏览器功能 ⭐️ // 实现浏览器原生 API 缺少的功能
- 第三遍 1.28 -
⭐️ // shim:优雅降级
⭐️ // polyfill:实现浏览器原生 API 中缺少的功能
- 第二遍 1.25 -
⭐️ // 实现浏览器原生 API 缺少的功能
- 第一遍 1.20 -
 // 1、记反了,shim 是处理兼容性问题-优雅降级,polyfill 是实现浏览器不支持的原生API-实现原生API中缺少的功能
// 作业42:TED 如何和大脑正确交流沟通
- 第四遍 2025.2.15 -
1I want it, I like it , I have chosen it!
大脑只会对我们给它的语言和图像大脑也天然追逐快乐(把想做的事情关联到快乐把不想做的事情停留在原地绑定到痛苦)
2Make it familiar! 大脑喜欢熟悉的东西
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
 // 1、少写了:I like it!I want it!I've chosen it! 大脑天然逃避痛苦、追逐快乐:将想要做的事情和巨大的快乐联系起来;将停滞在当下的痛苦细细描述
 // 2、大脑喜欢熟悉的事情:让大脑对想要做的事情变熟悉
// 作业43:TED 怎么提升自信
- 第四遍 2025.2.15 -
1repetitionrepetitionrepetition 重复一千遍做好准备
2不批评做得不好的事情而是夸奖做得好的事情 做自己最好的引导者
3用自己的语言解释世界 - 世界上没有客观只有主观
- 第三遍 1.28 -
- 第二遍 1.25 -
- 第一遍 1.20 -
 // 1、少写:重复训练!用足够多次的练习带来信心
 // 2、少写:而是不断夸奖正确行为 - 自己的或别人的 - 引导自己去学习和改变
2用自己的语言解释世界
// 作业76:显卡的作用?
- 第四遍 2025.2.15 -
合成图像放入到后缓存区
- 第三遍 2025.2.10 -
- 第二遍 2025.2.2 -
⭐️ // 合成新的图像,并将图形保存到后缓存区
// 作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次?
- 第五遍 2025.2.15 -
异步React 合成事件 + 生命周期函数 react 提供了批量执行机制在执行事件处理函数执行过程中如果遇到 setState 会投入一个队列中执行结束后统一处理 setState 修改 state 一次触发一次渲染
同步原生事件setState 执行一次触发一次渲染
- 第四遍 2025.2.10 -
- 第三遍 2025.2.2 -
 // 1、除了合成事件,还有「生命周期函数」中都是异步
⭐️ // 批量处理机制 + 更新一次 state ,触发一次重新渲染
⭐️ // 原生事件:setState 一次,就会触发一次渲染
- 第一遍 -
 // 1、除了合成事件,还有生命周期函数
// 作业61:常见的数据结构有哪些
- 第五遍 2025.2.15 -
按逻辑结构划分结构型队列数组链表
非结构型
- 第四遍 2025.2.9 -
- 第三遍 2025.2.2 -
 // 1、少写了一个类型:数组
// 作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例
- 第六遍 2025.2.15 -
高阶函数 - 输入/输出是函数map
高阶组件 - 输入是组件
const high = (My) => {
    return class extends React.Component {
        constructor(props) {
            super(props);
        }
        render() {
            return <My {...this.props} />
        }
    }
}
副作用 - 函数编程的说法除了返回值还会对外部产生影响IO下载交互全局变量等 ⭐️ // 改变输入参数
- 第五遍 2025.2.9 -
- 第四遍 2025.2.2 -
⭐️ // 副作用,少写了:除了返回值
render() {
    return Child
     // 1、<Child {...this.props} />
}
// 作业55:flex 常见缩写
- 第六遍 2025.2.15 -
flex: none; 0 0 auto
flex: 1; 1 1 0
flex: inital;  0 1 auto
flex: auto; 1 1 auto
- 第五遍 2025.2.9 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
- 第四遍 2025.2.2 -
 // 1、少写了一个 flex: initial; // 0 1 auto
- 第二遍 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
- 第一遍 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
// 作业74:起舞弄清影 是那首诗?谁写的?下一句是什么?
- 第四遍 2025.2.15 -
苏轼 水调歌头 明月几时有
明月几时有把酒问青天不知天上宫阙今夕是何年
我欲乘风归去又恐琼楼玉宇高处不胜寒
起舞弄清影何似在人间
转朱阁低绮户照无眠
不应有恨千里共婵娟 ⭐️ // 不应有恨,何事长向别时圆。人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟
- 第三遍 2025.2.9 -
⭐️ // 顺序反了 - 起舞弄清影,何似在人间。转朱阁,低绮户,照无眠。
⭐️ // 不应有恨,何事长向别时圆。人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。
- 第二遍 2025.2.2 -
 苏轼/苏东坡   // 1、题目记不起来:《水调歌头 明月几时有》
 // 2、乱序:起舞弄清影,何似在人间
// 作业25:简单写下 请求创建过程
- 第六遍 2025.2.15 -
function xmlRequest(url) {
    const xhr = new XMLHttpRequest();
    xhr.onerror = ()=>{};
    xhr.ontimeout = ()=>{};
    xhr.onreadystatechange = function(res) {
        switch(res.readySate) {
            case 0:
                break;
            case 4:
                if(this.status === 200 || this.status === 301) { ⭐️ // 304
                    console.log(this.responseText);
                }
                break;
        }
    }
    xhr.open('GET', url, true);
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader(key, value);
    xhr.send();
}
- 第五遍 2025.2.9 -
case 4:
	if(res.status === 200 || res.status === 301) { ⭐️ // status 在this上
		console.log(this.responseText)
}
- 第四遍 2025.2.2 -
xhr.onreadystate = function (res) {   // 1、函数名称错误:onreadystatechange
	switch(this.readyState) {  // 2、属性不在 this 上,在 res 上:res.readyState
		 case 0:// 还未开始请求
		  // 3、switch 中 0 表示请求还没开始,要使用 break 跳出循环
}}
- 第二遍 -
 // 1、属性名称记错 :res.readyState
- 第一遍 -
 // 1、open 的参数顺序记错了:xhr.open('GET', url, true)
// 2021.04 02 网络协议 - IP/UDP/TCP协议
- 第二遍 2025.2.15 -
应用层 HTTP
传输层 TCP/UDP通过端口找程序
网络层 IP通过 ip 找电脑
网际层 MAC通过 mac 找网络服务
传输顺序 data]TCP]IP]---data]TCP]---data
- 第一遍 2025.1.9 -
// 作业56:对那些没有 deadline 的事情,怎么解决拖延症
- 第二遍 2025.2.15 -
 deadline 的事情脑中有一个怪兽可以警醒
但人生很多事情是不做出行动就不会有改变要做出行动掌握到主动性才可以有所改变
想要解决人生的拖延症就要明确人生是有限的人就活这么多天不去做就少一天
- 第一遍 2025.1.9 -
// 作业52:道可道,非常道 翻译一下,有什么含义
- 第二遍 2025.2.15 -
如果真理是能够被语言描述的那就不是永恒不变的道了
语言是有局限性的真理只能身教无法言传
- 第一遍 2025.1.9 -
// 作业51: 如何停止过度思考?如何停止焦虑
- 第二遍 2025.2.15 -
思考是一件非常容易上瘾的事情不想过度思考的话就把思维当做一条线出现get itlet it go
如果焦虑应该是害怕自己会失败那就把可能导致失败会导向成功的可能性都列出来
- 第一遍 2025.1.9 -
// 作业50:学习的感觉是?
- 第二遍 2025.2.15 -
学如飞鸟过脑内了无痕
- 第一遍 2025.1.9 -
// 作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置
- 第二遍 2025.2.15 -
inputsize宽度value 初始值maxLength 最大长度 ⭐️ // 单行,单位为字符数
textareacols宽度标签中间内容 为初始值rows 高度 ⭐️ // 多行,单位为字符数
option value 时取 value没有 value 属性时取标签中间内容
- 第一遍 2025.1.9 -
// 作业44:怎么能够不把所有事情、语言往心里去
- 第二遍 2025.2.14 -
1It is not about me. 和我无关的事情看看说话人的目的
2It is about me. 给自己一些同情同时学会表达自己的情绪没有指责只是表达
you will always keep your value
- 第一遍 2025.1.9 -
// 作业29:Promise的进度通知
- 第二遍 2025.2.14 -
class MyPromise extends Promise {
    constructor(executor) {
        let handles = [];
        super((resolve, reject) => {
            return executor(resolve, reject, (state) => {
                handles.forEach(it => it(state));
            })
        })
        this.handles = handles;
    }
    notify(fn) {
        this.handles.push(fn);
    }
}
const p = new MyPromise((resolve, reject, notifyFunc) => {
    function count(n) {
        if(n>0){
            setTimeout(() => {
                count(n-1);
                notifyFunc(`${n*20} remained`)
            }, 0)
            ⭐️ // 这里的写法可以优化,需要间隔时间执行的只有 count(n-1)
            /**
            	notifyFunc(`${n*20} remained`);
            	setTimeout(() => count(n-1), 0);
            */
        } else {
            resolve()
        }
    }
    count(5);
})
p1.notify((it) => { console.log(it) })
p1.then(() => console.log('completed'))
- 第一遍 2025.1.9 -
// 作业10:分别用两种方式新建一个映射实例、一个集合实例
- 第二遍 2025.2.14 -
const m1 = new Map([['a', 1], ['b', 2], ['c', 2]]);
const m2 = new Map().set('a', 1).set('b', 2).set('c', 3);
const s1 = new Set([1,2,3]);
const s2 = new Set({ *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3
} })
- 第一遍 2025.1.9 -
// 2021-06 Position属性 - 占位?相对什么定位?
- 第二遍 2025.2.14 -
static占位原文件流 // ⭐️ 标准文档流
relative占位原文件流 // ⭐️「相对自己本身的定位」
absolute不占位相对于上级 position  static 属性的节点
fixed不占位相对于 window视口
sticky占位超过阈值后不占位-fixed
- 第一遍 2024.12.25 -
// 2019.10 三栏,两边固定,中间自适应
- 第四遍 2025.2.14 -
.left,.right{ width: 100px }
1.left,.right{ position: absolute;top: 0; } .right{ right:0 } .middle{ margin: 0 100px }
2.outer{ display:flex; } .middle{ flex:1 }
3.outer{ display:table;width:100%; } .left,.right,.middle{ display: table-cell }
.outer{ display:grid;grid-template-columns:100px auto 100px; }
4.left,.right,.middle{ float: left } .middle{ width:cacl(100%-200px) }
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
- 第一遍 -
float 会导致 right 换行在第二行 - 要调整 左中右 的DOM布局(BFC也不行了)
2flex
.outer { display: flex; justify-content: space-between; } // ⭐️ 横向布局可不写
.middle { flex: 1 }
4calc
.left, .right { float: left } // ❌ 1、三浮动,否则 right 就会因为向左靠齐换行了
.middle { width: calc(100% - 400px) }
// 2019.10 两栏,左边固定,右边自适应的布局
- 第四遍 2025.2.14 -
.left { width: 200px };
1marign-left+float.left{ float: left } .right { marign-left: 200px }
2marign-left+absolute.left{ position: absolute } .right { marign-left: 200px }
3BFC.left{ float: left } .right{ overflow: hidden }
4flex.outer{ display: flex } .right{ flex:1; } 1 1 auto  ⭐️ //  1 1 0
5calc.left, .right{ float: left } .right{ width: calc(100% - 200px) }
6table+grid.outer{ display: table; width: 100% } .left, .right{ display: table-cell }
.outer{ display: gird; grid-tenplate-columns: 200px auto; }
- 第三遍 2025.1.24 -
- 第二遍 2025.1.19 -
.outer {
    display: grid;
    grid-template-column: 100px auto; ❌❌ // 1、grid-template-columns
}
- 第一遍 -
4flex
.outer { display: flex }
 // 1、父盒子变成弹性盒子后, right 要变成可拉伸 .right { flex: 1 }
// 2022.08 包管理工具
- 第五遍 2025.2.14 -
npm将依赖打平提升到 node_modulesnpm>5开始有 lock文件package-lock.json .npmrc
yarn classic将依赖打平提升到 node_modules为了解决早期 npm 的加载慢问题并行加载多个依赖yarn.lock .yarnrc
pnpm依赖依旧结构化存储在node_modules内存依赖存储物理地址单一存储pnpm-lock.yml .npmrc ⭐️ // 内容寻址存储
yarn berry不再使用node_modules依赖查找表 .pnp.cjs+.yarn/cache zip存储yarn.lock .yarnrc.yml ⭐️ // 即插即用
- 第四遍 2025.1.24 -
- 第三遍 2025.1.19 -
⭐️ // pnpm:内容寻址存储
⭐️ // yarn berry:老是想不到 - 即插即用
- 第二遍 -
 // 1、yarn v2/Berry:即插即用,且lock文件和配置文件分别为 yarn.lock + .yarnrc.yml
- 第一遍 -
 // 1、pnpm:「不是pnpm.yml,而是 pnpm-lock.yml」
// ⭐️ yarn v2/Berry:「即插即用」
// 2024.10 怎么实现跨域
- 第四遍 2025.2.14 -
1jsonp只能处理 GET 请求依赖于script的 src 可以引入第三方资源
2CORS跨域资源共享 Access-Control-Allow-Origin 服务端允许访问的域名可多个
3websocket 双全工双向实时通讯协议不受同源协议限制
4代理nginx ⭐️ // 同源协议只存在于浏览器端,服务端之间没有限制
- 第三遍 2025.1.24 -
- 第二遍 -
- 第一遍 -
// ❌ 1、CORS:哪些域名可以访问资源 - 是为了让浏览器知道哪些域名可以访问,而不是从客户端角度描述
// 2024.11 简单的发布 - 订阅模式实现 EventEmitter
- 第五遍 2025.2.14 -
class EventEmitter {
    constructor() {
        this.events = {};
        this._max = 10;
    }
    add(event, listener) {
        if(!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
        if(this.events[event].length > this._max) {
            throw Error('max exceed')
        }
    }
    remove(event, listener) {
        if(!listener) {
            delete this.events[event];
        }
        if(Array.isArray(this.events[event])) {
            const index = this.events[event].indexOf(listener);
            if(index > -1) {
                this.events[event].splice(index, 1);
            }
        }
    } 
    once(event, listener){
        const onceF = (...args) => {
            listener(...args);
            this.remove(event, onceF);
        }
        this.add(event, onceF);
    }
    emit(event) {
        const args = [].shift.call(arguments);
        if(Array.isArray(this.events[event])) {
            const static = [...this.events[event]];
            static.forEach((it, index) => {
                const arg = args[index];
                Array.isArray(arg) ? it(...arg) : it(arg);
            })
        }
    }
    setMax(v) {
        this._max = v;
    }
}
- 第四遍 2025.1.24 -
- 第三遍 2025.1.19 -
- 第二遍 -
 // 1、remove:这里要判断 index 存在才去删除:if(index>-1){ ... }
emit(event) {
    const args = arguments.slice(1);
    const list = this.events[event];
    ❌❌ // 2、不能直接赋值,要拷贝下来,才能避免 const list = [...this.events[event]];
    list.forEach((it, index) => {
        const arg = args[index];
        Array.isArray(arg) ? it(...arg) : it(arg);
    })
}
- 第一遍 -
 // 4、add:少了计算监听事件数量 if(this.events[event].length > this._max) { throw Error('exceed') }
 // 1、remove:这里要判断 index > -1,也就是 listener 已存在才删除
 // 2、emit:这里一定要注意,由于 once 的存在,this.events[event] 在执行过程是动态改变的,所以这里一定要将 this.events[event] 拷贝下来再处理 let static = [...this.events[event]]
 // 3、emit:args 还是要传进函数的 Array.isArray(args) ? it(...args) : it(args)
// 2021.07 数组扁平化(一层、全部展开、指定深度)
- 第七遍 2025.2.12 -
一层arr.flat() [].concat(...arr) [].concat.call([], ...arr) [].concat.apply([], arr)
全部展开arr.flat(Infinity) arr.toString().split(',') 
function expense(arr, deep = 1) {
    return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur)&& deep>1 ? expense(cur, deep - 1) : cur ), [])
}
- 第六遍 2025.2.9 -
- 第五遍 2025.1.31 -
全部展开arr.flat(Infinity)arr.toString().split(',')JSON.parse(JSON.stringify(arr))
 //  JSON.parse(JSON.stringify(arr)) 是用于深拷贝的,不能展开
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、少写了 arr.flat(Infinity)
 // 2、JSON.stringify 不会展开数组
 // 3、全部展开递归函数中,pre 的值不要使用 push 来更新 - concat 既能处理数组,也能处理非数组
- 第二遍 2025.1.19 -
 // 1、deep 传递的时候要减一
// 作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情?
- 第八遍 2025.2.12 -
挂载阶段 componentDidMountrenderconstructor 初始化 state  事件处理函数的 thisgetDerivedStateFromProps
更新阶段 componentDidUpdate ⭐️ // (prevProps, prevState)
rendergetDerivedStateFromPropsshouldComponentUpdate(nextProps, nextState)
卸载阶段 componentWillUnMount
useEffect - 模拟生命周期 componentDidMount+componentDidUpdate+componentWillUnMount
useLayoutEffect -  commit 之后会阻塞帧的渲染 ⭐️ // 阻塞重绘
- 第七遍 2025.2.9 -
- 第六遍 2025.1.31 -
挂载阶段  // 1、少写了 constructor - 初始化 state 和绑定事件处理函数的 this
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
 // 1、shouldComponentUpdate 写错在挂载阶段,实际应该归属于更新阶段
 // 2、阶段划分是:挂载-更新-卸载,其中更新阶段记错了 - 应该是当组件的 state/props 变化,进入更新阶段,重新执行 render 函数来更新 DOM
- 第三遍 2025.1.19 -
 // 1、挂载阶段:少写了 constructor - 初始化 state 和绑定时间处理函数的 this
 // 2、除了类组件函数的生命周期,还要意识到函数组件也通过 hooks 模拟生命周期
// 作业78:使一个标签看不见的几种样式及其区别
- 第四遍 2025.2.12 -
display: none; 不占位DOM 节点还在触发回流重绘
VS v-if 移除 DOM 节点
opacity: 0; 占位只是透明可以触发节点上的绑定事件只重绘
visibility: hidden; 占位但不能触发节点上的绑定事件只重绘v-show
- 第三遍 2025.1.23 -
- 第二遍 2025.1.19 -
⭐️ // display: none 触发回流+重绘
⭐️ // visibility: hidden 触发重绘(不影响结构)
- 第一遍 -
 // 1、display: none 元素仍在 DOM 中,会触发回流+重绘; v-if 移除了 DOM 节点
 // 2、visibility: hidden 依旧占位,不影响结构,会触发重绘
// 作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期
- 第六遍 2025.2.11 -
computed有缓存值除非依赖值变化否则不重新计算
watch监听改变一次触发执行一次
methods每次组件渲染都会重新执行
父子组件挂载顺序 beforeCreate created beforeMount  beforeCreate created beforeMount mounted  mounted
卸载  beforeDestroy  beforeDestroy destroyed  destroyed
每个组件实例化时如果 data 不是函数会导致多个实例间共享使用函数后每个实例间有独立的作用域
 beforeCreate created此时data/computed/watch/methods都初始化了 beforeMount mounted挂载到 DOM  beforeUpdateVDOM 已生成 updated beforeDestroy清理 destroyed
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
- 第三遍 -
 // 1、大虐,卸载是 beforeDestroy 和 destroyed
 // 2、大虐,和第一个一样的错误,名称错啦! beforeDestroy+destroyed,一般在 beforeDestroy 中清理一些 - 定时器、订阅时间、监听事件等,防止内存溢出
- 第二遍 -
 // 1、computed:咦,和上次错误的逻辑一样,不是「存储的值」是否变化,是「依赖的值」是否变化
❌❌ // 2、和上次错得一样 - 因为上次答案就是错的 😅,这就纠正了,下次不许再错了:子组件在父组件执行 父 beforeMounte 后触发
- 第一遍 -
 // 1、computed:这里其实对 computed 缓存的内容理解有误差,不是「值」,是「计算属性依赖的值」,同时 computed 必须return 返回
 // 2、watch:执行回调
 // 3、methods:每次页面发生变化,都会被调用
 // 子组件挂载完成后,父组件才会挂载 - 子组件在父组件执行 父 beforeMount 后触发
⭐️ // 这里补充:每个组件实例都有自己的「作用域」,不会互相影响
// 作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count,setCount]=useState(0)具体说明
- 第六遍 2025.2.11 -
JIT 运行时编译 JSX 编译为 React.createReactElement 方法 ⭐️ // React.createElement
render阶段生成 fiberNode  + 遍历执行全部 hooks + 收集副作用链 + Diff
	- hooks: useState useEffect
commit阶段将Diff计算出的结果应用到真实 DOM + 生命周期函数+副作用链表
	- componentDidMount/componentDidUpdate/componentWillUnMount
	- useLayoutEffect
count 值改变找到对应节点修改值触发 React 渲染整棵树渲染某组件时执行 fiberNode.memorizedState 存储的所有 hooks如果有副作用则收集副作用链 fiberNode.queue ⭐️ // fiberNode.updateQueue
计算 Diff 更新到真实 DOM执行副作用链
- 第五遍 2025.1.23 -
- 第四遍 2025.1.18 -
render fiber树 + hooks按序全部执行 + 副作用链表收集 + Diff
// ⭐️ FiberRootNode HostRootFiber beginWork-completeWork wip.tag
commit// ⭐️ useLayoutEffect
- 第三遍 -
 // 0、渲染之前,React 已经通过 JIT 将 JSX 描述的 UI 编译为 React createElement方法
render阶段:❌ // 1、FiberRootNode 才是「根节点」,「HostRootFiber」只是新的 fiber 树的遍历起始点,也就是 FiberRootNode.current
 // 2、「遍历AST逐个生成 fiberNode」可以更详细得表述为「遍历执行 beginWork,根据不同的 wip.tag 进行不同标签的处理,执行到叶子节点 completeWork,生成 WIP Tree」
 // 3、render 阶段也是有生命周期在执行的,例如每次渲染执行前的 shouldComponentUpdate 
 // 4、遍历执行 hooks 时,如果遇到有副作用的节点,除了被副作用链表收集,react 也会在节点 Node 上标记 fiberNode.subtreeFlags
⭐️ // fiberNode.updateQueue
⭐️ // 生命周期是类组件的概念
⭐️ // useLayoutEffect、ref引用也在这个时机
 // 1、「触发更新」其实应该描述成「更新 count 状态对应的节点」
 // 2、这里应该将「如果有副作用」改写为「要注意是否有 useEffect 的作用:根据依赖数组是否变化确定是否要更新」
- 第二遍 -
 // 1、在 render 之前,运行时就会执行 JIT 先将 JSX 描述的 UI 编译为 React createElement 方法
1.render 创建一颗完整的Fibernode的虚拟树 + diff
 // 2、中间少写了两个关键步骤:存储 hooks + 标记、收集副作用链表
 // 3、创建 FiberRootNode 后,创建 fiber 作为 HostRootFiber,也就是 FiberRootNode.current
 // 4、以单个组件为例,fiberNode 存储 hooks,按序执行时标记、收集副作用链表
 // 5、更新时会重新调用渲染函数,按序执行所有 hooks,所以也算是流程的一部分
- 第一遍 -
// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第三遍 2025.2.11 -
.father { display: flex; justify-content: center; align-items: center; }
.father { display:table-cell; text-align: center; vertical-align: middle; }
.son { display: inline-block }
.son { position: absolute; top:0; right:0; bottom:0; left:0; margin: auto; }
.son { position: absolute; transform: traslate(-50%, -50%); top: 50%; left: 50%; }
- 第二遍 2025.2.9 -
- 第一遍 2025.1.30 -
 .son { display: table-cell; text-align: center; vertial-align: middle; } 
 // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中 .father { display: table-cell; text-align: center; vertical-align: middle; } .son { display: inline-block; }
// 作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果
- 第三遍 2025.2.11 -
事件 - 同步 - 主线程执行 - 清空调用栈之前去事件队列中获取函数执行 - 循环执行清空事件队列
	- 异步 - Event Table - 异步函数有结果后将回调函数投入事件队列
宏任务IO下载交互UI渲染setTimeout/setTimeIntervaljs代码执行
微任务Promise.thenasync/await、MutationObserver
125364
script start - async1 start - async2 - promise1 - script end
    - async1 end - promise2 - promise3
    - setTimeout
- 第二遍 2025.2.9 -
⭐️ // Event Queue + 触发时机 - 等异步任务有了结果,将回调函数投入事件队列
- 第一遍 2025.1.30 -
⭐️ // Event Queue,等异步任务有了结果,将回调函数投入事件队列
⭐️ // IO表示下载和交互
1 5 2 6 3 4  // 125364 
// 作业3:迭代器原理
- 第四遍 2025.2.11 -
使用 next 方法可以遍历可迭代对象的所有值
- 第三遍 2025.2.9 -
- 第二遍 2025.1.30 -
提供暂停和恢复代码执行能力[Symbol.iterater] 默认迭代器
yield 暂停next 恢复执行 {done, value}
 // 迭代器使用 next 方法在可迭代对象中遍历数据
- 第一遍 2024.12.25 -
// 作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?
- 第四遍 2025.2.11 -
组件 - 返回可以被渲染的 JSX ⭐️ // 写想要做什么,而不是怎么做
hook - 返回类型不受限制但是只是提供状态处理逻辑hook之间独立不会共享状态且如果 hook 内没有使用其他 hook 不建议抽离为 hook
渲染时某些值改变触发组件重新渲染会遍历执行 fiberNode.memorisedState 存储的整个 hooks 链表保证每个值都是最新的准确的
- 第三遍 2025.1.22 -
- 第二遍 -
- 第一遍 -
// ❌ 修改下题目:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?
React 组件代码表达的是 目标而不是具体实现 - 描述的是想要做什么而不是怎么做
hook 中如果没有内置 hook则不建议使用 hook
自定义 hook 只是共享状态逻辑而不是状态本身 - 每次调用都有独立的状态
返回值组件一般是返回一段 React 能够显示的内容例如JSX结构hook可以返回任何值
// 2021.04 05-3 白屏优化?
- 第四遍 2025.2.11 -
1硬件加速 DNS缓存优化TCP连接加速服务器
2代码 js不写内联代码避免阻塞主线程解析DOM使用 async/defer
css模块化使用link开启预解析提前下载GPU加速减少层级因为css从右向左解析避免回流
html精简结构减少层级
压缩代码 + tree-shaking
减少 HTTP 请求次数减小请求大小
- 第三遍 2025.1.20 -
- 第二遍 2025.1.19 -
css从右向左解析所以减少层级媒体查询减少加载代码体量调用 GPU 等优化方式减少回流重绘
 // 1、使用 link 加载而非 @import,可以触发预加载
jsdeferasync 加载  // 2、尽量不使用「内联代码」
- 第一遍 -
白屏网络进程文件加载完毕浏览器开始刷新页面但是主线程渲染流程还没有结束页面短暂出现解析白屏
 // 1、少写 - 硬件加速:DNS 解析优化,例如缓存、预加载;TCP、服务器优化
2css 从右到左的解析方式尽可能减少层级使用 link 而非 @import从而触发预加载使用 css 优化/GPU 加速减少回流重绘  // 2、少写 - 对于大文件的 css,利用媒体查询拆分不同用途
3js 加载使用 defer/async 避免阻塞  // 3、少写 - 尽量不使用 内联代码
// 2024.05 rem是基于什么原理进行适配的?
- 第四遍 2025.2.11 -
em - font-size 值相对于父元素width/height/padding/margin 相对于当前元素的 font-size
rem - 相对于根元素html
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
- 第一遍 -
rem 相对于根元素的 font-size - html(body)  // 1、不是 body-只是html的一个子标签,就是 html
em  - 当前节点的字体大小相对于父元素的 font-size
	- 当前节点的 width/height/padding/border/margin 相对于当前元素的 font-size
// 2024.12 模拟实现promise
- 第五遍 2025.2.11 -
const S = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
function MyPromise(executor) {
    this.status = S.pending;
    this.value = null;
    this.reason = null;
    this.resolveCalls = [];
    this.rejectCalls = [];
    const resolve = (v) => {
        if(this.status === S.pending) {
            this.status = S.resolve;
            this.value = v;
            this.resolveCalls.forEach(it => it(v))
        }
    }
    const reject = (r) => {
        if(this.status === S.pending){
            this.status = S.reject;
            this.reason = r;
            this.rejectCalls.forEach(it => it(r))
        }
    }
    return try{
        executor(resolve, reject);
    }catch(e){
        reject(e);
    }
}
MyPromise.prototype.then = function (onResolved, onRejected) {
    onResolved = typeof onResolved === 'function' ? onResolved : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r };
    return new MyPromise((resolve, reject) => {
        const resovleF = () => {
            setTimeout(() => {
                try{
                    const res = onResolved(this.value);
                    resolve(res);
                }catch(e){
                    reject(e)
                }
            }, 0)
        }
        const rejectF = () => {
            setTimeout(() => {
                try{
                    const res = onRejected(this.reason);
                    resolve(res);
                }catch(e){
                    reject(e)
                }
            }, 0)
        }
        switch(this.status) {
            case S.pending:
                this.resolveCalls.push(resovleF);
                this.rejectCalls.push(rejectF);
                break;
            case S.resolve:
                resovleF();
                break;
            case S.reject:
                rejectF();
                break;
        }
    })
}
MyPromise.prototype.catch = function(onRejected) {
    return this.then(null, onRejected)
}
MyPromise.prototype.finally = function(call) {
    return this.then(v => { call(); return v; }, r => { call(); throw r; })
}
MyPromise.resolve = function (v) {
    return new MyPromise((resolve, reject) => {
        if(v instanceof MyPromise) {
            return v.then(resolve, reject)
        }
        resolve(v);
    })
}
MyPromise.reject = function (r) {
    return new MyPromise((resolve, reject) => reject(r))
}
MyPromise.all = function(arr){
    return new MyPromise((resolve, reject) =>{
        return arr.reduce((pre, cur) => {
            cur.then((res, index) => {
                pre.push(res);
                if(index === arr.length-1) {
                    resolve(pre)
                }
            }, reject)
            return pre
        }, [])
    })
}
MyPromise.race = function(arr){
    return new MyPromise((resolve, reject) =>{
		arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
    })
}
- 第四遍 2025.1.20 -
- 第三遍 2025.1.19 -
- 第二遍 -
class MyPromise {  // 1、模拟 Promise 的时候,一般都是用函数
MyPromise.prototype.then = function(onResolve, onReject) {
    // ...
    let that = this;
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
            setTimout(() => {
                const res = onResolve(that.value);
                resolve(res);
                ❌❌ // 2、遗漏捕获错误
                /**
                try{
                	const res = onResolve(that.value);
                	resolve(res);
                }catch(e){ reject(e) }
                */
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                const res = onReject(that.reason);
                reject(res);
                ❌❌ // 3、遗漏捕获错误 + onReject 返回的依旧是 resolve 的 promise
                /**
                try{
                	const res = onReject(that.reason);
                	resolve(res);
                }catch(e){ reject(e) }
                */
            }, 0)
        }
        // ...
    })
}
- 第一遍 -
MyPromise.prototype.then = function (onResolve, onReject) {
    // ...
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
			setTimeout(() => {
                try{
                    const res = onResolve(this.value);
                    resolve(res);
                }catch(reject);  // 6、catch 不能这么写 catch(e){ reject(e) }
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                try{
                    const res = onReject(this.reason);
                    resolve(res);
                }catch(reject);  // 6、catch 不能这么写 catch(e){ reject(e) }
            }, 0)
        }
        // ...
    })
}
MyPromise.resolve = (value) => {
    return new MyPromise((resolve, reject) => {
        if(value instanceof MyPromise) {
            return value.then(resolve, reject); ⭐️ // 3、这里的 return 可以删除,不影响
        }
        resolve(value);
    })
}
MyPromise.reject = (reason) => {
    ⭐️ // 2、简写成 return new MyPromise((resolve, reject) => reject(reason));
}
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) =>{
        return arr.reduce((pre, cur, index) => {
             ⭐️ // 4、这里的 return 可以删除,不影响
            cur.then(res => {
                pre.push(res);
                if(index === arr.length -1) {
                    resolve(pre);
                }
            }).catch(reject);
             // 1、reject 是作为 then 的第二个函数 -> }, reject)
            return pre;
        }, [])
    })
}
MyPromise.race = function (arr) {
    return new MyPromise((resolve, reject) => {
        ⭐️ // 5、这里科利简写 arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
    })
}
// 作业85:Lambdas是什么
- 第二遍 2025.2.11 -
匿名函数
- 第一遍 2025.1.22 -
匿名函数
// 作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的
- 第四遍 2025.2.11 -
原型链一个函数的原型对象是另一个函数的实例
原型链继承Sub.prototype = new Super();
	- 方法不需要反复重新创建
	- 属性和方法都共享修改一个实例的引用类型其他实例也改变了
组合继承- 每个实例都有私有的属性 - 父级实例每次都要执行2次
function Sub(){
    Super.apply(this, arguments);
}
Sub.prototype = new Super();
寄生组合式继承子级的原型对象是父级原型对象的副本
function iherit(Sub, Super){
    let proto = Object.create(Super.prototype);
    Object.defineProperty(proto, "constructor", {
        enumerable: false,
        value: Sub
    });
    Sub.prototype = proto;
}
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
寄生组合式继承: 父类的原型对象的副本是子类的原型
function inherit(Sub, Super) {
    let proto = Object.create(Super.prototype);
    proto.constructor = Sub;
    ❌❌ // 1、Object.defineProperty(proto, "constructor", {enumberable: false, value: Sub})
    Sub.prototype = proto;
}
- 第一遍 -
寄生组合式继承: 父类的原型对象副本是子类的原型对象
	function inherit(Sub, Super) {
        const proto = Object.create(Super.prototype);
         // 1、Object.defineProperty(对象,对象属性,对象值),所以应该写成 Object.defineProperty(proto, constructor, { enumerable: false, value: Sub })
        Object.defineProperty(proto, {
            enumerable: false,
			value: { constructor: Sub }
        }); // 修复 自定义原型对象会导致的 constructor 指向问题
        Sub.prototype = proto;
    }
// 作业53:胜利者一无所获 说的是什么
- 第四遍 2025.2.10 -
反战 海明威 战国春梦 质疑胜利的本质意义指出战争胜利者失去了美好的品质例如善良
鼓励人们追求内在美好品质 ⭐️ // 质疑赢家通吃逻辑
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
⭐️ // 质疑胜利的本质
- 第一遍 -
// ❌ 《战地春梦》
// 作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口)
- 第四遍 2025.2.10 -
<canvas id='drawing' width=200 height=200></canvas>
const drawing = document.getElementById('drawing');
2D左上角矩形
if(drawing.getContext) {
    const context = drawing.getContext('2d');
    context.beginPath();
    context.fillStyle/strokeStyle = 'red';
    context.fillRect/strokeRect(10, 10, 30, 50);
    context.drawImage/fillText('string', 10, 10);
    context.moveTo/lineTo();
    context.stroke();
}
3D: 左下角
if(drawing.getContext) {
    const context = drawing.getContext('webgl');
    context.viewport(drawing.width/2, 0, drawing.width/2, drawing.height/2); // 右下角
}
- 第三遍 2025.1.22 -
- 第二遍 2025.1.19 -
3D:左下角context.viewPoint() ❌❌ // 1、viewport
- 第一遍 -
2D:
context.beginStroke();  // 1、context.beginPath() - 创建路径
3D:  // 2、viewport
context.viewpoint(drawing.width/2, 0, drawing.width/2, drawing.height/2); // 右下角1/4
// 作业71:React为什么要合成事件?和原生事件有什么不同?
- 第五遍 2025.2.10 -
合成事件 - React 对原生事件的封装抹平浏览器之间的 API 差异 + 绑定事件使用事件委托机制在渲染时绑定在最外层节点一般是 document ⭐️ // 更好的说法:跨浏览器兼容 + 在挂载时,会在节点绑定事件处理函数
优点专注于代码抹平差异 + 统一绑定时间处理函数减少对 DOM 绑定的操作优化性能
差异
事件执行时机React 冒泡原生先捕获执行再冒泡一般比合成事件早执行
事件绑定方式React JSX 绑定事件处理函数事件委托绑定原生addEventListener ⭐️ // onclick
事件对象合成事件是统一的对象原生对象各个浏览器之间有差异

- 第四遍 2025.1.22 -
- 第三遍 2025.1.19 -
⭐️ // 跨浏览器兼容
- 第二遍 -
⭐️ // + 是对浏览器原生事件的封装
 // 1、合成事件的绑定,不是在「构建时」,事件委托发生在「组件挂载时」
 // 2、差异少写了:事件绑定方式 合成事件-JSX中属性指定事件处理函数;原生事件 标签的onclick或者addEventListener
- 第一遍 -
 // 1、内核是对的,不是“高级集合”,是「React对浏览器原生事件的封装」,是「一个跨浏览器兼容的事件系统」
// 优点/原因:① 跨浏览器兼容性 ②性能优化:通过事件委托到 document,减少大量事件绑定的开销
 // 2、差异需要具体的描述:
// ①事件对象(有一套标准的属性和方法,用于事件信息 VS 由浏览器提供,属性方法因浏览器而异)
// ②事件绑定方式(React中JSX属性中指定事件处理函数来绑定 VS 原生标签的 onclick/addEventListener)
// ③事件执行顺序(内部事件系统决定,冒泡执行,一般比原生晚 VS 先捕获再冒泡)
// 作业66:React如何处理错误
- 第八遍 2025.2.10 -
React 提供两个函数处理 render + commit 阶段的错误避免错误溢出影响整个应用影响 UI 展示
static getDerivedStateFromError - 返回 state 用于更新 UI
componentDidCatch - 抓取 err 用于对错误进行记录处理
class ErrorBoundaries extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasErr: false };
    }
    static getDerivedStateFromError() {
        return { hasErr: true };
    }
    componentDidCatch(e, info) {
        console.log(info)
    }
    render() {
        if(this.state.hasErr) {
            return <div> There are errs. </div>
        } else {
            return this.props.children;
        }
    }
}
<ErrorBoundaries> <My /> </ErrorBoundaries>
- 第七遍 2025.2.9 -
- 第六遍 2025.1.29 -
React 提供两个函数处理错误 static getStateFromError()componentDidCatch用于捕获 
 // 1、静态函数名称错了 getDerivedStateFromError
render+commit 阶段的错误阻止错误蔓延到整个应用影响 UI 展示
几个情况不会捕获异步回调ssr错误边界内部
class Boundaries extends React.Component{
    constructor() {
        super(props);
        this.state = { hasError: false }
    }
    static getStateFromError() {  // 2、getDerivedStateFromError
        return { hasError: true }
    }
    componentDidCatch(e, info) {
        console.log(e)
    }
    render() {
        if(this.state.hasError) {
            return <p> There are errs. </p>
        } else {
            return this.props.children;
        }
    }
}
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 作业6:new操作符实例化一个对象的过程
- 第四遍 2025.2.10 -
function newF() {
    const obj = new Object();
    const ctor = [].shift.call(arguments);
    obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
    return typeof res === 'object' ? res : obj;
}
- 第三遍 2025.2.9 -
- 第二遍 1.28 -
function newFun () {
    let obj = new Object();
    const ctor = [].shift.call(arguments);
    obj.__proto__ = ctor;  // 1、不是构造函数,是构造函数的原型对象 obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
	return typeof res === 'object' ? res : obj;
}
- 第一遍 1.20 -
 // 1、不是调整 constructor 的指向

LTN①⑤

①⑤ 错题重做 - 23题

2.9 - 14 题 2.10 - 9题 = 2h57min

✅作业6:new操作符实例化一个对象的过程 ✅作业66:React如何处理错误 ✅作业3:迭代器原理 ✅2021.06 子盒子在父盒子中水平垂直居中有几种方法? ✅作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情? ❌作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ✅作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果

Promise.resolve().then(() => {
    console.log(1);
    Promise.resolve().then(() => {
        console.log(2);
    }).then(() => {
        console.log(3);
    }).then(() => {
        console.log(4);
    })
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);
})  
------
async1 = async () => {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
function async2(){
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
})
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
    return new Promise(function(resolve) {
        resolve();
    })
}).then(() => {
    console.log('promise3');
})
console.log('script end');

❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅2021.07 数组扁平化(一层、全部展开、指定深度) ✅作业25:简单写下 请求创建过程 ✅作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? ❌作业55:flex 常见缩写 ✅作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ✅作业61:常见的数据结构有哪些 ✅作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅作业76:显卡的作用? ✅作业90:金玉满堂是什么意思?在花卉中的说法是什么? ✅作业91:三次握手的过程和具体包的作用 ❌作业92:前端中的序列化是什么?常见序列化方法 ❌作业93:闭包的作用和原理 ❌2021.07 事件流 + 事件模型 ✅2021.06 链表 141. 环形链表 ✅2021.09 get和post有什么区别

// 2021.09 get和post有什么区别
- 第二遍 2025.2.10 -
get参数在请求行上参数长度有限制 2kb浏览器默认缓存 GET 请求结果回退无害参数在链接中重新发起请求
post参数在请求体中参数长度无限制默认不缓存可以手动设置回退有害参数不存储在链接中

- 第一遍 2025.2.8 -
 // 1、从四个方面描述:数据参数 + 参数长度 + 回退安全性 + 缓存
get
	请求参数在链接上 ⭐️ // 也就是请求行
	参数大小限制;❌ // 2、参数长度:get - 大小限制2kb
     // 4、回退安全性:get - 回退无害,参数被保留在浏览器历史记录
     // 6、缓存:get - 请求会被浏览器主动 cache
post
	请求参数在请求体
    数据大小限制很大 2kb;❌ // 3、参数长度:post - 没有限制
     // 5、回退安全性:post - 回退时,post请求会被再次提交,参数不会被保留
     // 7、缓存:post - 不会缓存,除非手动设置
// 2021.06 链表 141. 环形链表
- 第二遍 2025.2.10 -
function cricle(head) {
    try{
        JSON.stringify(head);
        return false
    }catch(e){
        return true
    }
}
function circle2(head) {
    while(head) {
        if(head.circle){
            return true
        }
        head.circle = true;
        head = head.next;
    }
    return false
}

- 第一遍 2025.2.8 -
function circle(head) {
    try{
        JOSN.stringify(head);
        return false;
    } catch(e){
        return true;
    }
}
function circle2(head) {
    while(head.next) {  // 1、 while(head) 即可
        if(head.circle) {
            return true
        }
        head.circle = true;
        head = head.next;
    }
    return false
}
// 2021.07 事件流 + 事件模型
- 第二遍 2025.2.10 -
事件流 捕获 从上至下直到具体节点 window-document-body-html-... 冒泡 从内至外直到最外层节点 
事件模型 现代事件模型 先捕获再具体节点执行事件处理函数最后冒泡
 // 1、现代事件模型:是事件委托,利用事件冒泡,将事件处理程序添加到父元素;DOM2级事件模型才是我写的:捕获+处理+冒泡

- 第一遍 2025.2.8 -
先捕获再冒泡
 /** 缺少详细描述
	捕获:从 window->document->html->body 传递到最具体的节点
	冒泡:从具体节点逐渐向上传播到DOM最高层父节点
	现代事件模型:事件委托-利用事件冒泡,将事件处理程序添加到父元素
	DOM2级事件模型:捕获+处理+冒泡
*/
// 作业93:闭包的作用和原理
- 第二遍 2025.2.10 -
闭包内部函数引用外部函数的变量外部函数执行完后原本应该清理变量但因为引用就会以 closure 存储以便存储值后续访问 ⭐️ // 活动对象没有被销毁,词法作用域
作用保存变量值私有变量  // + 函数工厂、回调函数

- 第一遍 2025.2.8 -
外部函数执行完毕后会清理执行栈和内存将变量清理但是如果内部函数引用了变量变量将不被清理 closure 存储在内存 ⭐️ // 活动对象没有被销毁,词法作用域
用于保存变量持续的值  // 保存状态 + 数据封装/私有变量 + 函数工厂 + 回调函数
// 作业92:前端中的序列化是什么?常见序列化方法
- 第二遍 2025.2.10 -
序列化将对象等转换为方便存储传输的结构 ⭐️ // 将数据结构、对象转换
JSON.stringifynew FormData(form)
 // 1、JSON.stringify 无法处理 Function、无法处理循环引用;new FormData 收集表单数据并序列化,转换成键值对格式

- 第一遍 2025.2.8 -
将不同的数据结构转换为 JSON
 // 1、序列化是将数据结构/对象转换为可以存储/传输的格式的过程
JSON.stringify ⭐️ // 特殊点:无法处理 Function 和 互相引用,循环引用时会报错
 // 2、少写了 new FormData(form) 收集表单数据并序列化,转换成键值对的格式
// 作业91:三次握手的过程和具体包的作用
- 第二遍 2025.2.10 -
客户端和服务端建立 TCP 连接SYN 序列编号包ACK 确认包标志位)⭐️ // SYN 同步序列编号包
客户端发起 SYN 
服务端 SYN + ACK
客户端 ACK

- 第一遍 2025.2.8 -
客户端SYN
服务端SYN + ACK
客户端ACK
SYN - ...不记得了  // 1、SYN是同步序列编号包
ACK - 确认信息有序列号  // 2、ACK确认包
// 作业90:金玉满堂是什么意思?在花卉中的说法是什么?
- 第二遍 2025.2.10 -
形容财富很多比喻人学识丰富
玉堂春富贵玉兰 海棠 迎春 牡丹 桂花

- 第一遍 2025.2.8 -
金玉满堂很多内涵夸奖 ⭐️ // 形容财富很多、比喻学识丰富
海棠(其他的不记得了)
 // 1、花卉中的说法 玉堂春富贵 - 玉兰(玉)、海棠(堂)、迎春(春)、牡丹(富)、桂花(贵)
// 作业76:显卡的作用?
- 第三遍 2025.2.10 -
合成图像存储在后缓冲区
前后缓冲区替换

- 第二遍 2025.2.2 -
用于绘制帧
在后区渲染好之后直接替换前区帧
⭐️ // 合成新的图像,并将图形保存到后缓存区
// 作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次?
- 第四遍 2025.2.10 -
react  合成事件+生命周期函数 中都是异步 - 批量更新机制在执行事件处理函数时遇到 setState 先用队列存储执行完函数后将队列中的 state 统一计算处理修改 state 一次触发一次渲染
在原生事件中是同步触发一次 setState 重新渲染一次

- 第三遍 2025.2.2 -
React 合成事件 - 异步原生事件 - 同步
 // 1、除了合成事件,还有「生命周期函数」中都是异步
React 在处理 setState 的时候在执行回调函数时会创建一个队列存储执行完回调函数统一处理队列中的 setState 有个合并处理机制 ⭐️ // 批量处理机制 + 更新一次 state ,触发一次重新渲染
在原生事件中不受 React 管控直接执行 ⭐️ // setState 一次,就会触发一次渲染
- 第一遍 -
 // 1、除了合成事件,还有生命周期函数
// 作业61:常见的数据结构有哪些
- 第四遍 2025.2.9 -
按照逻辑结构分
结构型数组链表队列
非结构型

- 第三遍 2025.2.2 -
结构化数据队列链表  // 1、少写了一个类型:数组
非结构化数据
// 作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例
- 第五遍 2025.2.9 -
高阶函数输入或者输出的值是函数map
高阶组件输入参数是组件
副作用函数式编程的概念除了返回值还会修改外部其他值例如 IO全局变量
const f = (My) => {
	return class extends React.Component{
		constructor(props) {
    		super(props);
		}
		render() {
            return <My {...this.props}/>
        }
	}
}

- 第四遍 2025.2.2 -
高阶函数输入/输出是函数的函数例如 map
高阶组件输入是组件
副作用在函数内部会修改外部参数全局参数IO ⭐️ // 除了返回值
function fun(Child){
    return class extends React.Component {
        constructor(props) {
            super(props);
        }
        render() {
            return Child
             // 1、<Child {...this.props} />
        }
    }
}
// 作业55:flex 常见缩写
- 第五遍 2025.2.9 -
flex: 0; // 0 0 auto ❌ // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
flex: initial; // 0 1 auto
flex: auto; // 1 1 auto
flex: 1; // 1 1 0

- 第四遍 2025.2.2 -
flex: none; // 0 0 auto
flex: 1; // 1 1 0
flex: auto; // 1 1 auto
 // 1、少写了一个 flex: initial; // 0 1 auto
- 第二遍 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
- 第一遍 -
 // 1、把 0 0 auto 的缩写写错,应该是 flex: none;
// 作业74:起舞弄清影 是那首诗?谁写的?下一句是什么?
- 第三遍 2025.2.9 -
起舞弄清影何似在人间
苏轼 水调歌头 明月几时有
明月几时有把酒问青天不知天上宫阙今夕是何年
我欲乘风归去又恐琼楼玉宇高处不胜寒
转朱阁低绮户照无眠。⭐️ // 顺序反了 - 起舞弄清影,何似在人间。转朱阁,低绮户,照无眠。
起舞弄清影何似在人间
⭐️ // 不应有恨,何事长向别时圆。人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。
千里共婵娟

- 第二遍 2025.2.2 -
 苏轼/苏东坡   // 1、题目记不起来:《水调歌头 明月几时有》
我欲乘风归去又恐琼楼玉宇高处不胜寒
不知天上宫阙今夕是何年何似在人间  // 2、乱序:起舞弄清影,何似在人间
/**
	明月几时有?把酒问青天。不知天上宫阙,今夕是何年。
	我欲乘风归去,又恐琼楼玉宇,高处不胜寒。
	起舞弄清影,何似在人间。
	转朱阁,低绮户,照无眠。
	不应有恨,何事长向别时圆。
	人有悲欢离合,月有阴晴圆缺,此事古难全。
	但愿人长久,千里共婵娟。
*/
// 作业25:简单写下 请求创建过程
- 第五遍 2025.2.9 -
function request(url) {
    const xhr = new XMLHttpRequest();
    xhr.ontimeout = ()=>{};
    xhr.onerror = ()=>{};
    xhr.onreadystatechange = function(res) {
        switch(res.readyState) {
            case 0:
                break;
            case 4:
                if(res.status === 200 || res.status === 301) { ⭐️ // status 在this上
                    console.log(this.responseText)
                }
        }
    }
    xhr.open('GET', url, true);
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader(key, value);
    xhr.send();
}

- 第四遍 2025.2.2 -
function request(url) {
    let xhr = new XMLHttpRequest();
    xhr.onerror = () => {}
    xhr.ontimeout = () => {}
    xhr.onreadystate = function (res) {   // 1、函数名称错误:onreadystatechange
        switch(this.readyState) {  // 2、属性不在 this 上,在 res 上:res.readyState
            case 0:// 还未开始请求
                 // 3、switch 中 0 表示请求还没开始,要使用 break 跳出循环
            case:4
                if(this.status === 200 || this.status === 304) {
                    console.log(this.responseText)
                }
        }
    }
    xhr.open('GET', url, true); // 允许异步
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader(key, value);
    xhr.send();
}
- 第二遍 -
 // 1、属性名称记错 :res.readyState
- 第一遍 -
 // 1、open 的参数顺序记错了:xhr.open('GET', url, true)
// 2021.07 数组扁平化(一层、全部展开、指定深度)
- 第六遍 2025.2.9 -
一层: arr.flat()/[].concat(...arr)/[].concat.call([], ...arr)/[].concat.apply([], arr)
全部: arr.flat(Infinity)/arr.toString().split(',')/
function expense(arr, deep) {
    return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) && deep>1 ? expense(cur, deep-1) : cur), [])
}

- 第五遍 2025.1.31 -
一层arr.flat()[].concat(...arr)[].concat.call([], ...arr)[].concat.apply([], arr)
全部展开arr.flat(Infinity)arr.toString().split(',')JSON.parse(JSON.stringify(arr))
 //  JSON.parse(JSON.stringify(arr)) 是用于深拷贝的,不能展开
function expense(arr, deep = 1) {
    return arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur) && deep > 1 ? expense(cur, deep - 1) : cur)
    }, [])
}
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、少写了 arr.flat(Infinity)
 // 2、JSON.stringify 不会展开数组
 // 3、全部展开递归函数中,pre 的值不要使用 push 来更新 - concat 既能处理数组,也能处理非数组
- 第二遍 2025.1.19 -
 // 1、deep 传递的时候要减一
// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第四遍 2025.2.9 -
mysql2 是给node环境的数据库驱动可以直接连接数据库但是需要写比较多的 SQL
Sequelize ORM 可以将表和行解析为对象将数据库的操作以函数式呈现
 // 1、不是解析,是映射:将表和行映射为对象模型
mysql2
import { createPool } 'mysql2/promise';
const pool = createPool({host, user, password, database});
const connect = await pool.getConnect();
const [rows] = await connect.execute('SELECT * FROM table');
sequelize
import { Sequelize, DataType, Modal } from 'sequelize';
const se = new Sequelize({ host, user, password })
 // 2、实例化的参数错误 new Sequelize(database, user, password, { host: 'localhost', dialect: 'mysql2'})
方法一
class Usr extends Modal {}
Usr.init({
    name: DataType.STRING
})
 // 3、init 还需要第二个参数传递连接实例,模型名称
/**
Usr.init({ name: DataTypes.STIRNG }, {
	sequelize: se, // 传递连接实例
	modalName: 'Usr' // 模型名称
})
*/
方法二
se.define('table_name', {
    name: {
        type: DataType.STRING
    }
}, {
    underscore: false
})

- 第三遍 2025.1.31 -
mysql2 是适用于node场景的 mysql 驱动可以直连数据库但是需要写很多 SQL
⭐️ // 提供了 mysql2/promise 模块,允许 async/await 、管理多个数据库连接;没有内置的对象关系映射ORM
import { createPool } from 'mysql2'  // 1、依赖包错了 mysql2/promise
const pool = createPool({host, user, password});  // 2、少写了 database
const connection = await pool.getConnection();  // 3、获取连接的函数错了 await pool.getConnect()
const data = connection.execute('SELECT * FROM table')  // 4、需要使用 await:const [rows] = await connection.execute('SELECT * FROM table')
--
Sequelize 数据库模块ORM 把整个数据库表对象化可以通过函数式编程直接调用使用执行事务
⭐️ // ORM 把数据库的表和行映射为对象模型
import { sequelize, Datatypes, Modal } from 'sequelize'
const se = new sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql2'
})
创建连接后会一直保持连接状态直到调用 se.close() 关闭连接
class se extends Modal {}
se.define('table', {
    id: Datatypes.number
}, {
    underScore: false;
})
 // 5、有两种模型定义方法:一种是 define,另一种 extends Modal + init
/**
const User = se.define('table', {
    id: Datatypes.number
}, {
    underScore: false;
})
--
class User extends Modal {}
User.init({
	id: Datatypes.number
}, {
	sequelize: se, // 传递连接实例
	modalName: 'User' // 模型名称
})
最后调用: User.findAll({ attribute: ['id', 'name'] })
*/

- 第二遍 2025.1.26 -
 // 1、mysql2 的依赖包是 mysql2/promise
 // 2、mysql2 创建连接池不需要 await:const pool = createPool({host, user, password, database})
 // 3、mysql2 获取连接时需要 await:const connection = await pool.getConnect()
 // 4、mysql2 查询数据时需要 await:const [rows] = await connectiom.execute('SQL')
 // 5、sequelize 实例化创建连接 const s = new sequelize(database, user, password, { host, dialect })
 // 6、sequelize 模型定义的底层 - 先扩展,再init定义

- 第一遍 2025.1.22 -
 // 1、mysql2 提供的连接的两个特点: mysql2/promise 支持 async/await + 管理多个数据库连接池
 // 2、sequelize 将数据库的表和行映射为对象模型,直接通过 js 操作
// 作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果
- 第二遍 2025.2.9 -
事件 -同步 - 主线程执行 - 调用栈执行 - 调用栈清空前调用事件队列中的函数 - 直到清空
    -异步 - Event Table - 触发后回调函数加入事件队列 ⭐️ // Event Queue + 触发时机 - 等异步任务有了结果,将回调函数投入事件队列
宏任务IOUI渲染js执行setTimeout/setTimeInterval
微任务Promise.thenasync/await、MutationObserver
125364
script start - async1 start - async2 - promise1 - script end - async1 end - promise2 - promise3- setTimeout

- 第一遍 2025.1.30 -
时间循环系统
事件同步类型 - 主线程执行 - 执行完成清理执行栈之前从事件队列中获取事件执行 - 所有微任务执行完毕后进入下一个事件循环
	 异步类型 - Event Table - 将回调函数投入事件队列 ⭐️ // Event Queue
⭐️ // 等异步任务有了结果,将回调函数投入事件队列
宏任务  UI渲染IO下载js代码setTimeout/setTimeInterval ⭐️ // IO表示下载和交互
微任务 MutationObserverPromise.thenasync/await
1 5 2 6 3 4  // 125364 
script start - async1 start - async2 - promise1 - script end
- async1 end - promise2 - promise3 - setTimeout
// 作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page
- 第四遍 2025.2.9 -
预渲染主要是服务端提前渲染SSR客户端提前构建SSG增量客户端构建ISR
 // 1、名称错误:服务端渲染SSR、静态生成SSG、增量静态生成ISR
模式服务端渲染 - 在请求时服务端将引用数据插入返回给客户端后客户端只需要渲染不需要js执行
⭐️ // getServerSideProps
客户端构建 - 在构建时提前将页面渲染完成并使用 revalidate 设置时间到时间后重新构建
⭐️ // generateStaticParams
 // 2、缺少说明一下结果:客户端、服务端生成的结果都是 HTML,得说明一下
SSR
import { getServerSideState } from 'next'
 // 3、函数名称、依赖包错误:import { getServerSideProps } from 'next/server'
function Blog() { return <div>11</div> }
export default Blog;
export function getServerSideState(props) {
    return <Blog {...this.props} />
}
 // 4、SSR 的核心是在服务端提前获取数据 -> 传入组件
/**
export function getServerSideProps() {
	const res = await fetch('xxxx');
	const data = await res.json();
	return { props: data }
}
export default function Blog({data}) {return <div>{data}</div>}
*/
SSG
function Blog({data}) { return <div>{data}</div> }
export function generateParams() {
     // 5、函数名称错误:generateStaticParams
    return [{data: 'first'}, {data: 'second'}]
}
export const revalidate = 60; // 秒
export default Blog;

- 第三遍 2025.1.31 -
预渲染包含服务端渲染 SSR静态生成SSGISR 增量静态生成
SSR:getServerSideProps 客户端每次请求都在服务端构建 HTML动态引入 data 数据返回给客户端直接渲染而不是交给客户端js
SSG+ISR:getDerivedStateParams + revalidate 在构建时直接生成 HTML超过更新时间后后台直接重新构建
 // 1、记错函数名称,13.x 之前是 getStaticProps,现在已经改为 generateStaticParams,用于生成动态路由的参数列表
SSR
import { getServerSideProps } from 'next/server'
function Blog({ data }) { return <div>{data}</div> }
async function getServerSideProps() {
    const data = await fetch('xxxx');
    return { props: { data: data.json() } }
     // 2、data.json() 是异步的,异步解析数据,处理 json 数据转换为 js 对象 - const res = await data.json;
}
export default Blog;             
SSG
function Blog({ data }) { return <div>{data}</div> }
async function getDerivedStateParams() {
    const data = await fetch('xxxx');
    return { props: { data: data.json() } }
     // 3、return 的应该是个路由列表 例如 return [{slug: 'first'}, {slug: 'second'}]
}
const revalidate = 60; // 秒
 // 4、export const revalidate = 60; // 页面重新验证时间
export default Blog;
                         
- 第二遍 2025.1.26 -
 // 1、SSR getServerSideProps: fetch 数据需要 await: const data = await fetch('xxx')
 // 1、SSR getServerSideProps: 异步解析数据需要 await: const res = await data.json()
- 第一遍 2025.1.22 -
 // 1、SSG generateStaticParams 用于生成动态路由的参数列表
 // 2、SSG revalidate 用于定义页面重新验证时间 - 页面将重新生成,运行时生成/更新静态页面以增量更新
// 作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情?
- 第七遍 2025.2.9 -
挂载阶段 constructor(初始化state和事件处理函数的this) render getDerivedStateFromProps componentDidMount
更新阶段 shouldComponentUpdate(nextProps, nextState) componentDidUpdate(prevProps, prevState) render getDerivedStateFromProps
卸载阶段 componentWillUnMount
useEffect模拟三个componentDidMount componentDidUpdate componentWillUnMount
useLayoutEffectcommit 阶段后同步执行阻塞重绘

- 第六遍 2025.1.31 -
类组件
挂载阶段
getDerivedStateFromProps
componentDidMount - 首次渲染执行
render
 // 1、少写了 constructor - 初始化 state 和绑定事件处理函数的 this
更新阶段
getDerivedStateFromProps
componentDidUpdate - 更新后执行
shouldComponentUpdate(nextProps, nextState) - 返回布尔值判断是否需要重新渲染
render
卸载阶段
componentWillUnMount - 卸载前执行

函数组件
useEffect 模拟 componentDidMount回调函数执行一次 ⭐️ // 内部函数
		  模拟 componentDidUpdate 依赖更新再次执行回调函数
          模拟 componentWillUnMount 返回一个清理函数在组件卸载前执行
useLayoutEffect  commit 阶段后同步执行函数阻塞重绘
- 第五遍 2025.1.25 -
- 第四遍 2025.1.24 -
 // 1、shouldComponentUpdate 写错在挂载阶段,实际应该归属于更新阶段
 // 2、阶段划分是:挂载-更新-卸载,其中更新阶段记错了 - 应该是当组件的 state/props 变化,进入更新阶段,重新执行 render 函数来更新 DOM
- 第三遍 2025.1.19 -
 // 1、挂载阶段:少写了 constructor - 初始化 state 和绑定时间处理函数的 this
 // 2、除了类组件函数的生命周期,还要意识到函数组件也通过 hooks 模拟生命周期
// 2021.06 子盒子在父盒子中水平垂直居中有几种方法?
- 第二遍 2025.2.9 -
1.father { display: flex; justify-content: center; align-items: center; }
2.father { display: table-cell; text-align: center; vertical-align: middle; }
.son { display: inline-block; }
3.son { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; }
4.son { position: absolute; transform: translate(-50%, -50%); top: 50%; left: 50%; }

- 第一遍 2025.1.30 -
flex .father { display: flex; justify-content: center; align-items: center; }
 .son { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto;  }
 .son { position: absolute; transform: translate(-50%, -50%); left: 50%; top: 50%; }
 .son { display: table-cell; text-align: center; vertial-align: middle; }  // 1、不是给子元素 添加 table-cell,这个方法的核心是把 父盒子 设置为 table-cell,使得另外两个属性生效 - 而子盒子作为行内块类型,垂直、居中 .father { display: table-cell; text-align: center; vertical-align: middle; } .son { display: inline-block; }
// 作业3:迭代器原理
- 第三遍 2025.2.9 -
使用 next 访问可迭代对象的所有值不需要知道对象类型 ⭐️ // 遍历

- 第二遍 2025.1.30 -
提供暂停和恢复代码执行能力[Symbol.iterater] 默认迭代器
yield 暂停next 恢复执行 {done, value}
 // 迭代器使用 next 方法在可迭代对象中遍历数据
- 第一遍 2024.12.25 -
// 作业66:React如何处理错误
- 第七遍 2025.2.9 -
static getDerivedStateFromError + componentDidCatch 处理 render + commit 阶段的错误是为了错误不蔓延到整个应用影响 UI 展示
class ErrorBoundaries extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false }
    }
    static getDerivedStateFromError() { // 返回新的 state
        return { hasError: true }
    }
    componentDidCatch(e, info) {
        console.log(info);
    }
    render() {
        if(this.state.hasError) {
            return <div> There are errs. </div>
        } else {
            return this.props.children;
        }
    }
}

- 第六遍 2025.1.29 -
React 提供两个函数处理错误 static getStateFromError()componentDidCatch用于捕获 
 // 1、静态函数名称错了 getDerivedStateFromError
render+commit 阶段的错误阻止错误蔓延到整个应用影响 UI 展示
几个情况不会捕获异步回调ssr错误边界内部
class Boundaries extends React.Component{
    constructor() {
        super(props);
        this.state = { hasError: false }
    }
    static getStateFromError() {  // 2、getDerivedStateFromError
        return { hasError: true }
    }
    componentDidCatch(e, info) {
        console.log(e)
    }
    render() {
        if(this.state.hasError) {
            return <p> There are errs. </p>
        } else {
            return this.props.children;
        }
    }
}
- 第五遍 2025.1.22 -
- 第四遍 2025.1.18 -
// 作业6:new操作符实例化一个对象的过程
- 第三遍 2025.2.9 -
function newf() {
    const ctor = [].shift.call(arguments);
    const obj = new Object();
    obj.__proto__ = ctor.prototype;
    const res = ctor.call(obj, ...arguments);
    return typeof res === 'object' ? res : obj;
}

- 第二遍 1.28 -
function newFun () {
    let obj = new Object();
    const ctor = [].shift.call(arguments);
    obj.__proto__ = ctor;  // 1、不是构造函数,是构造函数的原型对象 obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
	return typeof res === 'object' ? res : obj;
}
- 第一遍 1.20 -
 // 1、不是调整 constructor 的指向

①⑤ 74题 2025.1.27 - 2.4 开始

2.8 - LTN2 ✅2021.05 29 WebSocket ✅2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击 ✅2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝? ✅2020.03 模块化 ✅2021.06 跨页面通信 ✅2019.07 数组去重(算法整理) ✅2021.07 柯里化函数实现

2.8 - LTN1 ✅作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现? ✅作业 89:mysql 中类型的区别:varchar 和 char,date datetime ❌作业90:金玉满堂是什么意思?在花卉中的说法是什么? ❌作业91:三次握手的过程和具体包的作用 ❌作业92:前端中的序列化是什么?常见序列化方法 ❌作业93:闭包的作用和原理 ❌2021.07 事件流 + 事件模型 ❌2021.06 链表 141. 环形链表 ❌2021.09 get和post有什么区别

// 作业 89:mysql 中类型的区别:varchar 和 char,date datetime
- 第一遍 2025.2.8 -
varchar 按照内容长度存储
char 需要指定固定存储长度不论存储什么都是这么大内存占用 ⭐️ // 内容长度不够时,使用空格填充到指定长度
date YYYY-MM-DD ⭐️ // 3字节
datetime YYYY-MM-DD HH:MM:SS ⭐️ // 8字节
// 作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?
- 第三遍 2025.2.8 -
arguments 默认会同步命名参数修改 aguments[1] 就会就该对应的命名参数
但是不会同步 设置了默认值的命名参数
数组直接传入arguements=[[1,2,3]] ->(...arr)
(first, ...rest)
[...arr] => 扩展运算符一一传入可迭代对象的值 
{...obj} => 扩展运算符复制可枚举属性
- 第一遍 -
 // 1、数组直接传入的 arguments 错了,应该是 [[1,2,3]]
 // 2、{...obj} 作用描述有问题,复制的是 可枚举属性
// 2021.07 柯里化函数实现
- 第二遍 2025.2.8 -
function curry(fn, ...args1){
	if(fn.length <= args1.length) {
        fn(...args)
    } else {
        return (...args2) => curry(fn, ...args1, ...args2)
    }
}
// 2019.07 数组去重(算法整理)
- 第三遍 2025.2.8 -
1[...new Set(arr)] Array.from(new Set(arr)) 
2arr.filter((it, index) => it === arr[index]) ⭐️ // 判断条件 index === arr.indexOf(it)
3
function unique(arr){
    const hash = {};
	return arr.filter(it => hash[it] ? false : hash[it] = true)
}
- 第一遍 -
 // 1、Set 不是数组类型,需要再次转换成数组 [...new Set(arr)] Array.from(new Set(arr))
// 2021.06 跨页面通信
- 第三遍 2025.2.8 -
同源页面:①localStorage setItem+window.onstorage=()=>{}只能处理非当前页面
new BroadCastChannel('b') 两个页面实例化相同广播b.postMessage('xxx')+b.onmessage=()=>{}
非同源页面
postMessageH5 API使用 const win = window.open(url); win.postMessage('xxx'); + window.onMessage=()=>{}
iframe 使用 origin 配置成同源
- 第一遍 -
 // 1、构造函数的名称记错:BroadCastChannel
// 2020.03 模块化
- 第三遍 2025.2.8 -
CommonJS同步适合服务端运行时输出值的拷贝require+exports
AMD异步依赖前置并行加载多个依赖按照加载先后顺序执行无法控制执行时机define(['a.js'], function(a){ a.call(); }) ⭐️ // 加载完了立即执行调用,无法按序执行
CMD异步依赖就近并行加载多个依赖按照书写顺序执行define(function(require, exports, model) { 
	let a = require('./a.js'); ⭐️ // 加载后按序执行
    a.call();
})
ES6异步服务端+客户端通用解决方案编译时输出值的引用export default import as from 
- 第一遍 -
 // 1、少写了 AMD - 加载模块后直接执行,无法保证执行顺序;CMD - 加载后,直调调用才按序执行
// 2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?
- 第三遍 2025.2.8 -
基础类型浅拷贝都是拷贝一个副本
引用类型浅拷贝拷贝的是引用地址修改新值会影响原值深拷贝-引用地址指向的数据修改新值不会影响原值
[...arr]/Array.from(arr)/arr.concat()/arr.slice()/Object.assign({}, obj)/{...obj}
JSON.parse(JSON.stringify(target))/
function isObj(t) {
    const type = typeof t;
    return t !== null && (type === 'function' || type === 'object');
}
function handleSpecial(t, type) {
    switch(type) {
		case '[object Date]':
            return new Date(t.getTime());
            break;
		case '[object RegExp]':
            return new RegExp(t.source, t.flags);
            break;
		case '[object Symbol]':
            return Symbol.for(t.description);
            break;
        default:
            const ctor = t.constructor;
            return new ctor(t); 
    }
}
function clone(target, m = new weakMap()) {
    if(!isObject(target)) {
        return target
    }
    const detailType = Object.prototype.toString.call(target);
    const specialType = ['Map', 'Set', 'Array', 'Object'].map(it => `[object ${it}]`);
    if(!specialType.includes(detailType)) {
        return handleSpecial(target, detailType);
    }
    
    const ctor = target.constructor;
	let res = new ctor();
    if(m.has(target)) {
        return target;
    }
    m.set(target, res);
    if(detailType === '[object Map]') {
		target.forEach((value, key) => {
            res.set(key, clone(value, m))
        })
    } else if (detailType === '[object Set]') {
        target.forEach(it => {
            res.add(clone(it, m))
        })
    } else if(Array.isArray(target)) {
        target.forEach((it, index) => {
            res[index] = clone(it, m);
        })
    } else {
        for(let i in target) {
            res[i] = clone(target[i], m);
        }
    }
    return res
}
- 第一遍 -
 // 1、属性名写错了:正则类型的 t.flags
 // 2、 方法错了:object 不是可迭代对象,不能使用 for...of,这里应该改为 for...in
// 2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击
- 第三遍 2025.2.8 -
脚本攻击的原因浏览器给同源协议开了两个后门:①可以引入第三方数据 通过CORS可以请求第三方资源
XSS跨域脚本攻击
定义-前端页面注入代码修改服务端数据
解决:①禁止渲染时 DOM 执行 js 代码例如 imgscript
服务端对用户输入等数据过滤转义
HttpOnly 禁止js访问cookiejs引擎会禁用 document.cookie 
CSP 内容安全策略 Content-Security-Policy 客户端信任的引入资源域名 meta+响应头 ⭐️ // script-src 'self'
CSRF跨站请求伪造攻击
定义-冒用用户信息获取服务端信任修改服务端数据
cookie Samesite
验证码+CSRF token
请求头 referer
- 第二遍 -
⭐️ // CSP - 从客户端角度讲的,哪些域名的资源可以被信任;CORS - 从服务端角度讲的,哪些域名可以访问这个资源
- 第一遍 -
 // 1、少写了:XSS的解决方案中,在「渲染时」禁止标签执行 js 代码
// 2021.05 29 WebSocket
- 第三遍 2025.2.8 -
全双工双向实时通讯协议
由于 HTTP 的请求-应答模型无法实现实时通讯
1使用新的协议 ws:80 wss:443
2使用新的发现服务方式 URI而不是ip+端口
3不受同源协议限制使用二进制帧双向通讯 ⭐️ // 在语义语法上完全不兼容
4基于 HTTP 进行连接GET 请求头中 Connection: update如果响应头中 Update: websocket切换协议
- 第二遍 -
 // 1、少写了:修改了协议名 ws:80 wss:443
- 第一遍 -
⭐️ // 2、少写了全名:全双工 - 双向通信协议

2.2 - LTN2 ❌作业25:简单写下 请求创建过程 ✅作业49:牛顿三大定律、热力学两大定律 ✅作业54:举杯邀明月,对影成三人。的上一句 ❌作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? ❌作业55:flex 常见缩写 ❌作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ❌作业61:常见的数据结构有哪些 ❌作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ❌作业76:显卡的作用? ✅作业77:重绘和重排是什么?有什么区别? ✅作业79:地球四季的成因是什么? ✅2019.07 显示省略号 ✅2021.04 25 HTTP/2 特性

// 2021.04 25 HTTP/2 特性
- 第三遍 2025.2.2 -
语义上完全兼容 HTTP/1.1语法上完全不一样
1压缩头部字段HPACK字典表算法
2使用二进制帧双向传输数据流-模拟虚拟的流多路复用-多个请求响应复用一个连接多个请求响应之间没有了顺序关系解决了队头堵塞问题
3服务器推送
4使用HTTPS
5设置优先级
- 第一遍 -
 // 1、少写一个特性:服务器推送
// 2019.07 显示省略号
- 第三遍 2025.2.2 -
.line {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.lines {
    display: -webkit-box-;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    ⭐️ // line-height: 10px; height: 30-padding;
}
- 第一遍 -
 // 1、css 属性名错误:-webkit-box-orient: vertical;
// 作业79:地球四季的成因是什么?
- 第二遍 2025.2.2 -
和太阳旋转的椭圆形无关
和太阳直射夹角 + 穿过大气层厚度有关强度
⭐️ // 直射点的移动 + 太阳照射时长 - 夹角和地面角度的改变,改变了太阳辐射面积和经过大气层的路程衰减的辐射能量强度
// 作业77:重绘和重排是什么?有什么区别?
- 第二遍 2025.2.2 -
解析DOM树 - 样式计算 css - 布局树 - 分层树 - 绘制
重绘只需要修改分层+合成
⭐️ // 触发方式主要是 字体、背景、边框颜色
重排需要布局+分层+合成性能消耗大
⭐️ // 字体大小、padding/margin、激活伪类、style样式的修改、resize、页面初始渲染、DOM增删、位置内容的修改、DOM布局查询
// 作业54:举杯邀明月,对影成三人。的上一句
- 第三遍 2025.2.2 -
花间一壶酒独酌无相亲
// 作业49:牛顿三大定律、热力学两大定律
- 第三遍 2025.2.2 -
牛顿三大定律加速度定律 a=F/m惯性定律作用力和反作用力
热力学两大定律能量守恒定律熵增定律在自然条件下变得混乱是趋势
- 第一遍 -
 // 1、少写了惯性定律 - 公交车记忆:刹车-加速度、乘客-惯性、地上痕迹-摩擦

1.31 - LTN1 - 8题

✅作业70:Vue 和 React 的 Diff 算法比较 ❌作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ✅作业80:对DOM 树的理解 - 定义和作用 ✅作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更? ❌作业87:Nextjs 中预渲染是什么?有哪些模式?写一个动态路由的page ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ❌2021.07 数组扁平化(一层、全部展开、指定深度) ✅2021.07 bind、apply/call三者异同+apply/call实现bind

// 2021.07 bind、apply/call三者异同+apply/call实现bind
- 第五遍 2025.1.31 -
改变函数的 this 指向 + 不传第一个参数时函数的 this 指向 window
bind 返回函数副本apply/call 立即执行函数
   apply 的第二个参数是类数组或者数组call 接收多个参数
Function.protytype.fakeBind = function(thisArg, ...args1) {
    let func = this;
    return function F(...args2) {
        if(func instanceof F) {
            return new func(...args1, ...args2)
        }
        func.call(thisArg, ...args1, ...args2)
    }
}
- 第四遍 2025.1.25 -
- 第三遍 2025.1.24 -
 // 1、bind 实现应该绑定在原型对象上实现,这样才能 this 指向调用函数
- 第二遍 2025.1.19 -
 // 1、bind 函数接收多个参数 - 第一个是 this 指向;后面接收多个参数
// 作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?
- 第二遍 2025.1.31 -
13.x 开始 Link 中不再写 a 标签用于跳转 - 因为 Link 会被解析为 a 标签
文件路由系统 -  pages 改为 src/app/page.tsxLayout.tsx global.css 可以直接修改全局布局方便布局
后端路由系统 - src/app/api/route.tsx
- 第一遍 2025.1.22 -
 // 1、少写了后端路由系统
// 作业80:对DOM 树的理解 - 定义和作用
- 第三遍 2025.1.31 -
DOM 树是 HTML 的树形对象结构属性对应于 DOM 节点的各种特性 ⭐️ // 「对象化表示」,是按照 HTML 结构转换的树形结构的对象模型
js - DOM 树提供了 JS 访问修改 HTML 的交互接口
浏览器 - 浏览器渲染依赖于 DOM 结构 ⭐️ // 浏览器根据 DOM 结构构建页面布局
框架 - 框架利用 VDOM 比较得到最小更新 UI 再改动真实 DOM提高了 Diff 性能 ⭐️ // 提高页面更新性能
- 第二遍 2025.1.25 -
- 第一遍 2025.1.24 -
 // 1、除了js,少写了浏览器 - 渲染页面的依据和框架视角 - VDOM Diff 计算提高更新性能
// 作业70:Vue 和 React 的 Diff 算法比较
- 第六遍 2025.1.31 -
Vue 双端指针指针算法 + 静态节点跳过 + 找不到匹配节点先判断同类型+ key 的节点是否同索引索引相同则复用 + 列表组件同类型同key 的节点复用
React分层比较 + 同层级同类型节点优先比较 - 不同节点类型直接重新渲染同类型同key 移动复用同类型 修改属性
⭐️ // React 是精确到节点属性级别的 Diff 计算,适合大型动态复杂场景;Vue 基于组件级别的 Diff 计算
- 第五遍 2025.1.25 -
 // 1、Vue 少写了静态节点跳过 + 基于组件级别的更新算法
 // 2、React 是基于单个节点属性级别,适合大型冬天复杂场景
- 第四遍 2025.1.24 -
 // 1、Vue - 找不到匹配的节点后,先找同类型+同key的节点进行索引比较,一致则复用,不一致则移动到新位置修改复用
- 第三遍 2025.1.18 -
 // 1、Vue - 双端指针算法比较后,是找同类型和同key的节点比较
 // 2、Vue - 少写了:静态节点跳过比较
- 第二遍 2025.1.13 -

1.30 - LTN1 - 2题

✅作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ✅2023.07 clickhouse、mysql、mongodb异同

LTN3 ❌作业3:迭代器原理 ✅作业41:TED 如何解决焦虑

LTN4 ✅作业4:什么是生成器?有什么作用? ✅作业18:多进程浏览器会开启几个进程(5个点)?相同站点的页面,可以复用么? ✅作业19:发起请求后,得到 301,有效信息是哪些? ✅作业21:下面这段代码输出结果是什么?为什么

console.log(11, fun1);
function fun1(n1, n2) { arguments[1] = 10; return n1 + n2; }
var fun1 = 1;
console.log(22, fun1)

✅作业22:分别输出什么?

function foo(){
    var a=1;
    let b=2;
    {
        let b=3;
        var c=4;
        let d=5;
        console.log(a);
        console.log(b);
    }
    console.log(b);
    console.log(c);
    console.log(d);
}
foo();

❌作业24:写出事件循环系统的流程图,常见宏任务、微任务有哪些。并写出几个常见题目的输出结果

Promise.resolve().then(() => {
    console.log(1);
    Promise.resolve().then(() => {
        console.log(2);
    }).then(() => {
        console.log(3);
    }).then(() => {
        console.log(4);
    })
}).then(() => {
    console.log(5);
}).then(() => {
    console.log(6);

})
------

async1 = async () => {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
function async2(){
    console.log('async2');
}
console.log('script start');
setTimeout(() => {
    console.log('setTimeout');
})
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(() => {
    console.log('promise2');
    return new Promise(function(resolve) {
        resolve();
    })
}).then(() => {
    console.log('promise3');
})
console.log('script end');

✅作业27:写一个 sleep 函数 ✅作业28:写出 js 按什么顺序执行

<script src='./a.js' defer></script>
<script src='./b.js'></script>
<script>
    console.log('event start');
    document.addEventListener('DOMContentLoaded', () => {
        console.log('DOMContentLoaded finish');
    })
    window.addEventListener('load', () => {
        console.log('onload finish');
    })
</script>
<body>
    <div>
        <script>
            let i = 0;
            while(i<1000) {
                i++;
            }
            console.log('compute finished');
        </script>
    </div>
</body>

✅作业35:写出HTPPS和HTTP的差异?对称加密和非对称加密有什么异同?混合加密是怎么做的? ✅2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现? ❌2021.06 子盒子在父盒子中水平垂直居中有几种方法? ✅2024.10 第十一章 期约与异步函数 小结 ✅2019.06 随机给一个盒子添加一个十六进制的颜色

// 2019.06 随机给一个盒子添加一个十六进制的颜色
function addColor() {
    let res = '#';
    const arr = ['0', ...,'9','a', ... 'f'];
    Array(6).forEach(it => {
        const index = Math.floor(Math.random() * 16);
        res += arr[index];
    })
    return res;
}
// 2019.07 css中的动画特性可以用js实现,那为什么还要用css来实现?
- 第一遍 2025.1.30 -
js 实现动画 占用主线程执行低版本浏览器可能需要写兼容性代码
css 实现动画 有些 transformopacity 只需要合成线程并且可以开启 GPU 加速加载完 css 就可以开始渲染动画不用等 js 执行优雅降级
⭐️ // 避免回流重绘 + 自然降级 + 硬件GPU加速 + 预扫描加载快
// 作业35:写出HTPPS和HTTP的差异?对称加密和非对称加密有什么异同?混合加密是怎么做的?
- 第一遍 2025.1.30 -
HTTPS = HTTP + SSL/TLS TCP 层改为 SSL/TLS 
对称加密 - 只有一个密钥用于加密解密
非对称加密 - 公钥可以发放客户端加密使用公钥私钥用于解密只有服务端独有
混合加密 - 使用非对称加密商讨出对称加密使用的密钥 ⭐️ // 协商
// 作业28:写出 js 按什么顺序执行
- 第一遍 2025.1.30 -
b.js - event start - compute finished - a.js - DOMContentLoaded finish - onload finish
// 作业27:写一个 sleep 函数
- 第一遍 2025.1.30 -
async function sleep(delay){
    return new Promise((resolve) => setTimeout(() => resolve(), delay));
    ⭐️ // 不用写 async + setTimeout 可以简写成 setTimout(resolve, delay)
}
await sleep(100);
// 作业22:分别输出什么?
- 第一遍 2025.1.30 -
1 3 2 4 error
// 作业21:下面这段代码输出结果是什么?为什么 
- 第一遍 2025.1.30 -
11 function
22 1
执行上下文中函数内先创建 auguments再函数提升最后变量提升
// 作业19:发起请求后,得到 301,有效信息是哪些?
- 第一遍 2025.1.30 -
Location 重新导航
// 作业18:多进程浏览器会开启几个进程(5个点)?相同站点的页面,可以复用么?
- 第一遍 2025.1.30 -
浏览器进程渲染进程*n插件进程*n网络进程GPU进程
同协议同域名同端口 可以复用当前渲染进程
// 作业4:什么是生成器?有什么作用?
- 第一遍 2025.1.30 -
生成器暂停恢复代码执行* 声明yield next 
自定义可迭代对象模拟协程
// 2023.07 clickhouse、mysql、mongodb异同
- 第二遍 2025.1.30 -
clickhouse: 列式数据库支持异步操作读写性能都好适合大型动态场景 ⭐️ // 高吞吐、低延迟
mysql关联型数据库支持事务操作读写性能好在并发操作时会出现数据一致性问题
mongodb文档型数据库存储半结构化/非结构化数据读的性能好写的性能弱在异步操作时有数据一致性问题 ⭐️ // 分布式场景下一致性问题
- 第一遍 2025.1.22 -
// 作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序)
- 第二遍 2025.1.30 -
SELECT DISTINCT t.name, 
    CONCAT('(', t.interval, ')') AS interval, 
    UPPER(t.type) AS type,
    COUNT(*) AS with_null,
	COUNT(t.name) AS without_null,
    AVG(t.gap) AS avg // -- MAX MIN SUM
FROM Times as t, Issues as i
WHERE t.id = 1 AND
	(t.price > 10
    OR it.price IS NULL
    OR it.price BETWEEN 5 AND 10,
    OR it.price in (1,2),
    OR YEAR(it.date) = 2025)
GROUP BY t.date
SORT BY t.date
- 第一遍 2025.1.23 -
// 作业41:TED 如何解决焦虑
- 第二遍 2025.1.30 -
1列出所有可以解决问题的方案去做 do it badly ⭐️ // anything worth doing worth doing badly the first time
2给自己一些同情 ⭐️ // 我们不会和天天批评自己的人交朋友,我们也不要做自己这样的朋友 
3找到意义 ⭐️ // 即使别人不知道,自己一定要认可自己的行为
- 第一遍 2024.12.25 -

1.29 - LTN1 - 4题

❌作业66:React如何处理错误 ✅作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ✅作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?

// 作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?
- 第二遍 2025.1.29 -
path.join() 纯拼接
path.resolve() 拼接绝对路径 / ⭐️ // 绝对路径解析
process.cwd() 获取 node 执行环境的当前目录
__dirname 当前文件的所处目录
- 第一遍 2025.1.22 -
// 作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询
- 第二遍 2025.1.29 -
INNER JOIN 内联查询 取AB表都满足条件的行即交集 ⭐️ // 内连接 
LEFT OUTER JOIN 左表查询 取左表满足条件的行及右表补充的行
RIGHT OUTER JOIN 右表查询 取右表满足条件的行及左表补充的行
UNION 连接两次查询 ⭐️ // 拼接
封装 CREATE VIEW mySearch AS SELECT ...
下次使用的时候直接 FROM mySearch
- 第一遍 2025.1.22 -
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第二遍 2025.1.29 -
Redux 将全局状态保存在 store 对象树中通过 action 才能 dispatch 改变 store ⭐️ // 改变 state
RTK - Redux tollkit 官方推荐依赖
reducer: 使用 旧的state  action 计算得到 新的state
import { crateStore } from 'redux';
const reducer = (oldState = { value: 1 }, action) => { 
    if(action.type === 'add') { return { value: oldState.value + 1 } } 
}
const store = createStore(reducer);
store.dispatch({ type: 'add' });
store.subscribe(() => { console.log(store.getState()) })
- 第一遍 2025.1.22 -

1.28 - LTN1 - 2题

✅作业20:浏览器打开一个 URL 发生了什么 ✅2021.09 Object属性排序、与Map的区别

// 作业20:浏览器打开一个 URL 发生了什么
- 第三遍 1.28 -
1浏览器进程 UI线程将用户输入内容或者链接转换成 URL
2通过 IPC 协议传递给网络进程
网络进程:① 查询本地缓存有缓存且有效则 强缓存生效
	无缓存/有缓存但已过期DNS 解析得到 IP
    TCP 队列排队三次握手进行连接
    组装请求请求头cookie 等信息
    发起请求
响应 状态码301302 重定向获取响应头中的 Location 进行重新跳转
		   304 Not Modified使用缓存并刷新本地缓存时间
           200 响应类型是 os-stream 下载类型直接触发下载停止导航
           	   响应类型是 html和浏览器进程建立管道边下载边解析
3渲染进程
分为主线程合成线程预解析线程解析 html 开启预扫描线程扫描 jscss提前下载
管道传输完毕后通知浏览器进程更新 页面前进后退url安全锁
下载完成解析完成之前 - 解析白屏
主线程解析DOMDOM树 - 计算样式表 - 布局树 - 分层树 - 绘制指令列表
合成线程 - 栅格化图块合成位图[会开启 GPU 加速]
浏览器进程 UI线程- 合成
- 第二遍 1.20 -
 // 1、IPC 协议名错了 2、有缓存但缓存失效也继续请求 3、请求的响应,应该先判断状态码再判断类型
// 2021.09 Object属性排序、与Map的区别
- 第三遍 1.28 -
正整数 > 字符串 > 负数 > 浮点数 > Symbol
区别有序属性可以是任意类型m.size()大数据占用内存小增删快可迭代对象
- 第二遍 1.21 -

1.27 - LTN1 - 8题

✅作业2:支持迭代器的原生语言特性有哪些? ✅作业43:TED 怎么提升自信 ✅作业42:TED 如何和大脑正确交流沟通 ✅作业39:shim 和 polyfill 区别 ❌作业6:new操作符实例化一个对象的过程 ✅2019.07 为什么要清除浮动?怎么清除浮动? ✅2021.06 CSS 选择器 - 权重/匹配方式

// 2021.06 CSS 选择器 - 权重/匹配方式
- 第二遍 1.28 -
!important > 行内 > 内联外联只和加载顺序有关
#id > .red a[href] :hover LVHA > div ::after > *
- 第一遍 1.20 -
// 2019.07 为什么要清除浮动?怎么清除浮动?
- 第二遍 1.28 -
对于浮动元素块级元素把它当做不存在行内块元素环绕布局
父组件中的子组件浮动时会造成高度塌陷父级元素的背景无法撑开padding和border展示异常
清除浮动:①给父级元素添加高度 父盒子里最后添加一个盒子 .box {clear:both} 父盒子添加伪元素 .father::before{ content:''; display: block; clear: both; } BFC .father{ overflow: hidden } - BFC 的盒子计算高度时包含浮动元素
- 第一遍 1.20 -
 // 1、写错了:是给父元素添加BFC + 父盒子设置高度(宽度不行
// 作业39:shim 和 polyfill 区别
- 第二遍 1.28 -
shim垫片提供新API使用原生功能实现处理兼容问题 ⭐️ // 优雅降级
polyfill补丁实现浏览器原生功能不提供新的API ⭐️ // 实现原生 API 中缺少的功能
- 第一遍 1.20 -
// 作业42:TED 如何和大脑正确交流沟通
- 第二遍 1.28 -
1I like it, I want it, I've chosen it!
2、Make it familiar!
大脑只会响应我们给它的语言 + 图像(具体的);大脑天然追寻快乐,所以把想做的事情绑定到巨大的快乐中,不想做的事情、停留在原地,绑定痛苦;大脑喜欢熟悉的东西,做得越多越能够坚持
- 第一遍 1.20 -
// 作业43:TED 怎么提升自信
- 第二遍 1.28 -
1repetitionrepetitionrepetition
2不要批评自己鼓励表扬做得好的地方 - 引导自己
3用自己的语言解释世界
- 第一遍 1.20 -
// 作业2:支持迭代器的原生语言特性有哪些?
- 第三遍 1.28 -
Array.from  ...扩展运算符  数组解构  for...of  new Set  new Map  yield *  Promise.all/race
- 第二遍 1.25 -
- 第一遍 1.20 -
 // 1、少写了:yield * 操作符 - 只能在生成器中使用;不是对象解构,是「数组解构」

LTN①④

❌2019.07 为什么要清除浮动?怎么清除浮动? ❌2023.07 clickhouse、mysql、mongodb异同 ❌作业70:Vue 和 React 的 Diff 算法比较 ❌作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ❌作业87:Nextjs 中预渲染是什么?有哪些模式?实现一个动态路由的预渲染-一个客户端一个服务端 ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接

LTN①④ 错题重做 - 23题 - 2025.1.25-1.26 4h50m

✅作业2:支持迭代器的原生语言特性有哪些? ✅作业43:TED 怎么提升自信 ✅作业42:TED 如何和大脑正确交流沟通 ✅作业39:shim 和 polyfill 区别 ✅作业6:new操作符实例化一个对象的过程 ❌2019.07 为什么要清除浮动?怎么清除浮动? ✅2021.06 CSS 选择器 - 权重/匹配方式 ✅作业20:浏览器打开一个 URL 发生了什么 ✅2021.09 Object属性排序、与Map的区别 ✅2021.07 bind、apply/call三者异同+apply/call实现bind ✅2021.07 数组扁平化(一层、全部展开、指定深度) ❌2023.07 clickhouse、mysql、mongodb异同 ✅作业66:React如何处理错误 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情? ✅作业80:对DOM 树的理解 - 定义和作用 ✅作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ✅作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ❌作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ✅作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别? ✅作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更? ❌作业87:Nextjs 中预渲染是什么?有哪些模式?实现一个动态路由的预渲染-一个客户端一个服务端 ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接

// 作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接
- 第二遍 1.26 -
mysql2: 专为 node 设计的 mysql 驱动程序可以直接连接数据库要写大量 SQL
⭐️ // 提供了 mysql2/promise 模块,允许 async/await + 可以管理多个数据库的连接
SequelizeORM 将数据库映射成对象提供封装的函数处理上手快
⭐️ // 被映射成对象的是 数据库的 表和行

import { createPool } from 'mysql2';
 // 1、依赖包错了,是从 import mysql from 'mysql2/promise'
const pool = await createPool(database, host, password, {});
 // 2、创建连接池时不需要 await,只是创建连接 + 传递对象而不是具体顺序的参数 const pool = mysql.createPool({host, user, password, database})
const mysql = pool.connect();
 // 3、获取连接时需要 await 来等待连接成功 + 函数错了 const connection = await pool.getConnect();
 // 4、查询数据 const [rows] = await connection.execute('SELECT * FROM table WHERE ...')
创建连接后会一直保持连接状态直到手动关闭 mysql.close()  // 5、错误,是 Sequelize 的情况

import { sequelize, modal, DataTypes } from 'sequelize';
⭐️ // 导入 Sequelize Modal - 用首字母大写
sequalize.init(database, { host, password })
 // 6、创建连接不是用 init 函数(这是定义模型属性的方法),是实例化创建连接 const sequelize = new Sequelize(database, user, password, { host: 'localhost', dialect: 'mysql' })
 // 7、这里就是上面记错的:创建连接后,将一直保持打开状态,并对所有查询使用相同状态,关闭连接需要调用实例关闭 sequelize.close()
class MyModal extends modal {
    constructor() {
        return {
            date: {
                type: DataTypes.DATE,
                allowNull: false
            }
        }
    }
}
 // 8、定义模型是扩展 Modal - 先继承Modal,再init定义模型
/**
	class MyModal extends Modal{}
	MyModal.init({
		 date: {
			type: DataTypes.DATE,
            allowNull: false
         }
	}, {
		sequelize,
		modalName: 'MyModal'
	})
*/
this.modal.MyModal.findAll()

- 第一遍 2025.1.22 -
mysql2 mysql 驱动轻量需要手动写大量原生 SQL
⭐️ // 专为 Node 设计的 MySQL 驱动程序
 // 1、少写了:可以直接连接 MySQL 服务器 以及 提供的两个连接特点:①提供了 mysql2/promise 模块,支持 async/await 的异步操作 + ②支持创建连接池,管理多个数据库的连接
 // 2、缺点少写了:没有内置的对象关系映射 ORM 功能
Sequelize提供统一事务操作使用函数包装 SQL更适合函数式编程
 // 3、强大的 ORM 功能,可以将数据库的表和行「映射为对象/模型」,直接通过 js 操作 - 会被自动转换成 SQL
连接方式 
 // 4、代码写法真的不记得了
import mysql from 'mysql2/promise';
const pool = mysql.createPool({host, user, password, database}); // 创建连接池
const connection = await pool.getConnection(); // 获取连接
const [rows] = await connection.execute('SELECT * FROM users WHERE id = 1'); // 查询

import { Sequelize, DataType, Modal } from 'sequelize';
const sequelize = new Sequelize(database, user, password, {
    host: 'localhost',
    dialect: 'mysql'
})
// 创建连接,一直保持连接状态,直到实例关闭 sequelize.close();
// 模型是 Sequelize 的本质
class User extends Modal {}
User.init({
    name: { type: DataType.STRING, allowNull: false },
    age: { type: DtaType.NUMBER }
}, {
    sequelize,
    modelName: 'User'
})
// 作业87:Nextjs 中预渲染是什么?有哪些模式?实现一个动态路由的预渲染-一个客户端一个服务端
- 第二遍 1.26 -
预渲染机制主要包含 SSR服务器渲染SSG 静态生成ISR 增量静态生成
SSR-在请求时在服务端将需要引入的动态数据打包进 html返回给浏览器直接解析而不是交给客户端的 js 处理
SSG-在构建时生成 html + revalidate 配置验证有效时间时间到期后框架后台重新渲染
SSG
export default function Blog ({ params }) => {
    return <div> { params.slug } </div>
}
export async function generateStaticParams() {
    return [ {slug: 'first'}, {slug: 'second'} ]
}
export const revalidate = 60;
SSR
⭐️ // import { getServerSideProps } from 'next/server';
export async function getServerSideProps() {
    const data = fetch('xxxx');  // 1、少写了 await:const data = await fetch('xxxx')
     // 2、const res = await data.json() - 异步解析数据,处理JSON数据转换为 js 对象
    return { props: data.json() }
}
export default function Blog (props) => {
    return <div> {props.text} </div>
}

- 第一遍 2025.1.22 -
 // 1、什么是预渲染 - 预渲染机制主要是 SSR服务端渲染、SSG「静态生成」、ISR 增量静态生成
-SSG 构建时预渲染 + 静态资源增生 ISR  // 2、名词记错了:SSG 静态生成、ISR 层两静态生成
	getStaticProps  // 3、这是13.x以下版本使用的函数 - 已改为 generateStaticParams,主要用于生成动态路由的参数列表
    revalide - 设置预渲染的更新时间超过设定时间后框架在后台重新渲染生成新的后下次访问使用新的渲染
	 // 4、单词错了 revalidate - 是「页面重新验证时间」- 页面将重新生成,运行时 生成/更新 静态页面以增量更新 ISR
-SSR 服务端预渲染 - 在服务端先构建把数据注入 js 包裹进 HTML返回给浏览器时直接进行解析渲染即可不交给客户端的 js 处理了  ⭐️ // 在每次请求时,在服务端重新渲染 HTML 并嵌入拉取到的动态数据
	getStatexxxx  // 5、getServerSideProps
export function Blog(props) {return <div>{props.text}</div>}
export const revalid = 60;
export function getStaticProps() {}
export function getStatexxxx() {}
 // 6、上述单词都错了,准确写法如下:
/**
	SSG - src/app/[slug]/page.js
	export default function Blog({ params }) { return <div> hello,{params.slug} </div> }
	export const revalidate = 60;
	export async function generateStaticParams(){
		return [{slug: 'first'}, {slug: 'second'}]; // 返回生成动态路由的参数列表
	}
	
	SSR
	import { getServerSideProps } from 'next/server';
	export default function SSRPage({ data }){return <div>{ data }</div>}
	export async function getServerSideProps(context){
		const res = await fetch('xxxx');
		const data = res.json();
		return {
			props: { data } // 作为组件的 props 传递给当前页面的组件
		}
	}
*/
// 作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更?
13.x 开始 Link 标签在底层被解析为 a标签所以之后使用的时候 Link 里面不用写 a 标签了
文件路由系统src/app/page.tsx layout.tsx 用于统一全局布局
后端路由系统src/app/api/xxx/route.tsx 

- 第一遍 2025.1.22 -
Nextjs 13.x 开始Link 中不再使用 a 标签因为框架把 Link 标签直接解析为 a 标签
路由从之前的 pages/ 路由模式 改为 src/app 
优点自定义统一布局 layout.tsx + global.css
 // 1、只写了「文件路由系统」的变更,还少写了「后端路由系统」的变更 从 pages/api 变更为 src/app/api
// 作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别?
- 第二遍 1.26 -
path.join() 拼接路径
path.resolve() 拼接绝对路径 ⭐️ // 把 / 当做根路径
process.cwd() 当前 node 指令执行的当前环境
__dirname 当前文件所在目录

- 第一遍 2025.1.22 -
path.join(a,b)用于拼接两个地址 a/b ⭐️ // 纯拼接
path.resolve(ab)解析地址 a/b  // 1、是「绝对路径解析」,把 / 当做根目录 -> /a/b
process.cwd()文件的当前路径  // 2、node 执行时所在目录
__dirname文件的当前路径
// 作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询
- 第二遍 1.26 -
INNER JOIN 内联查询AB表都满足条件的行相当于取交集
LEFT OUTER JOIN 左联查询A 表满足及B表相关
RIGHT OUTER JOIN 右联查询B表满足及A表相关
UNION 拼接两次查询结果
封装 CREATE VIEW mySearch SELECT a from table ❌❌ // 1、少了一个 AS 
下次使用 SELECT a from mySearch

- 第一遍 2025.1.22 -
INNER JOIN求交集两个表都符合条件的行 ⭐️ // 内连接
LEFT OUTER JOIN左表所有符合筛选条件及关联的右表的数据
RIGHT OUTER JOIN右表所有符合筛选条件及关联的左表的数据
UNION拼接上下两个查询
封装一个 SQLcreate mySelect SELECT name from table; 
 // 1、create View MY AS 关键词:Create View ... AS
select name from mySelect
// 作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序)
- 第二遍 1.26 -
SELECT DISTINCT t.date, 
    CONCAT('(', t.title, t.source,')') AS title,
	UPPER(t.weekday) AS week,
    COUNT(*) AS total_with_null,
	COUNT(t.date) AS tota_without_null,
    SUM(t.interval) AS sum,
    MAX(t.interval) AS max,
    MIN(t.interval) AS min,
    AVG(t.interval) AS avg,
FROM ltn_time AS t, ltn_data AS d
WHERE d.id = 1,
    AND (d.price > 10 
         OR d.price IS NULL
        OR d.price BETWEEN 5 AND 10
        OR d.price IN (1, 2)
		OR YEAR(d.date) = 2025)
GROUP BY d.date
SORT BY d.source DESC

- 第一遍 2025.1.23 -
 // 1、由于 from 中有两张表,如果查询的 column 名在两张表都存在,有重名就会报错 - 所以查询的时候最好声明表名 t.name1 t.age
SELECT constinct name1, age,   // 2、关键词错了 distinct
    concat(sex, height) as answer2, // ⭐️ concat(t.sex, '(', t.height, ')') as answer2
    toUpperCase(weight3) as weight3,  // 3、函数名错了 upper
    count(name) as name42, count() as name41,  // 4、关键词参数错了 包含 null - 查询时传递 count(*) as name41_with_null;不包含 null - 查询时包含 column 名是对的
    sum(age) as age5, max(age) as max5, min(age) as min5, avg(age) as avg5
from table as t, sex as s
where t.id = 1 
And 
(t.price > 10 
 or t.price = Null  // 5、关键词错了:IS 操作符主要用于判断某个值是否为 NULL,且 t.price = null 的比较结果始终是 UNKNOWN, 因为 NULL 表示未知的值,无法确定它是否和另一个值相等 - t.price is null
 or t.price between 5 to 10  // 6、关键词错了 between-and:t.price between 5 and 10
 or t.price in [1,2]  // 7、关键词错了,应该是():t.price in (1,2)
or year(s.time) = 2025)
group by age
sort age desc  // 8、关键词错了 sort by
// 作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用
- 第二遍 2025.1.26 -
Redux 核心是将整个应用的状态全部由 store 管理只能通过 action dispatch 变化
reducer 通过旧的 state  action 生成新的 state - 返回新的 state 而不是改变
RTK Redux ToolKit 官方推荐的核心工具包
import { createStore } from 'redux';
const reducer = (oldState = { value: 'old' }, action) => {
    if(action.type === 'add') {
        return { value: 'new' }
    }
}
const store = createStore(reducer); ⭐️ // dispatch subscribe getState
store.dispatch({ type: 'add' });
store.subscribe(() => { console.log(store.getState()); })  ⭐️ // 可以更新 UI

- 第一遍 2025.1.22 -
将全局状态以对象树的形式存储在 store 
reducer(oldState, action) => newState
action用于描述 state 变化redux 中要改变 state 只能通过把 action dispatch  store
RTKredux toolkit 核心包
const { createStore } from 'redux';
const reducer = (state={val:0}, action) => {if(action.type){return {val: state.val+1}}}
const store = createStore(reducer); // dispatch getState ? ❌ // 1、少一个 subscribe
store.dispatch({type: true});
 // 2、通过 subscribe 更新 UI 来响应 state:store.subcribe(() => {console.log(store.getState())})
// 作业80:对DOM 树的理解 - 定义和作用
- 第二遍 2025.1.25 -
DOM 树是 HTML 文件结构的对象结构表示标签的属性以对象属性存储 ⭐️ // 对象化表示,树形结构对象模型
 js 而言DOM 树提供了可以访问修改 HTML 的交互接口 ⭐️ // 编程接口
对框架而言VDOM 树提供了机会可以先从 VDOM 计算最小更新量 Diff 来更新最新UI提高更新性能
对浏览器而言浏览器根据 DOM 渲染 HTML 结构 ⭐️ // DOM 是渲染页面的依据

- 第一遍 2025.1.24 -
用对象树描述 HTML标签的属性就是 DOM 节点的属性值
⭐️ // DOM 是 HTML 文档的「对象化表示」,是按照 HTML 结构转换的树形结构的对象模型
提供了浏览器对 HTML 操作入口获取 DOM 节点进行操作 ⭐️ // 提供了一个编程接口 - DOM 是 js 可以访问、修改 HTML 的基础
 // 1、除了 js,还少写了 浏览器 + 框架 视角
/**
	对浏览器而言,DOM 是「渲染页面的依据」,浏览器根据 DOM 结构构建页面布局
	对框架而言,框架通过比较 虚拟DOM 和实际 DOM 树的差异,以最小 UI 更新量,提高页面更新性能
*/
// 作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情?
- 第五遍 2025.1.25 -
挂载阶段 ⭐️ // 就是组件的创建和首次插入 DOM
    constructor 初始化 state 和绑定事件处理函数的 this
	getDerivedStateFromPropsrendercomponentDidMount 首次渲染
更新阶段
	shouldComponentUpdate(nextProps, nextState)
    getDerivedStateFromPropsrendercomponentDisUpdate(prevProps, prevState)
卸载阶段
	componentWillUnMount 卸载前清理监听定时任务等
⭐️ // 函数组件 useEffect useLayoutEffect
    
- 第四遍 2025.1.24 -
类组件
挂载阶段:⭐️ // 组件的创建 + 首次插入 DOM
shouldComponentUpdate(nextProps, nextState) - 自定义布尔值是否重新渲染  ❌❌❌❌ // 3、应该归属于更新阶段
getDerivedStateFromProps -  props 更新 state
render - 初次渲染
constructor - 初始化 state  绑定事件处理函数的 this
渲染阶段 ❌❌❌❌ // 1、不是渲染阶段,是更新阶段 - 当组件的 state/props 发生变化,进入更新阶段,重新执行 render 函数来更新 DOM
componentDidMount - 第一次渲染执行 ❌❌❌❌ // 2、应该归属于 挂载阶段
componentDidUpdate(prevProps, prevState) - 更新时渲染 ⭐️ // 更新后
render - 重新渲染
卸载阶段componentWillUnMount - 卸载前
函数组件
useEffect - 模拟componentDidMount依赖空数组[]初次渲染时执行内部函数
		  - 模拟componentDidUpdate更新依赖数组内容重新执行内部函数
		  - 模拟componentWillUnMount返回一个清理函数在卸载前被调用
useLayoutEffect - 在commit挂载到 DOM 之后执行但是是同步的阻塞重绘直到执行完毕

- 第三遍 2025.1.19 -
挂载阶段 ⭐️ // 创建组件+首次挂载
 ❌❌❌ // 1、少写:constructor - 初始化 state 和 绑定事件处理函数的 this
 getDerivedStateFromProps -  props 更新 state
 render - 触发渲染生成 VDOMdiff 后挂载到真实 DOM
 componentDidMount - 挂载DOM后 ⭐️ // 只在首次渲染执行
更新阶段 ⭐️ // 当组件的 state/props 更新
 shouldComponentUpdate(nextProps, nextState) - 返回布尔值自定义是否需要重新渲染
 getDerivedStateFromProps -  props 更新 state 
 render - 更新渲染diff 后挂载到真实 DOM
 componentDidUpdate(prevProps, prevState) - 更新后
卸载阶段
 componentWillUnMount - 卸载前一般用于清理避免内训泄漏
 ❌❌❌ // 2、上面的都是「类组件」的生命周期,应该还要意识到「函数组件」也是通过 hooks 来模拟生命周期了
 /**
 	useEffect - 模拟 componentDidMount - 组件首次渲染时,内部函数执行
 			  - 模拟 componentDidUpdate - 依赖数组传入 state/props,变化时内部函数再次执行
 			  - 模拟 componentWillUnMount - 可以返回一个清理函数,组件卸载时执行
 	useLayoutEffect 在DOM 更新后,副作用在 commit 阶段同步执行,阻塞「重绘」
 */

- 第二遍 -
❌❌ // 1、不应该以渲染阶段(Render Phase:render-commit)来进行回答,应该以生命周期阶段(Lifecycle Phases:挂载-更新-卸载)来回答 
render
	类组件 shouldComponentUpdate(nextProps, nextState) - 自定义是否需要更新
	❌❌ // 1、shouldComponentUpdate 不是在 render 阶段执行的,是在更新阶段,用于确定当前组件是否需要渲染
	按序执行所有 hooks
	函数组件 useEffect - 根据依赖是否变更确定是否有副作用
commitDiff 计算出的最小更新 UI 应用到真实 DOM 执行几个生命周期
	执行副作用链表
	类组件 componentDidMount - 首次挂载后执行 ⭐️ // 一般用于初始化数据
	类组件 componentDidUpdate - 更新挂载后执行 ⭐️ // 参数是(prevProps, prevState) 一般用于操作 DOM 和执行副作用
	类组件 componentDidUnMount - 卸载前更新 ❌❌ // 2、不是 Did 是 Will,componentWillUnMount 清理定时器、取消监听,避免内存泄漏
    函数组件 useLayoutEffect -  useEffect 的唯一差别是时机回调在挂载到 DOM 之后执行并且是同步的会阻塞渲染主线程
❌❌ /** 3、重新简单梳理下 类组件的三个生命周期阶段
	挂载阶段-创建组件+首次挂载到DOM:初始化constructor()、static getDerivedStateFromProps->纯函数render()->挂载后componentDidMount
	更新阶段-重新执行render构建新VDOM:static getDerivedStateFromProps->shouldComponentUpdate->render()->更新后componentDidUpdate()
	卸载阶段:卸载前componentWillUnMount
*/
❌❌ // 4、函数组件 主要是使用 hooks 来模拟实现生命周期 useEffect + useLayoutEffect
    
- 第一遍 -
react 的生命周期主要是针对 类组件 来讲的函数组件通过 useEffect 等hooks 实现
 // 1、对也是对的,但是函数组件的生命周期还是可以「展开讲讲」
 componentDidMount - 首次渲染 commit 阶段Diff 计算出的最小更新量 应用到真实 DOM 后触发一般用于 初始化数据
 componentDidUpdate - 更新commit 阶段更新真实 DOM 后触发
 // componentDidUpdate(prevProps, prevState),一般用于「操作DOM」和「执行副作用」
 componentDidUnMount - 卸载组件之前调用一般用于清理定时器解除监听等避免内存泄漏
 // componentWillUnMount
 shouldComponentUpdate - 自定义组件是否重新渲染传入的参数为 新的状态和属性
⭐️ // shouldComponentUpdate(nextProps, nextState)
 // 2、不止上面四个,还有 constructor()、render(),分三个阶段来描述:
// ⑤constructor - 类组件的构造函数,用于初始化组件状态 state + 绑定事件处理函数 + super(props) 传递 props
// ⑥render - 渲染函数,返回一个React元素
 // 3、分为三个阶段,分别执行对应的生命周期
// 挂载阶段:⑤constructor()、①componentDidMount、⑥render()
// 更新阶段:④shouldComponentUpdate、⑥render()、②componentDidUpdate
// 卸载阶段:③componentWillUnMount

 // 4、函数组件的生命周期
 // ① useEffect - 模拟了三个生命周期
// 模拟componentDidMount - 当组件首次渲染时,useEffect 内部的函数执行
// 模拟componentDidUpdate - 通过在 useEffect 的依赖数组中传入状态或属性,依赖发生变化时,内部函数再次执行
// 模拟componentWillUnMount - useEffect 可以 return 一个函数作为清理函数,会在组件卸载时执行
 // ② useLayoutEffect - [commit阶段]在DOM更新后,浏览器下一次重绘前,同步执行 - 会阻塞重绘
// 		   	   useEffect - [commit阶段]在DOM更新后,异步执行 - 适合不依赖DOM布局的副作用,例如数据获取、订阅事件等
// 作业70:Vue 和 React 的 Diff 算法比较
- 第五遍 2025.1.25 -
Vue双端指针算法 - 同类型+同key 索引 - 移动复用 ❌❌❌❌❌ // 1、少写了:静态节点跳过 ;是基于组件级别的更新算法
React分层比较 + 同类型优先比较不同标签直接重新渲染相同标签更新属性列表组件-同类型同key复用 ❌❌❌❌❌ // 2、少写了:是基于单个节点属性级别的,适合大型动态复杂场景

- 第四遍 2025.1.24 -
Vue 1双端指针算法匹配 
2找不到匹配的点则判断索引是否一致可复用 ❌❌❌❌ // 1、找不到匹配的点,找到同类型+同 key的节点,进行索引比较,一致则复用,不一致则移动到新位置修改使用
3列表组件 key+同标签
4静态节点优化跳过 
React 分层比较+同类型优先比较同类型同key复用同类型则更新属性不同类型重新渲染
⭐️ // React 是精确到单个节点属性级别,适合大型动态复杂场景; Vue 基于组件级别的更新算法

- 第三遍 2025.1.18 -
React:⭐️ // 精确到「单个节点属性级别」,适合大型复杂动态场景
分层比较 + 同类型优先比较
- 分层比较时不同类型则重新渲染
- 相同类型则更新属性
- 列表组件同层级相同类型 + key 相同则移动到当前位置来复用

Vue:⭐️ // 基于「组件级别」更新的算法
- 双端比较算法找寻匹配的节点
- 找不到匹配的先查找索引相同索引则直接复用 ❌❌❌ // 2、找到同类型+同key来比较
- 列表组件同类型 + key 相同复用
❌❌❌ // 1、静态节点跳过比较 - 不要遗漏了,是个很重要的性能优化

- 第二遍 2025.1.13 -
React Diff: 
	比较虚拟 fiber 树和真实 UI 对应的 fiber 深度优先遍历
⭐️ // 是精确到单个节点属性级别的算法,适合大型复杂动态场景 
	分层同类型优先比较如果同层级标签相同则更新当前节点属性即可
    				如果标签不同则标记重新渲染
                    如果同层级标签相同 key 相同则直接复用移动到当前位置
                    ⭐️ // 「列表组件,根据 key 确定新老节点关系」
Vue Diff:
⭐️ // 是基于组件级别更新的算法,适合简单场景
	双端指针法比较两棵 VDOM 分别从首尾双端进行比较如果标签相同则修改属性标签不同则重新渲染
❌❌ // 1、双端比较算法是直接找匹配节点,不进行 标签修改
    如果找不到匹配的 DOM 节点则按 key 查找相同标签节点进行复用
❌❌ // 2、key 是第三层比较逻辑,第二层比较逻辑是「索引」 - 若索引没有变化,则不更新当前节点;如果索引有变化,判断是否「同类型 + 同key」,移动到新位置
❌❌ // 3、性能优化,编译时标记的静态节点跳过 - 虽然老生常谈,但是这其实是一大优化

- 第一遍 -
React
 // 1、简单描述下 React 的 Diff 算法是「精确到单个节点的属性级别」,适合「大型复杂场景」,例如大量动态组件和频繁更新的场景
1从根节点开始比较采用分层比较
 // 2、对,这里可以标题记忆 「① 分层比较 + 同类型比较优先策略」
2同类型节点则比较属性的变更不同类型则停止比较标记为需要重新渲染
3同类型+同key 的节点进行复用
 // 3、这里主要是指:「列表组件」中,根据 key 确定新老树子节点的关系
 // 4、少写了「② DFS 深度优先遍历子节点」
Vue
 // 5、描述:「更新基于组件级别」
1比较新老两棵 VDOM 采用双指针分别从新老两棵树的头尾开始比较适用于小型应用快速计算出最小更新量 UI
 // 6、双指针其实很不明确,「双端比较算法-双向指针」
2同类型+同key 的节点进行复用
 // 7、是会复用,但是场景是「双端比较法没有找到足够匹配的节点,就根据节点的索引进行比较」
// 8、检索「同类型+同key 的节点」,「索引位置」没有变化,就认为这个节点不需要更新;若有变化,就「移动到新位置」,「复用旧列表」
3跳过比较静态编译标记的静态节点加快比较速度
// 作业66:React如何处理错误
- 第六遍 2025.1.25 -
React 提供了两个函数用于处理错误为了让错误不影响到整个应用能够正常展示 UI捕获 render commit 阶段的错误
static getDerivedStateFromError 返回 state 用于更新 UIcomponentDidCatch 用于操作错误
class Boundaries extends React.component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }
    static getDerivedStateFromError() {
        return { hasError: true }
    } 
    componentDidCatch(e, info) {
        console.log(info);
    }
    render() {
        if(this.state.hasError) {
            return <div> There are Errs. </div>
        } else{
            return this.props.children;  
        }
    }
}
<Boundaries> <My /> </Boundaries>

- 第五遍 2025.1.22 -
react 提供了两个函数用于处理渲染流程中 render+commit 抛出的错误避免错误影响 UI 的渲染
static getDerivedStateFromError(修改UI) + componentDidCatch(错误处理)
class Boundaries extends React.Component {
    constructor(props) {
        ❌❌❌❌❌ // 1、派生类使用 this 需要调用 super(props); - 初始化组件的内部状态和绑定 this 关键字
        this.state = { hasError: false };
    }
    static getDerivedStateFromError() {
        return { hasError: true }
    }
    componentDidCatch(e) {
        console.log(e.message);
    }
    render() {
        if(this.state.hasError) {
            return <div> There are errs. </div>
        } else {
            return this.props.children;
        }
    }
}
<Boundaries> <My /> </Boundaries>

- 第四遍 2025.1.18 -
错误边界 static getDerivedStateFromError可以更新state来修改UI不能使用 this - 不绑定具体的组件 + componentDidCatch处理错误),可以将 render + commit 阶段的错误进行处理不会让错误蔓延至整个应用 UI
class ErrorBoundary extends React.Component {
    constructor(props) {
        super(props);
        this.state = { hasError: false };
    }
    static getDerivedStateFromError(e) {
        return { hasError: true };
    }
    componentDidCatch(e) {
        console.log(e.message);
    }
    render() {
        if(this.state.hasError) {
            return <div>There are errs.</div>
        } else {
            return this.props.children;
        }
    }
}
<ErrorBoundary> <MyComponent/> </ErrorBoundary>
// ⭐️ 错误边界不能捕获:事件回调、异步代码、ssr、错误边界内部错误

- 第三遍 -
react 提供了两个错误处理函数一般被称为错误边界用于处理在 render  commit 阶段的错误 ⭐️ // 用于避免错误传递到整个应用,影响 UI 的正常展示
static getDerivedStateFromError 静态方法用于捕获错误后有UI修改的时机
componentDidCatch 生命周期用于捕获错误后提供记录的时机
class boundaries extends React.component{ ⭐️ // 组件名称应该用大写 Boundaries
    constructor(props){
         super(props);
         this.state={hasError:false};
    }
    static getDerivedStateFromError(){
        this.setState({hasError:true});  ❌❌❌ // 1、这个 getDerivedStateFromError 是不能使用 this 的 -> 因为它是一个静态方法,不与组件实例绑定,所以不能直接访问this(因为它是在类级别上被调用的,而不是在组件实例的上下文中) -> 这种方式是为了保持函数的纯粹性,只能通过返回一个对象来修改state  return { hasError: true }
    }
    componentDidCatch(err){
        console.log(err);
    }
    render(){
        if(this.state.hasError){
            return <div>Its Err.</div>
        }else{
            return this.props.children;
        }
    }
}
错误边界不会捕获 1当前错误边界组件中的错误 2 异步函数 setTimeout setInterval因为不在 react 的渲染流程中 ⭐️ // 还有一个事件回调中的错误,也不在 react 渲染流中

- 第二遍 -
React提供两个错误处理API用于获取render和commit 期间生命周期当中的错误避免错误传递影响到整个应用页面的展示 ⭐️ // 是为了 UI 正常显示设计的
Static Getderivedstatefromerror 捕获错误提供重新渲染UI的时机(需要返回 state 对象来更新)
Componentdidcatch 捕获错误提供记录错误的时机
class MyBoundaryError extends React.Component {
    constructor() { ❌❌ // 1、错误边界这里需要传入 props - 才能传入子组件
        ❌❌ // 2、派生类使用 this 需要调用 super(props);
        this.state = { hasError: false };
    }
    static getDerivedStateFromError() {
        return { hasError: true };
    }
    componentDidCatch(e) {
        console.log(e);
    }
    render() {
        if(this.state.hasError) {
            return <div> There is error.</div>
        } else {
            return <p>Page.</p>
            ❌❌ // 3、如果没有错误,应该要渲染错误边界包裹的子组件
            // return this.props.children;
            // 可以看下使用:<MyBoundaryError><MyCom /></MyBoundaryError>
        }
    }
}

- 第一遍 -
React 提供了两个错误 API使用两个 API 可以组装错误边界
 // 1、捕获的错误是: React render+commit 中的错误
// 2、两个 API 分别是: static getDerivedStateFromError() [callback UI 有错误时提供渲染时机]
// componentDidCatch()[组件实例方法 有错误时提供时机记录错误]
// 3、为什么要有专门的错误边界处理错误:因为它可以捕获渲染、生命周期、构造函数中的错误,防止错误传播到整个应用,使其他页面能够正常展示
// 4、不会被捕获的四种错误:①事件回调中的错误,因为不在react工作流里(错误处理API被设计的目的是避免错误影响 UI显示,只有commit阶段会造成UI变化)②异步代码(setTimeout)③SSR④在错误边界组件内部发生的错误
<ErrorBoundary><MyComponent /></ErrorBoundary>
class ErrorBoundary extends React.Component {
    constructor(props){
        super(props);
        this.state = { hasError: false };
    }
    static getDerivedStateFromError(err){
        return { hasError: true }
    }
    componentDidCatch(err, errInfo) {
        console.log(err, errInfo);
    }
    render() {
        if(this.state.hasError) {
            return <h1> Sth wrong.</h1>
        }
        return this.props.children;
    }
}
// 2023.07 clickhouse、mysql、mongodb异同
- 第一遍 2025.1.25 -
clickhouse列式数据库读写性能都好但是没有事务适合高吞吐低延迟的场景例如日志 ⭐️ // 支持复制、分布查询
mysql关系型数据库读写性能不错支持事务操作但是在高并发下会有一致性问题
mongdb文档型数据库读的性能好写的性能弱在异步操作下会有一致性问题
❌❌ // 1、在分布式场景下可能出现一致性问题 ;适合半结构化/非结构化数据

- 第一遍 2025.1.22 -
 // 4、mysql 是关系型数据库-适合事务处理场景;MongoDB 是文档型数据库-半结构化/非结构化数据;ClickHouse 是列式数据库-适合高吞吐、低延迟的分析场景
clickhouse各种数据存储不支持事务操作适合大型数据例如操作记录等  // 1、少写了:读写性能都高,场景适合少了归类:高吞吐、低延迟场景,例如日志;操作少写了:支持复制和分布查询
mysql结构化的数据存储支持事务操作查询写入性能好一致性好适合小型应用  // 3、一致性描述错误:高并发时可能出现一致性问题
mongdb/半结构化的数据存储写入性能好一致性一般  // 2、性能描述错误:写的性能弱,读的性能好;适合数据少写了:半结构化数据,类似 XML HTML JSON 等,以及非结构化数据;一致性描述:在分布式场景下可能出现数据一致性问题
// 2021.07 数组扁平化(一层、全部展开、指定深度)
一层
arr.flat() [].concat(...arr) [].concat.call([], ...arr) [].concat.apply([], arr)
全部展开
arr.flat(Infinity) arr.toString().split(',')
function expense(arr, deep = 1) {
    return arr.reduce((pre, cur) => {
        return pre.concat(Array.isArray(cur)&&deep > 1 ? expense(cur, deep - 1) : cur)
    }, [])
}
// 2021.07 数组扁平化(一层、全部展开、指定深度)
- 第三遍 2025.1.24 -
一层
1arr.flat();
2[].concat(...arr);
3[].concat.call([], ...arr);[].concat.apply([], arr)
全部展开
❌❌❌ // 1、少写了 :arr.flat(Infinity)
Json.stringify(arr).split(',') ❌❌❌ // 2、错了: JSON.stringify 不会展开数组,arr.toString.split(',') - toString 将数组转换成字符串时,会打平所有数组
function deepFlat(arr, deep = 1) {
    return arr.reduce((pre, cur) => {
        pre.push(Array.isArray(cur) && deep>1 ? deepFlat(cur, deep - 1): cur);
        ❌❌❌ // 3、不能用 push, deepFlat 返回的是数组 -> 应该使用 concat
        return pre;
    }, [])
}

- 第二遍 2025.1.19 -
一层arr.flat();[].concat(...arr);[].concat.call([], ...arr);[].concat.apply([], arr)
全部展开arr.toString().split(','); ❌❌ // 1、少写了 arr.flat(Infinity)
function flatten(arr, deep = 1){
    return arr.reduce((pre, cur) => {
        pre.concat(Array.isArray(cur)&&deep>1 ? flatten(cur, deep) : cur) 
        ❌❌ // 2、deep 传递的时候记得 -1
    }, [])
}

- 第一遍 -
const arr = [1, 2, [3, 4], 5];
一层
1arr.flat()
2[].concat.call([], ...arr) // ❌ 1、对 concat 了解不够深,concat 会铺平一层,同样原理的还可以写成 [].concat(...arr) / [].concat.apply([], arr)
全部展开
1arr.flat(Inifity)  // 2、拼写错误 Infinity
2Object.prototype.toString.call(arr).join(',')  // 3、学杂了,这个写法会返回'[object Array]' 用于查询 arr 的类型 -> arr.toString().join(',')
3
function spread(arr) {
	return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) ? spread(cur) : cur), [])
}
指定深度
function spread(arr, deep) {
	return arr.reduce((pre, cur) => pre.concat(Array.isArray(cur) && deep > 1 ? spread(cur, deep - 1) : cur), [])
// 2021.07 bind、apply/call三者异同+apply/call实现bind
- 第四遍 2025.1.25 -
三者都是用于处理函数的this 指向如果不传递第一个参数的话this指向window
bind的是返回函数的副本apply和call 是立即执行函数
apply 第二个参数类数组或数组call 接收多个参数
Function.prototype.fakeBind = function(thisArg, ...args1) {
    const fun = this;
    return function F(...args2) {
        if(fun instanceof F) {
            return new fun(...args1, ...args2);
        }
        fun.call(thisArg, ...args1, ...args2);
    }
}

- 第三遍 2025.1.24 -
共同点是1都是用于改变函数的this指向 2如果第一个参数不传那么函数的this指向将指向window
差异点1bind是绑定修改函数的this指向后返回函数的副本apply和call是修改函数的this指向后立即执行
2apply的第二个参数是类数组或者数组call 可以有多个参数依次传入
function fakeBind(thisArg, ...args1) { ❌❌❌ // 1、bind 实现应该绑定在原型对象上实现: Function.prototype.fakeBind - 这样 this 才是指向调用 bind 的函数
    let func = this;
    return function F(...args2) {
        if(func instanceof F) {
            return new func.call(thisArg, ...args1, ...args2);
        }
        func.call(thisArg, ...args1, ...args2);
    }
}

- 第二遍 2025.1.19 -
都是用于改变函数内部 this 指向不穿参数时指向 window
bind-返回函数副本apply/call-立即执行apply第二个参数是类数组/数组call 接收多个参数
function fakeBind(thisArg) { ❌❌ // 1、「bind 有多个参数,第一个是 this 指向,后面接收多个参数 」...arg1
    const cb = this;
	return function F(...arg) {
        if(cb instanceof F) {
            return new cb(...arg) ❌❌ // 2、return new cb(...arg1, ...arg)
        }
        cb.apply(thisArg, arg);❌❌ // 3、cb.apply(thisArg, [...arg1, ...arg])
    }
}

- 第一遍 -
1都是用于改变函数内部 this 指向的上下文
2在第一个参数不传的时候this 默认指向 window
1bind 改变函数内部 this 指向后返回函数副本执行的话需要再次调用
apply/call 都是改变函数内部 this 指向后直接执行
2aplly 的第二个参数接收一个类数组/数组call 的第二个参数开始接收多个参数
Function.prototype.fakeBind = function(thisArg, ...arg1) {
    const func = this;
    return (...arg2) => {
         // 1、遗漏了判断是否为 new 调用
        func.call(thisArg, ...arg1, ...arg2);
    }
     /**
     return function F(...arg2) {
     	if(func instanceof F) {
     		return new func(...arg1, ...arg2);
     	}
     	func.call(thisArg, ...arg1, ...arg2);
     }
	*/
}
// 2021.09 Object属性排序、与Map的区别
- 第三遍 2025.1.25 -
正整数 字符串 负数 浮点数 Symbol
有序属性可以是所有类型字符串/Symbolm.size()可迭代类型大小增删快

- 第二遍 2025.1.21 -
Object属性排序: 正整数>字符串>负数>Symbol  ❌❌ // 1、少写了浮点数
与Map的区别:①有序 属性值任意类型 可迭代对象 数据量大时占用空间小增删快 访问数量方便m.size() Object.keys(obj).length

- 第一遍 -
Object 属性排序正整数 > 字符串 > 负数 > 浮点数 > Symbol
与Map的区别
 Map的键可以是任何类型 VS Object 的键只有字符串/Symbol
 Map可以记录插入顺序 VS Object 无法记录自有自己的默认排序方式
 Map写法简单长度为 m1.size() VS Object.keys(Object).length
 Map在大量数据时占用空间小增删快
 Map是可迭代对象 VS Object不是
// 作业20:浏览器打开一个 URL 发生了什么
- 第三遍 2025.1.25 -
浏览器进程 UI线程 - 把用户输入的内容或者是链接补充成完整的 url
通过 IPC 协议传递给网络进程
网络进程1查找当前缓存是否存在存在判断当前缓存是否在有效期内 - 直接返回缓存内容强缓存生效
2缓存不存在或者是缓存已失效的情况下进行DNS解析获得IP
3排队tcp队列进行三次握手连接
4拼接请求例如请求头cookie等信息
5发起请求
如果状态码为301或者302则获取响应头中的location字段进行新的url导航跳转
如果状态码为304则代表继续使用缓存并刷新缓存时间
如果状态码为200判断当前返回的响应类型是
	Osc stream 下载类型触发下载导航结束
	HTML 和渲染进程建立管道边下载边解析	
渲染进程分为主线程 合成线程 预解析线程
1在解析的过程当中提前扫描HTML文件如果有js或者css文件则使用预解析线程提前下载
2下载完成后通知浏览器进程对页面url前进后退安全锁进行刷新
3下载完成但是解析还没有完成的过程当中出现的是解析白屏
渲染流程分为以下几步
主线程中执行解析dom得到dom树 - 计算css样式表 - 布局布局树 - 分层得到分层树 - 绘制指令列表
合成线中执行栅格化会启用GPU加速图块合成位图
浏览器UI线程执行合成帧

- 第二遍 2025.1.20 -
1浏览器进程-UI线程将用户输入的内容/链接拼接成完整的url
2网络进程通过 RPC 传递给网络进程后查找本地缓存  ❌❌ // 1、协议名错了:IPC - 进程通信协议
	有缓存则直接返回 - 强制缓存无缓存则继续进行导航  ❌❌ // 2、少了:有缓存但缓存失效
3、①DNS 解析-缓存得到 ip
TCP 队列排队三次握手建立连接
组装请求包括cookie请求头等
发起请求
4返回的类型是 stream 进入下载流程不再导航  ❌❌ // 3、应该是先判断状态码,200的状态码下再判断类型
返回的类型是 html 判断状态码 301302 使用响应头中的 Location 字段触发 重新导航
					状态码是 200 和渲染进程建立管道
                     ❌❌ // 4、少写了 304,使用本地缓存,并刷新缓存有效时间
5渲染进程边下载边传输解析
渲染进程分为主线程预解析线程合成线程解析时预扫描js css文件提前下载
下载完成时通知浏览器进程刷新页面URL地址安全锁前进后退按钮
下载完成后解析完成前出现解析白屏
6主线程解析DOM DOM树 - 样式计算 css样式表 - 布局树 - 分层树 - 绘制指令列表
合成线程+GPU进程-栅格化位图转换成为图块
浏览器UI线程-合成

- 第一遍 -
1浏览器进程-UI线程将用户输入内容/链接转换为完整的URL
2通过IPC协议传递给网络进程
	 查询本地是否有缓存有缓存且在有效期内-直接返回缓存
	有缓存但无效/无缓存DNS查询解析得到ip
	TCP队列排队连接-三次握手
	组装请求信息请求头cookie等
    发起请求301/302 重定向取响应头中的Location作为新的url导航
    		  304 Not Modified使用缓存并刷新本地缓存有效期
              200 OK type类型为 o-stream 下载流
              				   html 和渲染进程建立管道边下载边传输
3渲染进程主线程合成线程预解析线程
	和网络进程建立管道并开启预解析如果检测到js/css文件提前下载
	传输完成后渲染进程还没解析完之前页面会出现短暂空白就是解析白屏
    传输完成后会通知浏览器进程更新 页面+url+前进后退+安全锁
注意解析过程中遇到js代码会暂停DOM解析因为JS可能会改变DOM
渲染流程
	主线程解析DOM(DOM树)->样式计算(css样式表)->布局(布局树)->分层(分层树)->绘制(指令列表)
	合成线程->栅格化(图块->位图)此处会开启GPU进程加速
	浏览器进程-UI线程->合成展示
// 2021.06 CSS 选择器 - 权重/匹配方式
- 第二遍 1.25 -
!important > 行内 > 内联+外联按照加载顺序排序
#id > .red a[href] :hover LVHA > div ::after > *
选择器从右向左解析

- 第一遍 1.20 -
!important > 内联 > 行内   // 1、行内 > 内联/外联
#id > .red [attr] :hover LVHA > div ::after > *
从右向左
// 2019.07 为什么要清除浮动?怎么清除浮动?
- 第二遍 1.25 -
块级元素把浮动元素当做不存在行内元素围绕布局
高度塌陷子元素浮动后父元素的背景和 border padding 无法撑开 ❌❌ // 1、背景无法撑开 + padding、border 无法正常展示
清除浮动1给父元素最后一个子元素后添加一个 box .box{ clear:both }
2给父元素添加伪元素 .father::before{ content: ''; display: block; clear: both; }
3BFC 给父元素添加 .father { overflow: hidden; } - BFC 高度计算包含浮动元素
4给父元素添加宽度高度 ❌❌ // 2、宽度不对,高度+padding+border

- 第一遍 1.20 -
对于浮动元素行内元素会环绕布局块级元素会当做浮动元素不存在且子元素浮动时会导致父元素背景无法撑开 - 高度塌陷
清除浮动:①子元素其他盒子 BFCoverflow:hidden;   // 1、给父元素添加 - BFC 内部高度计算时包含浮动元素 - 解决高度塌陷的关键
给父盒子设置 padding border  // 2、还有高度
给父盒子末尾添加 box .box { clear: both } 
给父元素添加伪元素 .box::before{ display: block; clear: both; content: ''; }
// 作业6:new操作符实例化一个对象的过程
- 第二遍 1.25 -
创建空对象 - 将新对象的原型指向构造函数的原型对象 - 构造函数this指向新对象 - 执行构造函数为新对象添加属性和方法 - 返回新值/新对象
function fakeNew() {
    let ctor = [].shift.call(arguments);
    let obj = new Object();
    obj.__proto__ = ctor.prototype;
    const res = ctor.apply(obj, arguments);
    return typeof res === 'object' ? res : obj;
}

- 第一遍 1.20 -
1创建空对象
2设置constructor  // 1、新对象/实例的 __proto__ 指向构造函数的原型对象
3将构造函数this指向当前空对象
4执行构造函数为空对象添加属性和方法
5返回对象
function new() {
    let ctor = [].shift.call(arguments);
    let obj = new Object();
    Object.defineProperty(obj, 'prototype', {
        enumerable: false,
        value: ctor
    });  // 1、obj.__proto__ = ctor.prototype;
    const res = ctor.apply(this, arguments);
    return typeof res === 'object' ? res : obj;
}
// 作业39:shim 和 polyfill 区别
- 第二遍 1.25 -
shim垫片会提供新的 API利用浏览器原生功能实现处理兼容问题优雅降级
polyfill补丁实现浏览器原生功能低版本),没有新的 API ⭐️ // 实现浏览器原生 API 缺少的功能

- 第一遍 1.20 -
shim:垫片提供新的 API用浏览器现有功能实现
polyfill:补丁不提供新的API提供兼容性用低浏览器版本的现有功能实现新功能
 // 1、记反了,shim 是处理兼容性问题-优雅降级,polyfill 是实现浏览器不支持的原生API-实现原生API中缺少的功能
// 作业42:TED 如何和大脑正确交流沟通
- 第二遍 1.25 -
1I want it, I like it, I've chosen it!
2、Make it familiar!
You always keep your value.
大脑只会响应你告诉它的语言和图片,说得越详细越好
大脑天然追求快乐,把想要做的事情绑定到非常快乐,把不想做的事情绑定到痛苦
大脑喜欢熟悉的事情

- 第一遍 1.20 -
1、大脑只会对你给的语言和你想的画面有反应
❌ // 1、少写了:I like it!I want it!I've chosen it! 大脑天然逃避痛苦追逐快乐将想要做的事情和巨大的快乐联系起来将停滞在当下的痛苦细细描述
 // 2、大脑喜欢熟悉的事情:让大脑对想要做的事情变熟悉
// 作业43:TED 怎么提升自信
- 第二遍 1.25 -
1repetitionrepetitionrepetition
2不要批评要用鼓励来引导
3用自己的视角解释一切

- 第一遍 1.20 -
 // 1、少写:重复训练!用足够多次的练习带来信心
1少批评自己 
 // 2、少写:而是不断夸奖正确行为 - 自己的或别人的 - 引导自己去学习和改变
2用自己的语言解释世界
// 作业2:支持迭代器的原生语言特性有哪些?
- 第二遍 1.25 -
for...ofArray.from...扩展运算符数组解构创建 Set Mapyield *(只能在生成器函数中使用)Promise.all/race

- 第一遍 1.20 -
... 扩展运算符对象解构for-ofArray.from创建 Map创建 SetPromise.all/race
 // 1、少写了:yield * 操作符 - 只能在生成器中使用;不是对象解构,是「数组解构」

LTN①④ - 2025.1.20开始 12h

LTN1 - 43题 8h33m

✅2019.10 两栏,左边固定,右边自适应的布局 ✅2019.10 三栏,两边固定,中间自适应 ✅2020.07 对象分类、Promise按序执行、实现map ✅2021.04 05-3 白屏优化? ❌2021.07 bind、apply/call三者异同+apply/call实现bind ❌2021.07 数组扁平化(一层、全部展开、指定深度) ✅2022.08 包管理工具 ❌2023.07 clickhouse、mysql、mongodb异同 ✅2024.05 rem是基于什么原理进行适配的? ✅2024.10 怎么实现跨域 ✅2024.11 观察者模式 EventEmitter ✅2024.12 模拟实现promise ✅作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的 ✅作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口) ✅作业53:胜利者一无所获 说的是什么 ✅作业58:React和Vue是怎么描述UI的 ✅作业62:fiber架构是什么?优点和实现方式? ✅作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 ✅作业64:Vue渲染怎么实现的? ✅作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期 ❌作业66:React如何处理错误 ✅作业67:React 怎么做的性能优化? ✅作业68:React hooks 的原理是什么?useEffect useState模拟实现 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业71:React为什么要合成事件?和原生事件有什么不同? ❌作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情? ✅作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序? ✅作业78:使一个标签看不见的几种样式及其区别 ❌作业80:对DOM 树的理解 - 定义和作用 ❌作业81:Redux 核心是什么?reducer、action、dispatch、store、RTK 怎么理解?简单写一个 redux 使用 ❌作业82:写一个复杂的 SQL: ①只返回某列不同的值,相同的忽略 ②拼接A列和B列为字符串后输出 ③将某一列转换为大写输出 ④计算某列行数,一个包含null的个数,一个不包含null个数 ⑤求和、求最大、求最小、求平均某列的值 ⑥给表起别名 ⑦查询条件涵盖 id为1,同时price大于10,或者是NULL,或者是 5-10之间,或者是 1或2,或者年份为2025 ⑧按照某列分组 ⑨按照某列排序(降序) ❌作业83:INNER JOIN、LEFT OUTER JOIN、RIGHT OUTER JOIN 有什么区别?UNION作用是什么?怎么封装一个 SQL 查询 ❌作业84:path.join()和path.resolve()的区别?process.cwd()和__dirname的区别? ✅作业85:Lambdas是什么作业 ❌作业86:Nextjs 中 Link 的变更是哪个版本开始的,有什么变更?从这个版本开始对路由有什么变更? ❌作业87:Nextjs 中预渲染是什么?有哪些模式?实现一个动态路由的预渲染-一个客户端一个服务端 ❌作业88:mysql2 和 Sequelize 的使用场景?有哪些差异?简单写一个连接 ✅2024.09 第八章 对象、类和面向对象编程 小结 ✅2024.11 第十七章 事件

// 2019.10 三栏,两边固定,中间自适应 .left, .right { width: 100px }
- 第三遍 2025.1.24 -
float\BFC 不行
1 position .left, .right { position: absolute; top: 0; }
.rgiht { right: 0; }
.middle { margin: 0 100px; }
2 flex .outer { display: flex; } // 可以使用 justify-content: space-around;
.middle { flex: 1; }
3 table .outer { display: table; width: 100%; }
.left, .right, .middle { display: table-cell; }
grid .outer { display: grid; grid-template-columns: 100px auto 100px; }
4 cacl .left, .right, .middle { float: left; }
.middle { width: cacl(100% - 200px); }

- 第二遍 2025.1.19 -
float\BFC 都因为浮动换行不能用
1
.left, .right { position: absolute; top: 0; }
.right { right: 0 }
middle { margin: 0 100px }
2flex
.outer { display: flex }
.middle { flex:1 }
3table/grid
.outer { display: table; width: 100%; }
.left, .right, .middle { display: table-cell; }

.outer { display: grid; grid-template-columns: 100px auto 100px; }
4calc
.left, .right, .middle { float: left }
.middle { width: calc(100% - 200px) }

- 第一遍 -
float 会导致 right 换行在第二行 - 要调整 左中右 的DOM布局(BFC也不行了)
1position
.left, .right { position: absolute; top: 0; }
.right { right: 0 }
.middle { margin: 0 200px }
2flex
.outer { display: flex; justify-content: space-between; } // ⭐️ 横向布局可不写
.middle { flex: 1 }
3table/grid
.outer { display: table; width: 100%; }
.left, .right, .middle { display: table-cell }

.outer { display: grid; grid-template-columns: 200px auto 200px; }
4calc
.left, .right { float: left } // ❌ 1、三浮动,否则 right 就会因为向左靠齐换行了
.middle { width: calc(100% - 400px) }
// 2019.10 两栏,左边固定,右边自适应的布局 .left{ width: 100px }
- 第三遍 2025.1.24 -
1 .left { float: left; }
.right { margin-left: 100px; }
2 .left { position: absolute; }
.right { margin-left: 100px; }
3 BFC .left { float: left; }
.right { overflow: hidden; }
4 flex .outer { display: flex; }
.right { flex: 1; } // 1 1 0
5 table .outer { display: table; width: 100%; }
.left, .right { display:table-cell; }
grid .outer { display: grid; grid-template-columns: 100px auto; }
6 cacl .left, .right { float: left }
.right { width: calc(100% - 100px) }

- 第二遍 2025.1.19 -
1margin-left - float
.left { float: left }
.right { margin-left: 100px }
2margin-left - position
.left {  position: absolute }
.right { margin-left: 100px }
3BFC
.left { float: left }
.right { overflow: hidden }
4flex
.outer { display: flex }
.right { flex: 1 }
或者
.left { flex: 0 0 100px }
5table/grid
.outer {
    display: table;
    width: 100%;
}
.left, .right {
    display: table-cell;
}

.outer {
    display: grid;
    grid-template-column: 100px auto; ❌❌ // 1、grid-template-columns
}
6calc
.left, .right { float: left }
.right { width: cacl(100% - 100px) }

- 第一遍 -
1margin-left float
.left { float: left }
.right { margin-left: 200px }
2margin-left position
.left { position: absolute }
.right { margin-left: 200px }
3BFC
.left { float: left }
.right { overflow: hidden }
4flex
.outer { display: flex }
// .left { flex: 0 0 200px }
 // 1、父盒子变成弹性盒子后, right 要变成可拉伸 .right { flex: 1 }
5table/grid
.outer { display: table; width: 100%; }
.left, .right { display: table-cell }

.outer { display: grid; grid-template-columns: 200px auto; }
6calc
.left, .right { float: left }
.right { width: calc(100% - 200px) }
// 2020.07 对象分类、Promise按序执行、实现map
- 第三遍 2025.1.24 -
let list = [{name:'Alice', age: 32}, name:{'qiu', 4}]
function classify(arr, property){
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]) {
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre;
    }) ⭐️ // 少写了 reduce 的初始值 - 一个空对象 {}
}
function promise(arr, init) {
    return arr.reduce((pre, cur) => pre.then(cur), Promise.resolve(init));
}
Array.prototype.fakeMap = function(cb, thisArg) {
    return this.reduce((pre, cur, index, array) => {
        pre[index] = cb.call(thisArg, cur, index, array);
        return pre;
    }, [])
}

- 第二遍 2025.1.19 -
const arr = [{age:1, name:'a'}, {age:2}]
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]) {
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre;
    }, {})
}
function executor(arr, init) {
	return arr.reduce((pre, cur) => pre.then(cur) , Promise.resolve(init))
}
Array.prototype.fakeMap = function(cb) {
    ❌❌ // 1、map 函数接收两个参数,第一个是 callback,第二个是 thisArg - 用来指定 this
    const arr = this;
    return arr.reduce((pre, cur, index, array) => {
        const res = cb(cur, index, array); ❌❌ // cb.call(thisArg, cur, index, array);
        pre.push(res);
        ⭐️ // reduce filter map forEach 会跳过 empty 的数组项( map 和 reduce 都跳过)
        return pre;
    }, [])
}

- 第一遍 -
const arr = [{name:1, age:2}, {name:1, sex:0}]
function classify(arr, property) {
    return arr.reduce((pre, cur) => {
        const key = cur[property];
        if(!pre[key]){
            pre[key] = [];
        }
        pre[key].push(cur);
        return pre;
    },{})
}
function order(arr, init) {
    return arr.reduce((pre, cur) => pre.then(cur), Promise.resolve(init))
}
Array.prototype.fakeMap = function(cb) {
    const arr = this; // ⭐️ 可以直接使用 this
    return arr.reduce((pre, cur, index, array) => {
        const res = cb(cur, index, array);  // 1、注意 map 除了 callback,第二个参数是可选的的 thisArg,用于指定前面函数的 this
        pre[index] = res; // ⭐️ 可以直接赋值
        return pre;
    }, [])
}
/**
	Array.prototype.fakeMap = funciton (cb, thisArg) {
		return this.reduce((pre, cur, index, array) => {
			pre[index] = cb.call(thisArg, cur, index, array);
			return pre;
		}, [])
	}
*/
// 2022.08 包管理工具
- 第四遍 2025.1.24 -
npm 将依赖拍平提升到node_modulesnpm>5 package-lock.json,.npmrc
yarn v1/Classic 将依赖拍平提升到node_modules针对早期npm版本优化并行加载多个依赖yarn.lock,.yarnrc
pnpm 内容寻址存储依赖只存储一次结构化嵌套存储在node_modulespnpm-lock.yml,.npmrc
yarn v2/Berry 即插即用存储在 .pnp.cjs+.yarn/cache(zip)yarn.lock,yarnrc.yml ⭐️ //不再使用 node_modules

- 第三遍 2025.1.19 -
npm:拍平提升至 node_modulespackage-lock.json,.npmrc
yarn v1/Classic:拍平提升至 node_modules针对早期 npm 问题并行加载多个依赖yarn.lock,.yarnrc
pnpm:保留结构化的 node_modules内容依赖存储只物理存储一次pnpm-lock.yml,.npmrc ⭐️ // 内容寻址存储
yarn v2/Berry:不再使用 node_modules依赖查找表 .pnp.cjs+.yarn/cache(zip)yarn.lock,.yarnrc.yml ⭐️ // 老是想不到:即插即用

- 第二遍 -
npm:将安装包拍平提升到node_modulespackage-lock.json(npm>5),.npmrc
yarn v1/Classic:将安装包拍平提升到node_modules相对于npm早期版本提供并行下载,yarn.lock,.yarnrc
pnpm:内存寻址存储结构化存储 node_modules只物理存储一次节省安装空间,pnpm-lock.yml,.npmrc
yarn v2/Berry:不再有 node_modules依赖查找表.pnp.cjs + .yarn/cache zip存储依赖,yarn-lock.yml,.yarnrc ❌❌ // 1、即插即用,且lock文件和配置文件分别为 yarn.lock + .yarnrc.yml

- 第一遍 -
npm
	将依赖提升拍平存储在node_modules7.x有针对peer dependencies的break changepackage-lock.json(npm>5) + .npmrc
yarn v1/classic
	将依赖提升拍平存储在node_modules;针对早期npm优化可以并行加载多个依赖yarn.lock + .yarnrc
pnpm:❌ // 「不是pnpm.yml,而是 pnpm-lock.yml」
	保留结构化的node_modules采用内容寻址存储只物理存储一次节省空间pnpm.yml + .npmrc
yarn v2/Berry // ⭐️「即插即用」
	不再使用node_modules使用依赖查找表 .pnp.cjs  /.yarn/cache 存储 zip文件yarn.lock + .yarnrc.yml
// 2024.10 怎么实现跨域
- 第三遍 2025.1.24 -
1jsonp利用 script src 可以引入第三方资源只能处理 GET 请求
2CORS推荐跨域资源共享 - Access-Control-Allow-Origin 设置可以访问资源的域名
3代理 nginx 反向代理 - 同源协议存在于浏览器端服务器端不存在
4websocket 不受同源协议限制

- 第二遍 -
jsonpGET 请求是传输callbak-服务端将数据包裹成函数-拿到返回信息触发callback 函数
nginx同源协议限制是 浏览器侧服务器端不受限
CORS跨域资源共享 Access-Control-Allow-Origin 服务端允许访问资源的域名
wesocket不受同源协议限制

- 第一遍 -
1JSONP只能处理 GET 类型的请求利用 script 标签的 src 可以引入第三方资源
在客户端强求中指定 callback在服务端将数据包裹成函数返回给客户端客户端执行 callback 函数
2代理 nginx同源协议限制只在客户端服务端之间不存在
3CORS 设置 Access-Control-Allow-Origin 可以信任的第三方资源 // ❌ 1、那些域名可以访问资源 - 是为了让浏览器知道哪些域名可以跨域资源共享,而不是从客户端角度描述
4websocket 不受同源协议的限制
// 2024.11 观察者模式 EventEmitter
- 第四遍 2025.1.24 -
class EventEmitter {
    constructor() {
        this._max = 10;
        this.events = {};
    }
    add(event, listener) {
        if(!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener)
        if(this.events[event].length > this._max) {
            throw Error('max exceed');
        }
    }
    remove(event, listener){
        if(Array.isArray(this.events[event])) {
            if(!listener) {
                delete this.events[event];
            } else {
                const index = this.events[event].indexOf(listener);
                if(index>-1) {
                    this.events[event].splice(index, 1);
                }
            }
        }
    }
    once(event, listener) {
        const onceFun = (...arg) => {
            listener(...arg);
            this.remove(event, onceFun);
        }
        this.add(event, onceFun);
    }
    emit(event){
        if(Array.isArray(this.events[event])){
            const args = arguments.slice(1);
            const static = [...this.events[event]];
            static.forEach((it, index) => {
                const arg = args[index];
                Array.isArray(arg) ? it(...arg) : it(arg);
            })
        }
    }
}

- 第三遍 2025.1.19 -
add once remove emit
class EventEmitter {
    remove(event, listener) {
        if(Array.isArray(this.events[event])) {
            if(!listener) {
                delete this.events[event]
            } else {
                const index = this.events[event].indexOf(listener);
                if(index > -1) {
                    this.events[event].splice(index, 1);
                }
            }
        }
    }
    emit(event) {
        if(Array.isArray(this.events[event])) {
            let static = [...this.events[event]];
            const args = arguments.slice(1);
            static.forEach((it, index) => {
                const arg = args[index];
                Array.isArray(arg) ? it(...arg) : it(arg);
            })
        }
    }
}

- 第二遍 -
class EventEmitter {
    constructor() {
        this.events = {};
        this._max = 10;
    }
    add(event, listener) {
        if(!this.events[event]){
            this.events[event] = [];
        }
        !this.events[event].push(listener);
        if(this.events[event].length >= this._max) {
            throw Error('max exceed');
        }
    }
    remove(event, listener) {
        if(Array.isArray(this.events[event])) {
            if(!listener) {
                delete this.events[event];
            } esle {
                const index = this.events[event].indexOf(listener);
                ❌❌ // 1、这里要判断 index 存在才去删除:if(index>-1){ ... }
                this.events[event].splice(index, 1);
            }
        }
    }
    once(event, listener) {
        const onceFun = (...arg) => {
            listener(...args);
            this.remove(event, onceFun);
        }
        this.add(event, onceFun)
    }
    emit(event) {
        const args = arguments.slice(1);
        const list = this.events[event];
        ❌❌ // 2、不能直接赋值,要拷贝下来,才能避免 const list = [...this.events[event]];
        list.forEach((it, index) => {
            const arg = args[index];
            Array.isArray(arg) ? it(...arg) : it(arg);
        })
    }
}

- 第一遍 -
add remove emit once setMax
class EventEmitter() {
    constructor() {
        this.events = {};
        this._max = 10;
    }
    setMax(limit) {
        this._max = limit;
    }
    add(event, listener) {
        if(!this.events[event]) {
            this.events[event] = [];
        }
          // 4、少了计算监听事件数量 if(this.events[event].length > this._max) { throw Error('exceed') }
        this.events[event].push(listener);
    }
    remove(event, listener) {
        if(!listener) {
            delete this.events[event];
            return;
        }
        if(Array.isArray(this.events[event])) {
            let index = this.events[event].indexOf(listener);
            this.events[event].splice(index, 1);
             // 1、 这里要判断 index > -1,也就是 listener 存在才删除
        }
    }
    once(event, listener) {
        const onceFunc = (...args) => {
            listener(...args);
            this.remove(event, onceFunc);
        }
        this.add(event, onceFunc);
    }
    emit(event) {
        const argsList = arguments.slice(1);
        if(this.events[event]) {
             // 2、这里一定要注意,由于 once 的存在,this.events[event] 在执行过程是动态改变的
             // 所以这里一定要将 this.events[event] 拷贝下来再处理 let static = [...this.events[event]]
            this.events[event].forEach((it, index) => {
                const args = argsList[index];
                Array.isArray(args) ? it(...args) : it();
                  // 3、args 还是要传进函数的 Array.isArray(args) ? it(...args) : it(args)
            })
        } 
    }
}
// 作业68:React hooks 的原理是什么?useEffect useState模拟实现
- 第五遍 2025.1.23 -
hooks 目的是不写类组件也能够使用 state 和状态管理模拟生命周期能力
原理是利用 闭包 实现 state 每次重新渲染都能够访问到最新值同时每个 fiberNode 都存储对应的所有 hooks 链表每次渲染全部执行保证使用最新值渲染 commit 挂载到真实 DOM 后执行 hooks 收集到的副作用模拟生命周期
function render() { 
    ReactDOM.render(<App />, document.getElementById('root'));  
    effectCursor = 0;
}
let memorizedState = {};
let effectCursor = 0;
function fakeUseEffect(cb, array) {
    const lastArray = memorizedState[effectCursor];
    const hasChanged = lastArray && array.some((it, index) => it !== lastArray[index]);
    if(!lastArray || hasChanged){
        cb();
        memorizedState[effectCursor] = array;
    }
    effectCursor++;
}
let cursor = 0;
function fakeUseState(init) {
	let currentCursor = cursor;
    memorizedState[currentCursor] = memorizedState[currentCursor] ?? init;
    const useValue = (newVal) => {
        memorizedState[currentCursor] = newVal;
        render();
    }
    cursor++;
    return [memorizedState[currentCursor], useValue];
}

- 第四遍 2025.1.18 -
hooks 设计的目的不使用类组件也可以使用 state状态管理模拟生命周期
原理闭包-每次渲染都拿到最新值hooks按序存储在 fiberNode.memorizedState更新时按序执行全部 hooks使用副作用链表收集副作用 commit 阶段挂载后执行副作用链表

const memorizedState = {};
let cursor = 0;
function fakeUseEffect = (cb, array) => {
    const lastArr = memorizedState[cursor];
    const hasChanged = lastArr && lastArr.some((it, index) => array[index] !== it);
    if(!lastArr || hasChanged) {
        memorizedState[cursor] = array;
        cb();
    }
    cursor++;
}
function render() {
    ReactDOM.createElement(<App/>);
	❌❌❌❌ // 1、ReactDOM.render(<App />, document.getElementById('root'));
	cursor = 0;
}

function fakeUseState = (init) => {
    const current = cursor;
    memorizedState[current] = memorizedState[current] ?? init;
    const setValue = (newValue) => {
        memorizedState[current] = newValue;
        render();
    }
    return [memorizedState[current], setValue];
    ❌❌❌❌ // 2、cursor++; 忘记写游标自增了
}

- 第三遍 2025.1.13 -
react 的每个组件都会将 hooks 链表存储在 fiberNode.memorizedState
组件有更新时会按需全部执行全部 hooks保证每次都可以获得最新的属性和状态
❌❌❌ // 1、这里描述的是 hooks 的执行过程 - 大虐,第一遍/第二遍的时候都记得是用于给函数组件添加功能用的,这一次反而完全没想起来
❌❌❌/** 记忆 React hooks原理
	先描述【目的】:16.8的新增功能,可以在不编写类组件的情况下「使用 state」及其他特性,「状态管理能力」、「生命周期替代能力」、逻辑复用能力
	再描述【实现上述目的的方法】,也就是【原理】:怎么用来「使用 state + 状态管理能力」 - ①主要是利用「闭包」,组件每次渲染时,使用闭包保持其状态;怎么用来「生命周期替代」 - ②使用链表保存一系列hooks,存储在 fiberNode 上,「在 Fiber 树更新时,能从 hook 中计算出最终输出的状态和需要执行的相关副作用」
*/

// useEffect(() => {}, []);
⭐️ /** 注意其实要写下 render 函数,因为 useEffect 触发 render 后,对应的游标要置0
	function render(){
		ReactDOM.render(<App />, document.getElementById('root'));
		count = 0;
	}
*/
const memorized = {};
let count = 0;
function fakeUseEffect(callback, array) {
    const lastArray = memorized[count];
    const hasChanged = lastArray && array.some((it, index) => it === lastArray[index]);❌❌❌ // 2、这里应该是判断「不等于」,有不相等的,代表依赖数组变化,需要更新:!Object.is(it, lastArray[index])
    if(!lastArray || hasChanged) {
        callback();
        ❌❌❌ // 3、少写了「更新依赖数组」的逻辑:memorized[count] = array;
    }
    count ++;
}
// const [a, useA] = useState(init);
const memorized = {};
let count = 0;
function fakeUseState(init) {
    const currentCount = count;
    let value = memorized[currentCount] ?? init;
    ❌❌❌ // 4、使用 value 会导致返回后,value 不会自动更新,和 memorized[currentCount] 失去了联系,本行改为:memorized[currentCount] = memorized[currentCount] ?? init;
    const setValue = (newValue) => {
        memorized[currentCount] = newValue;
        ❌❌❌ // 5、调用 useValue 必须触发 render:render();
    }
    count++;
    return [value, setValue]
    ❌❌❌ // 4、延续问题4,本行修改为:return [memorized[currentCount], setValue]
}

- 第二遍 -
Hooks的实现目的是为了实现和类组件一样拥有对组建流程进行自定义的能力
❌❌ // 1、和第一遍的逻辑是一样的,都是想到了「函数组件」缺失「类组件的生命周期」
// 但是 hooks 不只是「生命周期替代能力」,还有「状态管理能力」、「逻辑复用能力」
// 函数组件也不止缺失「生命周期」,还包括 state(没有 hooks 的函数组件主要接收 props 来展示 UI)
在首次渲染时react会为每个组件收集所有的hooks并以链表形式存储在每个节点的memorizestate当中当组件被更新react会重新调用渲染函数所有的hooks会按标记需要重新渲染的执行一遍
Hooks是和副作用链表哪个是被标记要重新渲染然后重新渲染执行的呀我记得hooks是在首次渲染的时候或者是后续更新的时候都会被全部按序执行一遍的而副作用收集的链表是在commit渲染把dom更新到真实dom之后执行副作用链表的
❌❌ // 2、质疑是对的: hooks 在组件更新时是「全部按序重新执行一遍」,副作用链表是「在 hooks 执行过程中,如果 useEffect 依赖有更新」,就更新到副作用链表
// 和第一遍一样想到链表:第一遍想到存储位置,第二遍描述收集、执行过程 - 而不是原理
// 原理是从「链表+闭包」描述的:利用闭包保存状态,使用链表保存一系列的 hooks 与 Fiber关联。在 Fiber 更新时,就能从 hooks 中计算出最终输出的状态 和执行相关的副作用。

// ⭐️ 1、function render(){ 
// ReactDOM.render(<App />, document.getElementById('root'));
// cursor = 0; // 这里注意在 render 函数中,将 effect 的游标置 0
//}
const memorizedState = {};
const cursor = 0; ❌❌ // 3、这里应该是用 let
function fakeUseEffect(cb, depsArray) {
    const lastDeps = memorizedState[cursor];
    const hasChange = lastDeps && depsArray.some((it, index) => !Object.is(it, lastDeps[index]));
    if(!lastDeps || hasChange) {
        cb();
        memorizedState[cursor] = depsArray;
    }
    cursor++;
}

const memorizedState = {}; // ⭐️ 核心是链表,但是这里选择使用 [] 实现
const cursor = 0; ❌❌ // 4、这里应该是用 let
function fakeUseState(init){
    const value = memorizedState[cursor] ?? init;
    ❌❌ // 5、cursor 应该使用其他变量保存,和下文中 setValue 形成了闭包 - 在异步情况下,cursor 可能会变化,导致索引错误 const currentCursor = cursor; 这里使用 currentCursor 进行索引
    const setValue = function (newValue) {
        memorizedState[cursor] = newValue;
        ❌❌ // 6、更新使用闭包保存的 memorizedState[currentCursor] = newValue;
        ❌❌ // 7、这里还要触发一次 render() 函数执行
    }
    return [value, setValue]
}

- 第一遍 -
之前渲染流程类组件中是使用生命周期管理的函数式组件也通过 hooks 实现了管理
 // 1、描述得不准确:hooks 可以在不编写类组件的情况下使用 state 及React其他特性
 render 阶段组件执行时会通过链表形式存储 hooks - FiberNode.memorizeState 存储第一个链表
 // 2、hooks 链表说得不错,链表是为了在更新时,以稳定的顺序执行,输出最终的状态和相关副作用
 // 3、还有一个 hooks 利用闭包保存状态,在组件每次渲染时通过闭包保持其状态
触发更新/渲染时会执行
⭐️ // 注意 hooks:1、不要在循环、条件、嵌套、try/catch/finally 中使用 - 无法保证顺序的稳定
⭐️ // 2、只有在 函数组件和hooks 中能够调用 hooks
let states = [];
let cursor = 0;
function render() { ReactDOM.render(<App />, document.getElementById('root')) }
function useFakeState(init) {
     // 5、当前定义时的 cursor 需要被闭包保存 let currentCursor = cursor;
    const states[cursor] = states[cursor] ?? init;
    const setCurrentState = (new) => {
        states[cursor] = new;
        render();
    }
     // 4、少处理了游标自增: cursor++;
    return [states[cursor], setCurrentState];
}
let states = []; ⭐️ // 这里使用 allDeps 声明或许更合适
let cursor = 0;
function render() {
    ReactDOM.render(<App />, document.getElementById('root'));
    cursor = 0;
}
function fakeUseEffect(cb, depsArray) {
    const lastDeps = states(cursor);
    const hasChanged = lastDeps && depsArray.some((it, index) => it !== lastDeps[index]);
    ⭐️ // 这里的 some 回调可以使用 !Object.is(it, lastDeps[index])
    if(!lastDeps || hasChanged) {
        cb();
        states[cursor] = depsArray;
    }
    cursor ++;
}
// 作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明
- 第五遍 2025.1.23 -
1JIT  JSX 编译成 React createElement
2渲染 render生成 fiber树 + fiberNode.memorized 存储 hooks + 收集副作用链表 fiberNode.updateQueue + diff 计算UI最小更新量
3commit将变更应用到真实 DOM + 执行生命周期 + 副作用链表

渲染前 shouldComponentUpdate
渲染 constructor render getDerivedStaeFromProps
commit  componentDidMount-初次渲染componentDidUpdate-更新挂载后componentWillUNMount-卸载前useLayoutEffect-同步执行阻塞重绘

useEffectcomponentDidMount-没有依赖值componentDidUpdate-依赖值componentWillUNMount-返回一个函数作为清理函数

render 时收集到 hooks 链表将副作用收集到 fiberNode.updateQueue
count 值更新找到对应节点修改值标记重新渲染 fiberNode.subTreeFlags
重新渲染执行全部 hooks根据依赖是否变化收集副作用变化
 VDOM 更新到真实 DOM有副作用执行副作用

- 第四遍 2025.1.18 -
JIT 运行时编译成 React createElement 方法
	- 渲染前 shouldComponentUpdate
render fiber树 + hooks按序全部执行 + 副作用链表收集 + Diff
// ⭐️ FiberRootNode HostRootFiber beginWork-completeWork wip.tag
commit应用到真实 DOM + 执行生命周期+副作用链表
	- 挂载后 componentDidMountcomponentDidUpdate // ⭐️ useLayoutEffect

 render 阶段执行 useState 按序将 count 值存储
更新 count 触发重新渲染组件渲染重新执行所有 hooks标记 fiberNode.subTreeFlag
判断是否有相关的 useEffect 副作用有的话收集到副作用链表 fiberNode.updateQueue
生成新的 VDOM Diff 比较最小更新 UI应用到真实 UI执行副作用

- 第三遍 -
❌❌❌ // 0、渲染之前,React 已经通过 JIT 将 JSX 描述的 UI 编译为 React createElement方法
render阶段fiber树 + hooks链表执行 + 收集副作用链表 + Diff
	react 首次渲染或者每次更新时都会遍历生成整棵 Fiber 先创建 FiberRootNode再创建 FiberHostNode 作为根节点遍历AST逐个生成 fiberNode),同时在每个 fiberNode.memorizedState 中存储 hooks 链表;❌❌❌ // 1、FiberRootNode 才是「根节点」,「HostRootFiber」只是新的 fiber 树的遍历起始点,也就是 FiberRootNode.current
❌❌❌ // 2、「遍历AST逐个生成 fiberNode」可以更详细得表述为「遍历执行 beginWork,根据不同的 wip.tag 进行不同标签的处理,执行到叶子节点 completeWork,生成 WIP Tree」
    在状态更新时遍历执行所有 hooks;❌❌❌ // 3、render 阶段也是有生命周期在执行的,例如每次渲染执行前的 shouldComponentUpdate 
    ❌❌❌ // 4、遍历执行 hooks 时,如果遇到有副作用的节点,除了被副作用链表收集,react 也会在节点 Node 上标记 fiberNode.subtreeFlags
    同时 React 创建一个 side-effects-list 用于存储副作用链表的收集;⭐️ // fiberNode.updateQueue
    将新的 fiber树和当前UI对应的 fiber 进行 Diff 比较得到最小UI更新
commit阶段应用到真实DOM + 执行副作用链表 + 执行生命周期 ⭐️ // 生命周期是类组件的概念
	将Diff计算出的最小 UI 更新到真实DOM
    然后执行副作用链表 + 
    生命周期 componentDidMount/componentDidUpdate/componentDidUnMount 中的操作 ⭐️ // useLayoutEffect、ref引用也在这个时机
    执行完成后 FiberRootNode.current 切换指向新的 fiber树
    
const [count, setCount]=useState(0)
在首次渲染的时候 hooks 存储在当前 fiberNode  hooks 链表中
当调用 setCount 触发更新fiber 定位到当前 hooks 标记更新 ❌❌❌ // 1、「触发更新」其实应该描述成「更新 count 状态对应的节点」
触发整棵 fiber 树重新创建更新当前组件触发组件的 hooks 链表被全部重新执行如果有副作用则被收集
得到最新的准确的状态和属性用于渲染
❌❌❌ // 2、这里应该将「如果有副作用」改写为「要注意是否有 useEffect 的作用:根据依赖数组是否变化确定是否要更新」
生成新的 fiber 树和当前UI对应的 fiber  Diff 计算得到最小更新量应用到真实 DOM
执行相关副作用渲染完成切换 current 指向

- 第二遍 -
❌❌ // 3、在 render 之前,运行时就会执行 JIT 先将 JSX 描述的 UI 编译为 React createElement 方法
1.render 创建一颗完整的Fibernode的虚拟树 + diff
❌❌ // 1、中间少写了两个关键步骤:存储 hooks + 标记、收集副作用链表
-创建FiberRootnode然后再创建hostfibernode作为起点深度遍历按照标签的所属类型转换成对应的fibernode
❌❌ // 2、创建 FiberRootNode 后,创建 fiber 作为 HostRootFiber,也就是 FiberRootNode.current
-核心就是通过react.createelement创建React element类型
❌❌ // 4、以单个组件为例,fiberNode 存储 hooks,按序执行时标记、收集副作用链表
-diff 计算最小更新量
2.commit 将计算出来的最小更新量UI更新到真实dom上 + 执行相关生命周期和副作用列表hooks不在这边执行hooks不作为单独的流程执行。)
❌❌ // 5、更新时会重新调用渲染函数,按序执行所有 hooks,所以也算是流程的一部分
3.将fiberrootnode.current指向新的fiberNode的树

- 第一遍 -
 // 1、「componentDidMount、componentDidUpdate」 都在「commit 阶段-修改更新到真实 DOM 后」执行,还有少写了:「componentWillUnMount、useLayoutEffect、ref引用」也在这个时机是执行
JSX 描述 UI
1rendercomponentDidMount
 // 2、还有几个生命周期在「render 阶段」执行:「shouldComponentUpdate + 所有状态相关的 hooks(例如 useState、useMemo、useCallback等)」
 // 4、 需要描述下 render 的「主要任务」 - 可以作为记忆的纲要:①构建新的Fiber树②存储 hooks③副作用的收集和标记④Diff
	 创建 FiberHostNode 作为实例
 // 5、先创建「FiberRootNode 作为根节点」,再创建「HostRootFiber 作为起始点」,也就是根节点存储的当前树 WIP tree - FiberRootNode.current
     DFS 遍历类组件-执行 render 函数/函数组件 - 编译成 FiberNode tree [hooks链表的第一个存储在 memoriesState]
 // 6、没有在这里执行 render 函数/函数组件,它们俩的执行时机是渲染之前-它们的执行就是重新渲染的时机
 // 7、「生成 WIP tree 的过程」其实是:执行「beginWork」-通过wip.tag来不同类型元素生成 fiberNode;遍历到叶子节点的时候,执行「completeWork」构建出 tree + flags 冒泡
 // 8、在此期间, 为「每个FiberNode,存储 hooks 链表」(memorizedSate) ,而不是整颗 tree
2pre-commitcomponentDidUpdate
 // 3、没有 pre-commit 阶段
	 执行过程中标记 flag 收集副作用链表 side-effects-list/EffectFalgs
 // 9、对,但是「标记 flag 收集副作用链表」是两件事,一个是对有副作用需要「在更新阶段执行」的节点进行标记 fiberNode.subtreeFlags,另一个是在 fiberNode.updateQueue「构建 side-effects-list 用于收集副作用链表」
	 执行完副作用列表后得到 WIP tree FiberHostNode.current 切换到 WIP tree
 // 10、不是在这里执行的副作用列表(在commit更新DOM后执行),也不是在这里切换指针(commit全部完成后)
 // 12、render 阶段的最后,进行「Diff 比较」
3commit-Diff
 // 11、Diff 在 render 阶段
 // 17、描述 commit 阶段的主要任务:「将更新应用到真实DOM,同步执行,不可中断 + 执行副作用、类组件生命周期」
 // 18、这个 commit 阶段涉及的生命周期 如 第1条 错误所示
	  current tree(和真实DOM对应的)  WIP 树进行对比将计算后的 更新 应用到真实 DOM
 // 19、这里前面描述的是 Diff,应用到真实 DOM 之后,还有两个步骤:①「执行生命周期」 ②「commit 工作都完成后,切换 FiberRootNode.current 指向 WIP 树」
Diff 算法 - 增量式更新
 // 13、描述不对,Diff 算法主要是「找出最小更新量」来更新真实 DOM
	1分层遍历对比标签是否一致一致则复用标签修改属性不一致则标记重新渲染删除重建
     // 14、少描述了:「先对比根节点,再遍历子节点」,之后才是具体的「同层比较」
    2生成 FiberNode 描述的增量修改 UI可以根据优先级调度和时间分片逐步渲染提高 diff 效率
     // 15、这里的「增量式」描述是 fiber 架构在 commit 阶段做的事情
     // 16、提高 diff 效率 - 实际上对应的 Diff 操作是「对于列表组件,使用 key 从新老树中匹配对应节点比较」
// 作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期
- 第五遍 2025.1.23 -
computed依赖值不变化计算的属性则使用缓存不重新计算
watch监听的对象改变一次重新计算一次
methods每次重新渲染都会重新执行

父子组件挂载顺序 beforeCreate-created-beforeMount ->  beforeCreate-created-beforeMount-mounted ->  mounted
父子组件卸载 beforeDestroy ->  beforeDestroy-destroyed  ->  destroyed

data 不为函数时组件被多次实例化后每个实例会共享 data 作用域
为函数时每个实例都有独立的作用域

beforeCreate 初始化data methods 都没有
created 创建data methods watch computed 都执行了
beforeMount 挂载前已有 VDOM
mounted 挂载到真实 DOM
beforeUpdate 更新前已有 VDOM
updated 挂载到真实 DOM
beforeDestroy 卸载前清理定时器监听函数等避免内存泄漏
destroyed 卸载

- 第四遍 2025.1.18 -
computed - 依赖的值不变化直接使用缓存值不重新计算
watch - 监听属性变化一次执行一次
methods - 组件每次渲染都执行

父子组件挂载顺序父beforeCreate created beforeMount -> 子beforeCreate created beforeMount mounted -> 父mounted
卸载父beforeDestroy  -> 子beforeDestroy destroyed  -> 父destroyed

data 不使用函数每次组件实例化后引用的是一个地址的data
使用函数后每个组件都有各自的作用域

beforeCreate没有初始化
createddata methods watch computed 初始化
beforeMount挂载前已生成 VDOM
mounted挂载后可以访问DOM
beforeUpdate更新前已生成 VDOM
updated更新后
beforeDestroy卸载前清理避免内存泄漏
destroyed

- 第三遍 -
1computed有缓存只要依赖的变量不变就不会触发重新计算
watch监听修改一次触发一次重新计算
methods每次组件重新渲染都会触发重新执行
2父组件 beforecreate>父组件created>父组件beforemounte>子组件 beforecreate 子组件created 子组件beforemount 子组件mounted>父组件mounted
卸载父组件beforeunmounte>子组件beforeunmounte>子组件unmounted>父组件unmounted
❌❌❌ // 1、大虐,卸载是 beforeDestroy 和 destroyed
3vue中的组件会被复用多次调用如果data不用函数返回会导致每个组件实例都共享 data对象采用函数形式后可以保证每个组件都有独立的作用域
4beforecreate 什么都没有加载data和methods都没有
created 加载了data和methodscomputedwatch但是还没有DOM
beforemount 挂载之前此时已经生成了完整的虚拟DOM树
mounted 挂载后此时可以访问 DOM
beforeupdate 修改前还没应用到真实DOM ⭐️ // 此时虚拟DOM已经重新构建完成 
updated修改后此时修改已经更新到DOM
beforeunmounte还未卸载
unmounted 已卸载
❌❌❌ // 2、大虐,和第一个一样的错误,名称错啦! beforeDestroy+destroyed,一般在 beforeDestroy 中清理一些 - 定时器、订阅时间、监听事件等,防止内存溢出

- 第二遍 -
1Compute需要返回值进行缓存如果值不变的话就不会重新计算
❌❌ // 1、咦,和上次错误的逻辑一样,不是「存储的值」是否变化,是「依赖的值」是否变化
Watch是监听的值变化一次则触发一次回调函数
Methods当中的函数每次更新组件渲染一次执行一次
2Created的时候页面当中的dom还没有挂载在其中不可以操作dom组件但是data methodscomputer和watch都已经处理了
Mounted的时候已经挂载了这个时候是可以操作Dom节点的
3父组件before created父组件created子组件before created子组件created子组件beforemount子组件mount父组件before mount父组件mounted
❌❌ // 2、和上次错得一样 - 因为上次答案就是错的 😅,这就纠正了,下次不许再错了:子组件在父组件执行 父 beforeMounte 后触发
 卸载组件也是父组件before destroy子组件before destroy子组件destroyed的父组件destroyed
4 如果不返回函数式的结构的话每一个组件被实例化后所有的实例访问的data都是一个引用对象通过函数形式返回后每一个被实例化的组件都拥有自己的data作用域

- 第一遍 -
 computed 有缓存值不变就不会重新计算触发更新
 // 1、这里其实对 computed 缓存的内容理解有误差,不是「值」,是「计算属性依赖的值」,同时 computed 必须return 返回
watch 主要是监听一个值变化一次就执行一次  // 2、执行回调
methods 方法在初始化时定义后续渲染时被调用  // 3、每次页面发生变化,都会被调用

 created  data+methods+computed+watch 已经定义存在但是 DOM 还没有挂载到真实DOM
mounted时DOM已经更新到真实 DOM可以访问/操作DOM了

  beforeCreated -  created -  beforeCreated -  created -  beforeMounted -  mounted -  beforeMounted -  mounted
 // 子组件挂载完成后,父组件才会挂载 - 子组件在父组件执行 父 beforeMounte 后触发

 因为组件会被多次调用实例化函数保证了每个组件实例都可以拥有自己的 data 数据
⭐️ // 这里补充:每个组件实例都有自己的「作用域」,不会互相影响
// 作业64:Vue渲染怎么实现的?
- 第五遍 2025.1.23 -
1模版编译
	generate 词法分析语法分析得到 AST
    optimize 静态节点优化
    render 生成 render 函数
2数据双向绑定
	初始化时通过 Object.defineProperty(obj, property,{get,set})/proxy 劫持数据的 get set
    开始渲染创建组件 Watcherget访问数据将正在执行的 Watcher 收集到 Dep 
    赋值更新触发 set通知所有订阅的 watcher - dep.notify()
3render 生成 VDOM
4patchdiff 计算出 UI 最小更新量

- 第四遍 2025.1.18 -
1模版编译parse语法分析词法分析得到ASToptimize标记静态节点generate生成render函数
❌❌❌❌ // 1、generate-optimize-render 函数名称记错
2双向数据绑定
初始化时劫持数据属性Object.defineProperty(target, property, {get, set})/Proxy
实例化组件 Watcher触发get在Dep收集当前正在执行的 Watcher // ⭐️ 订阅时机
修改属性时触发set更新相关 Watcher - dep.notify()
3执行 render 函数生成虚拟 DOM
4Diff 比较新的虚拟DOM  映射真实DOM的DOM树得到最小更新 UI // ⭐️  patch

- 第三遍 -
1模板编译 ⭐️ // AOT 参与全程
generate通过词法解析语法解析将模板转换成AST
optimize遍历AST标记静态节点方便后续Diff时跳过
render将AST转换成render函数字符串
2将js代码进行双向绑定数据劫持和发布订阅过getter/proxy劫持进行自定义 ❌❌❌ // 1、getter 被劫持是结果,「劫持方式是 Object.defineProperty(target, property, {get:()=>{}, set:()=>{}})」/Proxy
  在Dep中存储正在进行中的Watcher确认下Dep的结构是不是以变量为key所有订阅的Watcher为值的格式
❌❌❌ // 2、「每一个响应式对象属性都会创建一个对应的 Dep 类的实例」 -> 我写的这里理解有误,同时数据劫持之后,触发订阅的时机是「渲染组件,实例化 Watcher 时,执行到 getter 触发对应的 Dep 收集正在执行的 Watcher」
  等调用setter/proxy赋值时通知在Dep中所有订阅的Watcher即执行 dep.notify()
3执行render函数渲染虚拟DOM
4patch使用Diff算法计算虚拟DOM和当前UI对应的虚拟DOM的最小更新量应用到真实DOM

- 第二遍 -
1.模板编译 将模板编译成render函数执行的步骤分别是 parse optimize render
❌❌ // 1、执行步骤的最后一步是 generate
// 三个步骤分别作用(AOT参与全程):① parse 词法解析+语法解析 template 得到 AST ② optimize 对AST静态内容优化 ③ generate 递归转换 AST 得到 render 函数

2.先通过AOT进行预编译标记静态节点拿到可编译文件后对代码进行语法分析词法分析转换为抽象语法树AST
❌❌ // 2、AOT 预编译功能是针对 template 的,不是针对 js 的(第一次做的时候知道,这次做的时候误以为预编译是针对 js 的 -> 其实通过 AOT 和 JIT 的区别就可以推断,AOT 既然依赖的是静态编译,就是需要像模版语法这种,方便标记静态节点 - js 那么动态肯定是不行🚫的)
进行双向数据绑定通过object.define property或者是proxy对数据进行getter和setter的重写   
渲染组件时也就是实例化watcher时执行getter触发收集相关的正在执行的watcher 到deps
更新组件时也就是触发数据的setter函数通知所有的订阅 deps.notify()
⭐️ // Dep 用于依赖收集,作为容器管理多个订阅者 Watcher
3.执行render函数生成vdom树
4.patch也就是diff算法通过双端比较算法计算出最小UI的更新量更新到真实 DOM

- 第一遍 -
1render
 // 1、整个渲染「不止是 render + patch」,应该分成4步,①html:先「模版编译,得到render函数」 ②js:「实例化组件,同时收集依赖」(也就是数据双向绑定-实现响应式数据) ③html+js:「执行渲染函数/更新,得到 VDOM」 ④Diff/patch:「计算最小更新,应用到真实 DOM」
主要任务生成新的 VDOM 
相关生命周期createdbeforeMounted
 // 2、Vue 的生命周期很难罗列,这里模版编译前,js 都没执行到,怎么会有生命周期
模版语法静态编译 AOT 优化标记静态节点
 // 3、这里就是第一步:「模版编译,生成render函数」
// 分三步:①parse - 递归解析 template(词法/语法分析),「转换成AST」抽象语法树
// ②optimize - 对AST静态内容优化,「标记静态节点」 ③generate - 递归「转换AST,创建render字符串」
DFS遍历发布订阅模式+双向数据绑定 -  Dep 收集相关的 Watcher更新时通知这些订阅的 Watcher
 // 4、这里说得很粗糙,还少了「实例化组件」来触发,第二步:「实例化组件+创建响应式数据」 - 也就是「数据双向绑定」,是通过「数据劫持」和「发布订阅」模式相结合实现的 - Object.defineProperty/Proxy 劫持重新定义 getter和setter
 // 5、没说 Dep 是怎么收集依赖的:在创建 Watcher 访问 getter 时,把当前正在计算的Watcher订阅到 Dep
 // 6、没说怎么触发更新:setter 被触发,通知 Dep 中的所有订阅者 - dep.notify()
 // 7、数据双向绑定也有缺点:Vue2使用的 Object.defineProperty 在递归处理 data 中的属性时,必须要指定具体属性,所以不在 data 中声明的对象无法拥有双向绑定 - Vue3 改为 Proxy 劫持
生成 VDOM
 // 8、这是第三步,没有说清楚怎么生成 VDOM:执行渲染函数(更新时重新计算/执行渲染函数),得到 VDOM
2patch
主要任务利用 Diff 算法计算出需要更新的UI + 将更新应用到真实DOM上
相关生命周期Mounted 
 // 9、这里 patch 后,VDOM 已经更新到真实 DOM,除了 mounted,updated 也会在更新后在此执行
Diff 算法两棵树每棵各双指针进行往复的节点比较找到最小更新量
 // 10、Diff 比较的是新旧两棵 VDOM树
// 作业67:React 怎么做的性能优化?
- 第四遍 2025.1.23 -
fiber 时间分片 + 优先级调度 ⭐️ // 少写了 Diff 增量式更新 + 双 VDOM 树
VDOM 渲染时复用策略
- eagerStatestate 如果是新值和旧值无关则直接渲染不查询旧值
- bailoutstate+props 不变时复用组件
	- shouldComponentUpdate(nextProps, nextState) 通过返回布尔值来自定义是否需要重新渲染
	- React.memo((props) => {<div>{props.text}</div>}) 通过浅比较 props,不变则直接复	- useMemo(() => c*2, [c]) 缓存一个值依赖不变则复用
	- useCallback(() => c, [c]) 缓存一个函数依赖不变则复用每次渲染函数都会重新创建

- 第三遍 2025.1.18 -
eagerState策略bailout策略Diff增量式更新时间分片+优先级调度
bailout
1可变不可变分开写
2自定义是否需要重新渲染shouldComponentUpdate(nextProps, nextState)
3props浅比较React.memo((props) => <div>{props.text}</div>)
4依赖不变则使用缓存
缓存值useMemo(() => value*2, [value]); 
缓存函数useCallback(() => { value }, [value]) ⭐️ // 每次渲染,函数都是重新生成的,用了这个就缓存住了

- 第二遍 2025.1.12 -
由于react使用的jsx比较灵活在编译时很难做预编译的优化所以react在运行时 JIT 做了一些优化
主要是两个策略 ⭐️ // 主要是避免组件重新渲染
1eagerState 策略如果当前更新的state和旧值无关react 会直接使用新值渲染而不是访问旧值后再修改
2bailout策略判断组件的 state和props相等即可复用节点跳过重新渲染
针对 bailout有好几个生命周期和hooks 方便命中
1在写代码的时候将可变的和不可变的分开写方便命中
2shouldComponentUpdatenextPropsnextState通过返回布尔值自定义是否重新渲染当前类组件
⭐️ // shouldComponentUpdate 是为了「类组件」的性能优化设计的生命周期
/**
	❌❌ 1、对于「函数组件」,React 提供了 React.memo 这个高阶函数来处理 -> 对组件的 props 进行浅比较,如果不变则跳过重新渲染
	const MyMemo = React.memo((props) => { return <div>{props.time}</div> })
*/
3useMemo 将组件缓存只要依赖数组不变就不进行重新渲染
4useCallback 将函数缓存只要依赖不变就不进行重新计算
⭐️ // const expensiveMemoValue = useMemo(() => { return count * 2 }, [count])
// const expensiveCallback = useCallback(() => { setCount(count + 1) }, [count])
// 这里的记忆方法:shouldComponentUpdate(类组件)/React.memo(函数组件) 都是用于通过比较前后 props;2个函数组件的性能 hook:useMemo/useCallback

- 第一遍 -
React 每次更新都是重新生成一棵完整的树更新颗粒度细
1运行时优化fiber 架构实现时间分片 + 优先级调度
 // 1、对,在运行时优化,但是这个「架构级别」的属于渲染层面的优化,性能方便还是两个优化策略
	Time Slice 原理每次渲染新的一帧前fiber 都会查询 shouldYield 判断是否要执行一个渲染单元
    用户交互产生的渲染任务优先级高于非视口下的页面渲染
2在是否复用组件时两个优化策略
 // 2、对,但是一般不叫做「是否复用组件」,而是「避免组件重新渲染」
	s 策略:?
     // 3、eagerState:在useState 更新过程中,如果新值不依赖于旧状态,则直接使用新状态来更新组件
	bailout 策略props+state不变就可以进入该策略
	 // 4、只需要组件的 props 不变,即可避免重新渲染[state可能会影响子组件的 props,还是要考虑的]
3日常一些写法也可以优化
 // 5、两个策略中,bailout 策略可以通过调用 API 来实现
	把动态的变量和静态数据拆分组件
     // 6、React.memo - 针对函数组件,对组件的 props 浅比较,不变则跳过重新渲染
    const MyComponent = React.memo((props) => {return <div>{props.text}</div>}) // props.text 的值/引用不变就不会重新渲染
     // 7、shouldComponentUpdate - 针对类组件,在组件更新前调用,自定义是否需要重新渲染
    class MyComponent extends React.Component {
        shouldComponentUpdate(nextProps, nextState) {
            return this.props.text !== nextProps.text; // 不同时,重新渲染
        }
        render() {return <div>{this.props.text}</div>}
    }
	 // 8、useMemo/useCallback - 依赖项不变,返回之前的缓存
    const expensiceValue = useMemo(() => { return count*2 }, [count])
    const handleClick = useCallback(() => {setCount(count + 1)}, [count])
 // 9、除了两个策略,还有 VDOM + Diff - 减少对真实 DOM 的操作,避免不必要的回流重绘
// 作业78:使一个标签看不见的几种样式及其区别
- 第二遍 2025.1.23 -
display: none; 不占位DOM 节点还在VDOM树中触发回流重绘
	- v-if 直接移除 DOM 节点
opacity: 0; 占位透明绑定的事件还是可以触发的
visibility: hidden; 占位看不见且绑定的事件无法触发只触发重绘 ⭐️ // v-show

- 第二遍 2025.1.19 -
display: none; 不占位隐藏 DOM ⭐️ // 触发回流+重绘
	- v-if 不占位且移除 DOM
opacity: 0; 依旧占位且能够被触发事件
visibility: hidden; 依旧占位但是不能够被触发事件 ⭐️ // 触发重绘(不影响结构)
	- v-show same

- 第一遍 -
display: none; 不占位直接移除 DOM 节点 v-if  // 1、元素仍在 DOM 中,会触发回流+重绘; v-if 移除了 DOM 节点
opacity: 0; 依旧占位但是透明看不见节点绑定的事件依旧可以触发 
visibility: hidden; 不占位DOM 节点还在但是看不见节点绑定的事件无法触发 v-show  // 2、依旧占位,不影响结构,会触发重绘
// 作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口)
- 第三遍 2025.1.22 -
<canvas id="drawing" width=200 height=200></canvas>
const drawing = document.getElementById('drawing');
2D:左上角 矩形
if(drawing.getContext()) {
    const context = drawing.getContext('2d');
    context.fillStyle/fillRect/strokeStyle/strokeRect/fillText/drawImage
    context.beginPath()/moveTo()/lineTo()/stroke()
}
3D:左下角
if(drawing.getContext()) {
    const context = drawing.getContext('webgl');
}
context.viewport(drawing.width/2, 0, drawing.width/2, drawing.height/2) // 右下角1/4

- 第二遍 2025.1.19 -
<canvas id='drawing' width=200 height=200></canvas>
const drawing = document.getElementById('drawing');
2D: 左上角矩形
if(drawing.getContext){
    const context = drawing.getContext('2d');
    context.fillStyle/fillRect/strokeStyle/strokeRect/fillText/drawImage
    context.beginPath()/lineTo/moveTo/stroke()
}
3D:左下角context.viewPoint() ❌❌ // 1、viewport
if(drawing.getContext){
    const context = drawing.getContext('webgl');
}

- 第一遍 -
2D:
const context = drawing.getContext('2d');
坐标原点左上角
唯一形状矩形
基本操作
context.fillStyle = '#333';
context.fillRect(10, 10, 20, 30); // 在坐标(10, 10)画一个 #333 的长20,高30 的矩形
context.strokeStyle = 'red';
context.strokeRect(20, 20, 30, 40); // 在坐标(20, 20)画一个边框红色的长30,高40 的矩形
context.beginStroke();  // 1、context.beginPath() - 创建路径
context.lineTo(x, y);
context.moveTo(x, y);
context.stroke();
context.fillText('string', x, y);
context.drawImage...
3D:
const context = drawing.getContext('webgl');
坐标原点左下角
定义视口
 // 2、viewport
context.viewpoint(drawing.width/2, 0, drawing.width/2, drawing.height/2); // 右下角1/4
// 作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的
- 第三遍 2025.1.22 -
原型链:一个函数的原型是另一个函数的实例
原型链继承-函数共享不用再重复创建-属性共享修改一个其他实例也改变了
Sub.prototype = new Super();
组合继承-每个实例都有独立的作用域-父级构造函数被执行两次
function Sub() { Super.apply(this, arguments) } Sub.prototype = new Super();
寄生组合式继承父级的原型对象副本是子级的原型对象
function inherit(Sub, Super){
    let proto = Object.create(Super.prototype);
    Object.defineProperty(proto, "constructor", {
        enumerable: false,
        value: Sub
    })
    Sub.prototype = proto;
}

- 第二遍 2025.1.19 -
原型链一个函数的原型对象是另一个函数的实例
原型链继承: Sub.prototype = new Super();
	优点函数不再需要每次都创建实例共享函数方法
    缺点所有实例共享属性修改一个会改变所有其他访问实例
组合继承: function Sub() { Super.apply(this, arguments) }
		Sub.prototype = new Super();
	优点每个实例拥有各自的属性
    缺点父级构造函数每次都要执行2次
寄生组合式继承: 父类的原型对象的副本是子类的原型
function inherit(Sub, Super) {
    let proto = Object.create(Super.prototype);
    proto.constructor = Sub;
    ❌❌ // 1、Object.defineProperty(proto, "constructor", {enumberable: false, value: Sub})
    Sub.prototype = proto;
}

- 第一遍 -
原型链一个函数的原型是另一个函数的实例
原型链继承
	Sub.prototype = new Super();
	优点共享属性和方法
    缺点所有属性共享修改一个实例会影响其他实例
组合继承
	function Sub(){ Super.apply(this, arguments) }
	Sub.prototype = new Super();
	优点每个实例都会有独立的属性
    缺点Super 父类构造函数每次都要执行2遍
寄生组合式继承: 父类的原型对象副本是子类的原型对象
	function inherit(Sub, Super) {
        const proto = Object.create(Super.prototype);
         // 1、Object.defineProperty(对象,对象属性,对象值),所以应该写成 Object.defineProperty(proto, constructor, { enumerable: false, value: Sub })
        Object.defineProperty(proto, {
            enumerable: false,
			value: { constructor: Sub }
        }); // 修复 自定义原型对象会导致的 constructor 指向问题
        Sub.prototype = proto;
    }
// 作业62:fiber架构是什么?优点和实现方式?
- 第五遍 2025.1.22 -
三大主流程Scheduler 调度器 - 优先级调控在渲染时宏观掌控
Reconciler 协调器 - 时间分片使用 shouldYield 判断是否有足够时间render 阶段
Render 渲染器 - commit阶段应用到真实 UI
SchedulerReconciler 都是在内存中计算的会被以下一些情况打断报错时间分片不够优先级更高的任务
Render 会更新 UI同步不可打断
优点1优先级调度-Scheduler+中断恢复机制-优先处理一些用户交互非视口的渲染优先级延后
2时间分片-Reconciler+中断恢复机制-react每次更新都会重新创建整棵fiber树-对比-更新之前同步执行会长时间占用主线程阻塞重绘fiber 将渲染任务拆分成多个渲染任务单元在重绘前+空闲时间执行
3渐进式渲染-all-会优先处理高优先级渲染每次重绘前 shouldYield 判断时间是否够进行一次渲染单元
4增量式 Diff-不会一次性比较整棵树而是跟随程序进展逐步计算-渲染提高算法的性能 // ⭐️ 根据任务的执行进度,逐步比较和更新部分节点

- 第四遍 2025.1.18 -
Scheduelor  - 管理优先级在渲染过程中宏观把控 ❌❌❌❌ // 1、调度器
Reconciler 协调器 - 根据 shouldYield 计算时间分片是否有足够时间对应 render 阶段
Render 渲染器 - 对应 commit 阶段更新 UI
Scheduelor  Reconciler 都是在内存中计算的异步处理可随时被打断报错更高优先级时间分片不够
1渐进式渲染[all]先处理视口部分渲染逐步渲染整个页面 
❌❌❌❌ // 2、少:应该先说明 react 每次更新都要递归构建整棵 fiber 树+比较+更新,长时间占用主线程,会阻塞重绘 + 改:[Scheduelor+中断恢复机制]
2时间分片[Reconciler+中断恢复机制]将整个渲染分为多个渲染任务单元 - 在下一个渲染前计算时间分片是否足够执行一个单元或在空闲时间执行渲染任务
3优先级调控[Scheduelor+中断恢复机制]在渲染过程中根据优先级优先处理交互延迟非视口等渲染任务
❌❌❌❌ // 3、改:[Scheduelor+优先级调度机制]
4Diff 增量式[all]可以根据优先级和时间分片拆分任务小区域计算提高 diff 效率
❌❌❌❌ // 4、修改描述:不是一次性比较整棵树,而是根据任务执行进度,逐步比较、更新部分节点 - 减少了每次比较的范围,提高了效率

- 第三遍 -
fiber 分成三大部分 // ⭐️ 不是「三大部分」,是「三个主流程」
Scheduler 调度器 - 在渲染过程中不直接参与渲染用于优先级执行调度 // ⭐️ 宏观调控
Commit - 对应 render 阶段
❌❌❌ // 1、不是「Commit」,是「Reconciler」,叫做「协调器」,作用是「调用 shouldYield 判断当前 Time Slice 是否有剩余时间」
	上述两部分都是异步的随时可能会因为一些原因被打断例如报错 时间切片剩余时间不够 有更优先级
    ❌❌❌ // 2、不是因为「异步」才可以被打断,是以为「他们都在内存中执行,不会更新宿主环境 UI」
Reconciliator - 对应 commit 阶段是同步的不可被打断
❌❌❌ // 3、不是「Reconciliator」,是「Render」「渲染器」
❌❌❌ /** 上述三个主流程记忆法:
	Scheduler 调度器 - 优先级宏观调控 
	Reconciler 协调器 - render 阶段,用于判断时间切片剩余时间
	Render 渲染器 - commit 阶段(更新UI)*/
优点
Fiber 架构提供了时间分片 + 优先级调度
1之前 react 的解析渲染流程是同步的会占用主线程阻塞页面更新现在fiber 将渲染任务拆分成一个个小的渲染任务单元利用浏览器的空余时间执行小单元
❌❌❌ /** 1、第一个优点的关键词是「渐进式渲染」。
	描述内容和上述很像,但是「占用主线程」不是重点,是「长时间」占用主线程。
	不是「react 的解析、渲染」在占用,是「react 每次更新都要递归构建整棵 fiber 树、比较、更新」,重点要突出导致访问卡顿感的原因是渲染卡住了。
	实现方式:Scheduler调度器 + 中断恢复机制*/
2在处理用户交互时通过优先级调度优先处理交互提升用户交互体验比如不在视口的渲染任务可以延后执行
❌❌❌ // 2、第二个优点的关键词是「优先级调度」。实现方式:Scheduler 调度器 + 优先级调度机制
3Diff 算法优化比较两棵 fiber 树的差异计算出最小UI更新应用到真实 DOM减少了对 DOM 操作带来的性能影响
❌❌❌ /** 3、第三个优点,现在描述的是怎么实现 diff,但是 fiber 对 diff 的优化主要是「增量式Diff」。
	「不是一次性比较整棵树,而是根据任务的执行进度,逐步比较和更新部分节点,根据时间和优先级安排,逐步更新,减少了每次比较的范围,提高了效率」。
	实现方式:Scheduler调度器 + 中断恢复机制 + Reconciler协调器 + 双缓存机制*/
❌❌❌ /** 4、少一个优点「时间分片」。
	「有效利用浏览器空闲时间,在每帧开始之前检查剩余时间,是否执行一个小的渲染任务单元」。
	实现方式:Reconciler协调器 + 中断恢复机制*/
实现方式:⭐️ // 核心是对的,结合 fiber 的三大主流程一起
1实现了更好的时间切片提供了渲染任务暂停恢复的能力
2任务管理+优先级调度

- 第二遍 -
Fiber架构分为三个部分
第一个是schedule调度器在整个渲染流程当中不直接参与渲染但作为总把控
⭐️ // 主要用于优先级调度,宏观调控
第二个是 Reconciler 对应的是render阶段
上述两个部分都是异步执行的随时会被以下几种情况打断1️⃣错误2️⃣更高优先级的任务比如用户交互3️⃣时间分片时间不够
第三个是render对应的是commit阶段
这个部分是同步无法被打断直到渲染到真实页面的dom
优点
1.在Fiber架构出现以前react的渲染更新流程是同步执行的耗时比较久会阻塞页面渲染性能差
通过fiber实现了时间分片和优先级调度拥有了暂停和恢复执行渲染任务的能力将整个渲染任务拆分成了很多小的渲染单元在针和针的渲染间隙进行执行尽可能不影响渲染帧的执行
2.同时优先级调度任务可以让react优先处理和用户的交互将一些没那么重要的渲染任务稍后执行例如不再是口中的画面的图块处理
⭐️ // 总结:「渐进式渲染」、「暂停恢复渲染能力」、「优先级调度」、「时间分片管理」
1.双fiber node的虚拟dom树 
2.优先级调度器对任务进行统一调度
⭐️ // 「优先级调度器」其实是两样,「任务调度」 + 「优先级管理」
3.模拟了生成器和yield有了暂停和恢复渲染任务的能力
❌❌ // 1、这里缺少一个 Diff 算法的执行优化:增量式 
4.在每次渲染之前都会调用 shouldYield 判断当前是否拥有足够多的时间处理小的渲染单元任务
⭐️ // 这里就是时间分片的原理

- 第一遍 -
fiber 是为了解决CPU瓶颈 - 连续的 js 执行会占用主线程导致渲染阻塞低于60帧/秒出现卡顿
 // 1、不是「连续的 js 执行」会长时间占用主线程,是「React递归构建整颗 DOM 树、比较、更新」
 // 2、明确 React 由于每次构建的都是整颗树 + 之前是同步渲染 - fiber 做到了异步渲染
优点时间分片 + 优先级调度
	1将React的代码执行分成很多小模块在帧和帧之间的小间隙里面检测时间是否充足用于执行模块 - 不会占据很长时间的主线程不影响渲染
	 // 3、分成小模块的自然也不只是「代码执行」,是「渲染工作」被分解成了「小任务单元」 + 同时可以「暂停 + 恢复」 -> 使得 在每帧开始前检查剩余时间,能否执行的也就是「小渲染任务单元」
	2标记优先级如果遇到用户交互则优先处理提高渲染效率和用户体验
     // 4、少写了:渐进式渲染
 // 上述写的都是原理,并不是具体实现 1、双缓存机制 current tree 和 work-in-progress tree 2、任务调度和优先级管理 3、Diff 算法优化为增量式 Diff
// 作业58:React和Vue是怎么描述UI的
- 第四遍 2025.1.22 -
React: JSX=HTML+JS花括号{}类组件+函数组件state-组件自变量+props外部传入自变量
Vue: 模版语法双花括号指令 v-if/v-else/v-else-if/v-for/v-modal/v-bind ⭐️ // v-show

- 第三遍 2025.1.18 -
React: JSX=HTML+JS类组件+函数组件state-组件的自变量props-组件传入的变量花括号{}插入js
Vue: 模版语法 + 双花括号 + 指令 v-if/v-elseif/v-show/v-model/v-bind
⭐️ // v-else-if + v-for

- 第二遍 -
React - JSX=HTML+JS单个{}将js插入展示
❌❌ // 1、少写了 ①自定义组件-函数组件+类组件 ②state-状态,组件内部自变量;props-属性,外部传入自变量
Vue - 模版语法双个将js插入展示 + 指令 v-ifv-show
⭐️ // 指令还有 v-for v-bind v-else/v-else-if v-on v-model等

- 第一遍 -
React: JSX - 由js+HTML组成函数组件+类组件
// ❌ 少写了: 可以自定义组件 + 使用花括号{}将js嵌入JSX {note}
//	少写了: state-状态,组件内部自变量
//	少写了: props-属性,其他组件传入的自变量
Vue: 模版 - 花括号嵌入模版+指令<div v-if="show">
// ❌ 模拟语法 - 结合数据+预先定义的模版
//  少写了: 使用双大括号展示数据 + 指令v-if="show"/v-for="item in items"
// 作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?
- 第三遍 2025.1.22 -
组件返回可以用于渲染的 JSXhook 可以返回任何值
组件应该写想要做什么而不是怎么做 - 抽离到 hooks 
hooks如果你想要写得 hook 中没有使用其他 hook建议不要使用 hook
hooks 之间是不共享 state 只是使用相同的修改 state 逻辑
在渲染时有更新触发组件重新渲染渲染时会按序执行所有 hooks

- 第二遍 -
React 组件用于返回可以渲染的UI一般是 JSX
	- 组件用于描述做什么而不是怎么做
如果 hook 中没有 hooks 则不建议抽离成 hook
	- hook 只是共享状态逻辑并不是直接共享状态
    - hook 可以返回任何值
顺序组件更新时会按序执行所有 hooks

- 第一遍 -
React 组件用于复用 html 结构 - 清晰的输入和明确的输出尽可能不要有副作用写注释
hookhook用于复用代码逻辑内部没有 内置hook 可以不使用 hook 来复用函数即可
// ❌修改下题目:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序?
React 组件代码表达的是 目标而不是具体实现 - 描述的是想要做什么而不是怎么做
hook 中如果没有内置 hook则不建议使用 hook
自定义 hook 只是共享状态逻辑而不是状态本身 - 每次调用都有独立的状态
返回值组件一般是返回一段 React 能够显示的内容例如JSX结构hook可以返回任何值
渲染组件重新渲染时会执行所有 hook 链表所以组件一直可以接收到最新的 props  state
// 作业53:胜利者一无所获 说的是什么
- 第三遍 2025.1.22 -
海明威 战地春梦 反战战争胜利的一方失去了美好的品质善良等
质疑赢家通吃逻辑质疑胜利的意义鼓励人们追求内在美好的品质

- 第二遍 2025.1.19 -
海明威 战地春梦 反战 战争胜利者失去了美好的品质
鼓励人们追求内在美 ⭐️ //质疑胜利的本质

- 第一遍 -
海明威 战国春秋中用于反战战胜方失去了美好的品质善良公序 // ❌ 《战地春梦》
质疑胜利的本质如果胜利者并不得获得很多那要追求什么
鼓励人们追求内在美包括善良纯真等
// 2024.05 rem是基于什么原理进行适配的?
- 第三遍 2025.1.22 -
rem 相对于根元素 html
em font-size 相对于父级的 font-sizeheight width padding margin 是相对于当前元素的 font-size

- 第二遍 2025.1.19 -
rem - 根元素 html
em - font-size 属性相对于父级的 font-size; width/height/padding/margin 相对与本元素的 font-size

- 第一遍 -
rem 相对于根元素的 font-size - html(body)  // 1、不是 body-只是html的一个子标签,就是 html
em 
	- 当前节点的字体大小相对于父元素的 font-size
	- 当前节点的 width/height/padding/border/margin 相对于当前元素的 font-size
// 作业85:Lambdas是什么
匿名函数
// 作业71:React为什么要合成事件?和原生事件有什么不同?
- 第四遍 2025.1.22 -
react合成事件:①浏览器处理兼容性对事件系统的封装开发者专注于写代码 借用事件委托在组件挂载时将事件冒泡到最顶层一般是 document节省了对DOM元素绑定的开销
不同
事件绑定方式合成事件-JSX属性绑定事件处理函数原生事件-onclick属性addEventListener
事件执行顺序合成事件-由react统一调度一般是冒泡原生事件-先捕获后冒泡一般早于合成事件
事件对象合成事件-封装事件统一原生事件-各个浏览器有差异

- 第三遍 2025.1.19 -
合成事件:①React 对原生事件的封装抹平浏览器间的差异通过事件委托机制冒泡到顶层标签绑定一般是 document ⭐️ // 跨浏览器兼容
优点抹平差异关注功能实现事件委托机制-挂载到DOM时冒泡到 document 绑定节省对DOM绑定的性能
事件绑定方式JSX 绑定事件处理函数onclick 或者 addEventListener
事件执行顺序合成事件由React统一调控一般是冒泡原生事件一般是先捕获再冒泡比合成事件执行得早
事件对象合成事件统一对象包括一些属性和方法原生事件不同浏览器间有差异

- 第二遍 -
React 的合成事件其实是一个处理了浏览器兼容性的事件处理系统 ⭐️ // + 是对浏览器原生事件的封装
功能:①处理各个浏览器的差异使用者可以只关注代码实现
	 利用事件委托所有的合成事件在构建时统一冒泡到最外层一般是 document 标签上绑定大量减少了绑定事件的性能消耗
     ❌❌ // 1、不是在「构建时」,事件委托发生在「组件挂载时」
差异
	事件对象合成事件-统一了对象类型原生事件-根据浏览器不同有一定差异
	事件执行机制合成事件-由React内部事件系统统一管理执行一般是在冒泡阶段执行
				原生事件-捕获阶段就会触发比合成事件早同步执行触发后直接执行
	❌❌ // 2、少写了:事件绑定方式 合成事件-JSX中属性指定事件处理函数;原生事件 标签的onclick或者addEventListener

- 第一遍 -
合成事件的定义React 统一了各个浏览器内核的原生 API 差异抹平差异后用户只需要关注功能实现而不需要再关注宿主环境
合成事件是原生事件的高级集合
 // 1、内核是对的,不是“高级集合”,是「React对浏览器原生事件的封装」,是「一个跨浏览器兼容的事件系统」
// 优点/原因:① 跨浏览器兼容性 ②性能优化:通过事件委托到 document,减少大量事件绑定的开销
 // 2、“不同”需要具体的描述:
// ①事件对象(有一套标准的属性和方法,用于事件信息 VS 由浏览器提供,属性方法因浏览器而异)
// ②事件绑定方式(React中JSX属性中指定事件处理函数来绑定 VS 原生标签的 onclick/addEventListener)
// ③事件执行顺序(内部事件系统决定,冒泡执行,一般比原生晚 VS 先捕获再冒泡)
// 2021.04 05-3 白屏优化?
- 第三遍 2025.1.20 -
硬件 DNS 缓存优化TCP 优化服务器优化
代码1html 精简代码优化结构
2css尽可能使用 link 而不是@import触发预加载提前下载从右向左解析减少层级媒体查询减小 css 文件大小利用 GPU 加速减少回流重绘
3js尽可能不写内联代码deferasync不阻塞渲染
4压缩文件
5HTTP 请求减少数量和大小

- 第二遍 2025.1.19 -
网络进程下载完成触发浏览器刷新页面渲染进程解析还没完成出现解析白屏
1DNS 缓存优化服务器TCP优化
2html减少结构层级精简代码
css从右向左解析所以减少层级媒体查询减少加载代码体量调用 GPU 等优化方式减少回流重绘
❌❌ // 1、使用 link 加载而非 @import,可以触发预加载
jsdeferasync 加载 ❌❌ // 2、尽量不使用「内联代码」
3压缩代码
4HTTP 请求数量减少大小减少

- 第一遍 -
白屏网络进程文件加载完毕浏览器开始刷新页面但是主线程渲染流程还没有结束页面短暂出现解析白屏
 // 1、少写 - 硬件加速:DNS 解析优化,例如缓存、预加载;TCP、服务器优化
1html 精简代码简化结构减短DOM解析时间
2css 从右到左的解析方式尽可能减少层级使用 link 而非 @import从而触发预加载使用 css 优化/GPU 加速减少回流重绘  // 2、少写 - 对于大文件的 css,利用媒体查询拆分不同用途
3js 加载使用 defer/async 避免阻塞  // 3、少写 - 尽量不使用 内联代码
4压缩代码
5减少 http 请求次数大小
// 2024.12 模拟实现promise
- 第四遍 2025.1.20 -
const Status = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
function MyPromise(executor){
    this.status = Status.pending;
    this.value = null;
    this.reason = null;
    this.resolveCalls = [];
    this.rejectCalls = [];
    
    const resolve = (value) => {
        if(this.status === Status.pending) {
            this.status = Status.resolve;
            this.value = value;
            this.resolveCalls.forEach(it => it(value));
        }
    }
    const reject = (reason) => {
        if(this.status === Status.pending) {
            this.status === Status.reject;
            this.reason = reason;
            this.rejectCalls.forEach(it => it(reason));
        }
    }
    try{
        executor(resolve, reject);
    }catch(e){
        reject(e);
    }
}
MyPromise.prototype.then = function(onResolve, onReject) {
    onResolve = typeof onResolve === 'function' ? onResolve : v=>v;
    onReject = typeof onReject === 'function' ? onReject : r => { throw r };
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
            setTimeout(() => {
                try{
                    const res = onResolve(this.value);
                    resolve(res);
                }catch(e){
                    reject(e);
                }
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                try{
                    const res = onReject(this.reason);
                    resolve(res);
                }catch(e){
                    reject(e);
                }
            }, 0)
        }
        switch(this.status) {
            case Status.pending:
                this.resovleCalls.push(resolveFun);
                this.rejectCalls.push(rejectFun);
                break;
            case Status.resolve:
                resolveFun();
                break;
            case Status.reject:
                rejectFun();
                break;
        }
    })
}
MyPromise.prototype.catch = function(onReject) {
    return this.then(null, onReject);
}
MyPromise.prototype.finally = function(callback) {
    return this.then(value => { callback(); return value; },
                    reason => { callback(); throw reason; })
}
MyPromise.resolve = function(value) {
    return new MyPromise((resolve, reject) => {
        if(value instanceof MyPromise) {
            return value.then(resolve, reject); ⭐️ // 可以省略 return
        }
        resolve(value);
    })
}
MyPromise.reject = function(reason) {
    return new MyPromise((resolve, reject) => {
        reject(reason);
    })
}
MyPromise.all = function(arr) {
    return new MyPromise((resolve, reject) => {
        return arr.reduce((pre, cur, index) => {
            cur.then(res => {
                pre.push(res);
                if(index === arr.length - 1){
                    resolve(pre);
                }
            }, reject);
            return pre;
        }, [])
    })
}
MyPromise.race = function(arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
    })
}

- 第三遍 2025.1.19 -
then catch finally resolve reject all race
1promise 使用函数来实现而不是 class
2then  onResolveonReject 都会返回 fulfilled 状态的 promise
const Status = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
MyPromise.prototype.then = function(onResolve, onReject){
    onResolve = typeof onResolve === 'function' ? onResolve : v => v;
    onReject = typeof onReject === 'function' ? onReject : r => { throw r };
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
            setTimeout(() => {
                try{
                    const res = onResolve(this.value);
                    resolve(res);
                }catch(e){ reject(e) }
            }, 0)
        };
        const rejectFun = () => {
            setTimeout(() => {
                try{
                    const res = onReject(this.reason);
                    resolve(res);
                }catch(e){ reject(e) }
            }, 0)
        };           
        switch(this.status) {
            case Status.pending:
                this.resolveCalls.push(resolveFun);
                this.rejectCalls.push(rejectFun);
                break;
            case Status.resolve:
                resolveFun();
                break;
            case Status.reject:
                rejectFun();
                break;
        }
    })
}

- 第二遍 -
const State = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
class MyPromise { ❌❌ // 1、模拟 Promise 的时候,一般都是用函数
    constructor(executor) {
        this.state = State.pending;
        this.value = null;
        this.reason = null;
        this.resolveCalls = [];
        this.rejectCalls = [];

        const resolve = (value) => {
            if(this.state === State.pending) {
                this.state = State.resolve;
                this.value = value;
                this.resolveCalls.forEach(it => it(value));
            }
        }

        const reject = (reason) => {
            if(this.state === State.pending) {
                this.state = State.reject;
                this.reason = reason;
                this.rejectCalls.forEach(it => it(reason));
            }
        }
        try{
            executor(resolve, reject);
        }catch(e){
            reject(e);
        }
    }
}
MyPromise.prototype.then = function(onResolve, onReject) {
    onResolve = typeof onResolve === 'function' ? onResolve : v => v;
    onReject = typeof onReject === 'function' ? onReject : r => { throw r };
    let that = this;
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
            setTimout(() => {
                const res = onResolve(that.value);
                resolve(res);
                ❌❌ // 2、遗漏捕获错误
                /**
                try{
                	const res = onResolve(that.value);
                	resolve(res);
                }catch(e){ reject(e) }
                */
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                const res = onReject(that.reason);
                reject(res);
                ❌❌ // 3、遗漏捕获错误 + onReject 返回的依旧是 resolve 的 promise
                /**
                try{
                	const res = onReject(that.reason);
                	resolve(res);
                }catch(e){ reject(e) }
                */
            }, 0)
        }
        switch(that.status) {
            case Status.pending:
                that.resolveCalls.push(resolveFun);
                that.rejectCalls.push(rejectFun);
                break;
            case Status.resolve:
                resolveFun();
                break;
            case Status.reject:
                rejectFun();
                break;
        }
    })
}
MyPromise.prototype.catch = function(onReject) {
    return this.then(null, onReject)
}
MyPromise.prototype.finally = function(cb) {
    return this.then(value => {cb(); return value;},
                     reason => {cb(); throw reason;})
}
MyPromise.resolve = function(value) {
    return new MyPromise((resolve, reject) => {
        if(value instanceof MyPromise) {
            value.then(resolve, reject);
        } else {
            resolve(value);
        }
    })
}
MyPromise.reject = function(reason) {
    return new MyPromise((resolve, reject) => reject(reason));
}
MyPromise.all = function(arr) {
    return new MyPromise((resolve, reject) => {
        arr.reduce((pre, cur, index) => {
            cur.then(res => {
                pre.push(res);
                if(index === arr.length) {
                    resolve(pre);
                }
            }, reject);
			return pre;
        }, [])
    })
}
MyPromise.race = function(arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => MyPromise.resolve(it).then(resolve, reject));
    })
}

- 第一遍 -
then catch finally resolve reject all race
const Status = {
    pending: 'pending',
    resolve: 'fulfilled',
    reject: 'rejected'
}
function MyPromise(executor) {
    this.status = Status.pending;
    this.value = null;
    this.reason = null;
    this.callbacksResolve = [];
    this.callbacksReject = [];
    const resolve = (value) => {
        if(this.status === Status.pending) {
            this.status = Status.resolve;
            this.value = value;
            this.callbacksResolve.forEach(it => it(value));
        }
    }
    const reject = (reason) => {
        if(this.status === Status.pending) {
            this.status = Status.reject;
            this.reason = reason;
            this.callbacksReject(it => it(reason))
        }
    }
    
    try{
        executor(resolve, reject);
    }catch(e) {
        reject(e);
    }
}
MyPromise.prototype.then = function (onResolve, onReject) {
    onResolve = typeof onResolve === 'function' ? onResolve : v => v;
    onReject = typeof onReject === 'function' ? onReject : r => {throw r};
    return new MyPromise((resolve, reject) => {
        const resolveFun = () => {
			setTimeout(() => {
                try{
                    const res = onResolve(this.value);
                    resolve(res);
                }catch(reject);  // 6、catch 不能这么写 catch(e){ reject(e) }
            }, 0)
        }
        const rejectFun = () => {
            setTimeout(() => {
                try{
                    const res = onReject(this.reason);
                    resolve(res);
                }catch(reject);  // 6、catch 不能这么写 catch(e){ reject(e) }
            }, 0)
        }
        switch(this.status) {
            case Status.pending:
                this.callbacksResolve.push(() => resolveFun());
                this.callbacksReject.push(() => rejectFun());
                break;
            case Status.resolve:
                resolveFun();
                break;
            case Status.reject:
                rejectFun()
                break;
        }
    })
}
MyPromise.prototype.catch = function (onReject) {
    return this.then(null, onReject);
}
MyPromise.prototype.finally = function(callback) {
    return this.then((value) => { callback(); return value; },
                    (reason) => { callback(); throw reason; })
}
MyPromise.resolve = (value) => {
    return new MyPromise((resolve, reject) => {
        if(value instanceof MyPromise) {
            return value.then(resolve, reject);
        }
        resolve(value);
    })
    ⭐️ // 3、这里的 return 可以删除,不影响
}
MyPromise.reject = (reason) => {
    return new MyPromise((resolve, reject) => {
        reject(reason);
    })
    ⭐️ // 2、简写成 return new MyPromise((resolve, reject) => reject(reason));
}
MyPromise.all = function (arr) {
    return new MyPromise((resolve, reject) =>{
        return arr.reduce((pre, cur, index) => {
             ⭐️ // 4、这里的 return 可以删除,不影响
            cur.then(res => {
                pre.push(res);
                if(index === arr.length -1) {
                    resolve(pre);
                }
            }).catch(reject);
             // 1、reject 是作为 then 的第二个函数 -> }, reject)
            return pre;
        }, [])
    })
}
MyPromise.race = function (arr) {
    return new MyPromise((resolve, reject) => {
        arr.forEach(it => {
            return MyPromise.resolve(it).then(resolve, reject);
        })
        ⭐️ // 5、这里科利简写 arr.forEach(it => MyPromise.resolve(it).then(resolve, reject))
    })
}

LTN2 - 10题 - 2025.1.20 - 1.21 131min

✅作业17:对斐波那切数列进行尾调用优化 ❌作业20:浏览器打开一个 URL 发生了什么 ✅作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现 ✅作业34:写出强缓存、协商缓存的流程及相关属性 ✅作业57:“假装成功”的意义是什么 ✅2021.06 BFC特点、触发、存在/解决的问题 ✅2020.03 改变原数组+结束循环+性能排序 ❌2021.09 Object属性排序、与Map的区别 ✅2021.07 防抖节流 ✅2024.12 模拟实现jsonp

// 2024.12 模拟实现jsonp
- 第二遍 2025.1.21 -
function jsonp(url, params, cb) {
    const suffix = Object.keys(params).map(it => `${key}=${params[key]}`).join('&');
    const totalUrl = url + url.includes('?') ? '&' : '?' + suffix + `&callback=${cb}`;
    
    const dom = document.createElement('script');
    window[cb] = function() { ⭐️ // 带参数 data
        cb();
        document.body.removeChild(dom);
    }
    
    dom.src = totalUrl;
    document.body.appendChild(dom);
}

- 第一遍 -
function jsonp(url, params, cb){
    const suffix = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&');
    const allUrl = url + (url.includes('?') ? '&' : '?') + suffix + (suffix ? '&' : '') + `callback=${cb}`;
    
    const dom = document.createElement('script');
    window[cb] = function (...args) {
        cb(...args);
        document.body.removeChild(dom);
    }
    
    dom.src = allUrl;
    document.body.appendChild(dom);
}
// 2021.07 防抖节流
- 第二遍 2025.1.21 -
function debounce(fn, delay){
    let time;
    return (...arg) => {
        if(time) {
            clearTimeout(time);
        }
        time = setTimeout(() => {
            fn(...arg);
        }, delay);
    }
}
// 间隔多少秒才能执行一次
function throttle(fn, delay) {
    let flag = false;
    let time; ⭐️ // 已经不需要 time 来控制执行时机了
    return (...args) => {
        if(flag) {
            return
        }
        flag = true;
        fn(...args);
        time = setTimeoute(() => {
            flag = false;
        }, delay)
    }
}

- 第一遍 -
function debounce(fn, delay) {
    let timeId = null;
    return function (...args) {
        if(timeId) {
            clearTimeout(timeId);
        }
        timeId = setTimeout(() => {
            fn(...args);
        }, delay)
    }
}
function throttle(fn, delay) {
    let flag = true;
    return function(...args) {
        if(!flag) {
            return;
        }
        flag = false;
        fn(...args);
        setTimeout(() => {flag = true}, delay)
    }
}
// 2020.03 改变原数组+结束循环+性能排序
- 第二遍 2025.1.21 -
可以改变原数组的函数shift pop unshift push sort reverse splice fill
return  break 不可以结束的循环filter map forEach reduce
性能循环 for循环 for-of forEach map for-in

- 第一遍 -
可以改变原数组的数组方法有:shift unshift pop push reverse sort splice fill
不可以通过return和break结束循环 filter map reduce forEach
排序for>for...of>forEach>map>for...in
// 2021.06 BFC特点、触发、存在/解决的问题
- 第二遍 2025.1.21 -
BFC 特点:①独立容器 从上而下垂直排列 和外部浮动元素不重合 计算高度时包含浮动元素-高度塌陷
触发:①float不为none overflow不为none:hidden/auto/scroll ⭐️ // overflow不为visible
position:absolute/fixed display:flex/table-captoin/table-cell/inline-block 根元素
存在的问题和解决BFC内两个盒子上下 margin 重叠 - 外边距重叠解决方案给父元素 display:flex

- 第一遍 -
特点:①独立容器 从上到下垂直排列 不和外部的浮动元素重合
触发:①float  none overflow  visiblehidden/auto/scroll display: flex/inline-block/table-caption/table-cell position: absolute/fixed 根元素
问题同一个BFC中相邻的两个盒子会出现 外边距重叠只显示一个 margin
解决方案给他们的父元素添加 display:flex
// 作业57:“假装成功”的意义是什么
- 第二遍 2025.1.21 -
意识能够改为行为行为也能够改变心态 言灵 ⭐️ // 提高睾丸酮,降低皮质醇 - 提高自信心,降低压力 fake it until you become it

- 第一遍 -
客观从物理层面说肢体是可以改变人体内的睾丸酮和皮质醇(压力激素)
假装成功可以降低压力提高自信心
人是有自我预言机制的你越相信你越能做到
you derserve better, fake it until you become it!
// 作业34:写出强缓存、协商缓存的流程及相关属性
- 第二遍 2025.1.21 -
强缓存根据 URL 在本地内存磁盘超找缓存 - 找到并返回就是强缓存生效
协商缓存本地有缓存但是已过期 cache-control: max-age=2000 / Expires: deadline 进协商缓存流程
If-modified-since/Last-modifiedIf-none-match/ETag - 304 协商缓存生效 ⭐️ // 刷新本地缓存时间

- 第一遍 -
1url 在本地查找是否存在缓存存在的话直接使用[强缓存生效]
2存在缓存但是缓存失效 If-none-match/EtagLast-Modified/If-modified-since 如果服务器返回 304代表缓存内容正确使用缓存并刷新本地的缓存有效期[协商缓存生效]
有效期Cache-Control: max-age=2000;单位是秒)、Expires: deadline具体时间点
// 作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现
- 第二遍 2025.1.21 -
    Promise.all([A(), B()]).then(C);
async function() {
    await Promise.all([A(), B()]);
    C();
}

- 第一遍 -
1Promise.all([A(), B()]).then(C);
2async function () {
    await Promise.all([A(), B()]);
    C();
}
// 作业17:对斐波那切数列进行尾调用优化
- 第二遍 2025.1.20 -
斐波那契数列正常写法
function fib(n) {
    if(n<2){
        return n;
    }
    return fib(n-1) + fib(n-2);
}
0 1 1 2 3
优化写法
function betterFib(a = 0, b = 1, n) {
    if(n===1){
        return 0
    }
    return betterFib(a+b, a, n-1);
}

- 第一遍 -
function fib(n) {
    if(n<2)return n;
    return fib(n-1)+fib(n-2);
} 0 1 1 2 3 5 8 
function optimizeFib(a=0, b=1, n){
    if(n === 0) return a;
    return optimizeFib(b, a+b, n-1)
}

LTN3 - 11题 - 2025.1.20 65min

❌作业43:TED 怎么提升自信 ❌作业42:TED 如何和大脑正确交流沟通 ✅作业40:写出以下值

1Number(null)
2Number(undefined)
3isNaN() 检测那些值为 true?
4NumberparseIntparseFloat 检测 空字符串 得到?
51+'2'+'2'
1+ +'2'+'2'
1+ -'1'+'2'
'A'-'B'+'2'
'A'-'B'+ 2
6let i = 0, age = 30;
i++ + age;
++i + age;
7 12.34 | 0;
12.43 >> 0;
8null === undefined

❌作业39:shim 和 polyfill 区别 ✅作业23:写出js 代码的执行顺序。词法作用域的特点是什么?

function bar(){
    console.log(myName);
}
function foo(){
    var myName = 'hi 坑';
    bar();
}
var myName = 'bye 坑';
foo();

❌作业6:new操作符实例化一个对象的过程 ✅作业32:什么是队头阻塞?原因是什么?怎么解决? ✅作业16(1):箭头函数和普通函数的区别是什么?(3点) ✅2019.07 盒模型有哪些?有什么区别? ❌2019.07 为什么要清除浮动?怎么清除浮动? ❌2021.06 CSS 选择器 - 权重/匹配方式

// 2019.07 盒模型有哪些?有什么区别?
box-sizing: content-box; // W3C标准盒模型 witdh 只包含内容
box-sizing: border-box; // IE盒模型 width包含 内容+padding+border
// 作业16(1):箭头函数和普通函数的区别是什么?(3点)
不能使用 new.target super arguments不能用作构造函数没有prototype
// 作业32:什么是队头阻塞?原因是什么?怎么解决?
队头阻塞HTTP/1.1 请求-应答模式导致的一个请求响应不返回其他请求响应需要等待
 ⭐️ // HTTP/1.1 域名分片 + 并发连接
解决方式HTTP/2 采用二进制帧-双向传输序列虚拟的流一个连接中可以处理多个请求响应-多路复用多个请求响应之间没有顺序关系解决了队头阻塞在应用层解决但传输层TCP还存在队头阻塞
HTTP/3 将TCP换为UDP  ⭐️ // QUIC 协议
// 作业23:写出js 代码的执行顺序。词法作用域的特点是什么?
bye 
词法作用域只和代码所写位置有关
js代码执行顺序:①执行上下文this
						var 变量作用域 - 先创建 arguments再函数提升再变量提升
                        let const 词法作用域 - 作用域链多个执行上下文的变量作用域组成的链表
			可执行代码 AST
// 作业40:写出以下值
10 
2NaN 
3{} NaN undefined 
4Number - 0含有非数字字符时返回NaNparseIntparseFloat - NaN不以数字开头的字符串返回NaN 
5'122' '32' '02' 'NaN2' NaN 
630 32
712 位操作符只能操作 32 位的整数运算时将两遍值转换成整数
8true undefined派生自null在js中规定两者相等性比较为 true

LTN5 - 2题 - 2025.1.20 10min

❌作业2:支持迭代器的原生语言特性有哪些? ✅作业5:什么是生成器对象?有什么特点?什么时候会被执行?

// 作业5:什么是生成器对象?有什么特点?什么时候会被执行?
生成器对象生成器函数返回的对象
特点默认是可迭代对象
在创建时生成器对象保持一个暂停状态第一次调用 next 函数时被执行 ⭐️ // 恢复执行

LTN①③


LTN ①③ - 错题重做 28题 - 2025.1.18 - 1.19 5h10m

✅作业58:React和Vue是怎么描述UI的 ❌作业62:fiber架构是什么?优点有哪些? 是怎么实现的? ✅作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)说明 ❌作业64:Vue渲染怎么实现的? ✅作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期 ✅作业66:React如何处理错误 ✅作业67:React 怎么做的性能优化? ❌作业68:React hooks 的原理是什么?useEffect useState模拟实现 ❌作业70:Vue 和 React 的 Diff 算法比较 ✅作业71:React为什么要合成事件?和原生事件有什么不同? ❌作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ✅作业78:使一个标签看不见的几种样式及其区别 ✅2024.12 模拟实现promise ✅2024.11 观察者模式 EventEmitter ✅2022.08 包管理工具 ✅2024.09 第八章 对象、类和面向对象编程 小结 ✅2024.11 第十七章 事件 ❌作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口) ✅作业53:胜利者一无所获 说的是什么 ✅2024.10 怎么实现跨域 ❌2021.07 bind、apply/call三者异同+apply/call实现bind ❌2021.07 数组扁平化(一层、全部展开、指定深度) ❌2021.04 05-3 白屏优化? ❌作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的 ✅2024.05 rem是基于什么原理进行适配的? ❌2020.07 对象分类、Promise按序执行、实现map ❌2019.10 两栏,左边固定,右边自适应的布局 ✅2019.10 三栏,两边固定,中间自适应

LTN ①③ - 76题

2025.1.7 开始

(LTN1 盒子) 2025.1.10-1.17 - 21h31min

✅作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现? ✅作业25:简单写下 请求创建过程 ✅作业49:牛顿三大定律、热力学两大定律 ✅作业54:举杯邀明月,对影成三人。的上一句 ✅作业74:起舞弄清影 是那首诗?谁写的?下一句是什么? 作业36:多分支项目的大致情况 作业37:大盘的大致情况 作业38:部署两个项目的大致情况 ✅作业55:flex 常见缩写 ❌作业58:React和Vue是怎么描述UI的 ✅作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ✅作业61:常见的数据结构有哪些 ❌作业62:fiber架构是什么?优点有哪些? 是怎么实现的? ❌作业63:React渲染怎么实现的?涉及到哪些生命周期/hooks?以 const [count, setCount]=useState(0)具体说明 ❌作业64:Vue渲染怎么实现的? ❌作业65: ①computed/watch/methods 的区别? ②父子组件挂载顺序 ③vue组件中的data为什么是函数 ④ 常见生命周期 ❌作业66:React如何处理错误 ❌作业67:React 怎么做的性能优化? ❌作业68:React hooks 的原理是什么?useEffect useState模拟实现 ❌作业70:Vue 和 React 的 Diff 算法比较 ❌作业71:React为什么要合成事件?和原生事件有什么不同? ❌作业72:react 的 声明周期有哪些,在不同生命周期中做什么事情? ✅作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅作业75:React 组件代码表达的是什么?hook怎么写才好,自定义hook会共享状态么?组件和hook的返回值有什么不同?在渲染时,他们是怎么个顺序? ✅作业76:显卡的作用? ✅作业77:重绘和重排是什么?有什么区别? ❌作业78:使一个标签看不见的几种样式及其区别 ✅作业79:地球四季的成因是什么? ❌2024.12 模拟实现promise ❌2024.11 观察者模式 EventEmitter ❌2022.08 包管理工具 ✅2021.06 跨页面通信 ✅2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击 ✅2021.05 29 WebSocket ✅2021.04 25 HTTP/2 特性 ✅2020.03 模块化 ✅2019.07 数组去重(算法整理) ✅2019.07 显示省略号 ✅2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝? 红宝书需复习章节: ✅2024.09 第六章 集合引用类型 小结 ❌2024.09 第八章 对象、类和面向对象编程 小结 ✅2024.10 第九章 代理与反射 小结 ✅2024.10 第十章 函数 小结 ✅2024.10 第十二章 BOM 小结 ✅2024.10 第十四章-第十五章 DOM、DOM扩展 ✅2024.11 第十六章 DOM2和DOM3 ❌2024.11 第十七章 事件

// 2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?
- 第二遍 -
基本类型深拷贝浅拷贝都是复制值的副本修改新值不会影响原值
引用类型浅拷贝复制的引用地址修改新值会影响原值深拷贝复制的引用地址指向的数据修改新值不会影响原值
[...arr] arr.concat() arr.slice() Array.from(arr) {...obj} Object.assign({}, obj)
Json.parse(Json.stringify(obj))
const isObj = (t) => {
    const type = typeof t;
    return t !== null &&(type === 'function' || type === 'object');
}
function specificFun(t, type) {
    switch(type) {
        case '[object Date]':
            return new Date(t.getTime());
            break;
        case '[object RegExp]':
            return new RegExp(t.source, t.flags);
            break;
        case '[object Symbol]':
            return Symbol.for(t.description);
            break;
        default:
            const ctor = t.constructor;
            return new ctor(t);
    }
}
function clone(target, m = new WeakMap()) {
    if(!isObj){
        return target;
    }
    const type = Object.prototype.toString.call(target);
    const specificTypes = ['Map', 'Set', 'Array', 'Object'].map(it => `[object ${it}]`);
    if(!specificTypes.includes(type)){
        return specificFun(target, type);
    }
    const ctor = target.construcor;
    const res = new ctor();
    if(m.has(target)) {
        return target
    }
    m.set(target, res);
    
    if(type === '[object Map]') {
        target.forEach((value, key) => {
            res.set(key, clone(value, m));
        })
    } else if(type === '[object Set]') {
        target.forEach(it => {
            res.add(clone(it, m));
        })
    } else if(Array.isArray(target)) {
        target.forEach((it, index) => {
            res[index] = clone(it, m);
        })
    } else {
        for(let i in target) {
            res[i] = clone(target[i], m)
        }
    }
    return res;
}

- 第一遍 -
对于基础类型的数据深拷贝和浅拷贝都是数据的副本修改新的数据不会影响原来的数据
对于引用类型的数据浅拷贝拷贝指向数据的地址修改后会影响原有数据深拷贝的是引用地址指向的数据修改新数据并不会影响原数据
浅拷贝[...arr] Array.from(arr) arr.concat() arr.slice() {...obj} Object.assign({}, obj)
深拷贝Json.parse(Json.stringify(obj))
function isObj(t) {
    const type = typeof t;
    return (t !== null) && (type === 'function' || type === 'object');
}
function handleNormal(t, type) {
    switch(type) {
        case '[object Date]':
            return new Date(t.getTime());
            break;
        case '[object RegExp]':
            return new RegExp(t.source, t.flag);
             // 1、正则类型的 t.flags
            break;
        case '[object Symbol]':
            return Symbol.for(t.description);
            break
        default:
            const ctor = t.constructor;
            return new ctor(t);
            break;
    }
}
function clone(target, m = new WeakMap()) {
    if(!isObj(target)) {
        return target;
    }
    
    const deepType = ['Map', 'Set', 'Array', 'Object'].map(it => `[object ${it}]`);
    const type = Object.prototype.toString.call(target);
    if(!deepType.includes(type)) {
        return handleNormal(target, type);
    }
    
    const ctor = target.constructor;
    let res = new ctor();
	if(m.has(target)) {
        return target
    }
    m.set(target, res);
    
    if(type === '[object Set]') {
        target.forEach(it => {
            res.add(clone(it, m));
        })
    }
    if(type === '[object Map]') {
        target.forEach((value, key) => {
            res.set(key, clone(value, m));
        })
    }
    if(Array.isArray(target)) {
        for(let i = 0, len = target.len; i<len;i++) {
            res[i] = clone(target[i], m);
        }
    } else {
        for(let i of target) {
             // 2、 object 不是可迭代对象,不能使用 for...of,这里应该改为 for...in
            res[i] = clone(target[i], m)
        }
    }
    return res;
}
// 2019.07 显示省略号
- 第二遍 -
.line {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
.lines {
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    padding: 8px;
    line-height: 30px;
    height: 82px;
}

- 第一遍 -
.line {
    overflow: hidden;
	white-space: nowrap;
    text-overflow: ellipsis;
}
.lines {
    overflow: hidden;
    display: -webkit-box;
    -webkit-box-direction: vertical;  // 『不是 direction 是 orient: -webkit-box-orient: vertical;』
    -webkit-line-clamp: 3;
    // 解决 padding 带来的露出问题
    line-height: 30px;
    height: 90 - padding距离;
}
// 2019.07 数组去重(算法整理)
- 第二遍 -
arr = [1,1,2,2,2,3];
1set: [...new Set(arr)]  // ⭐️ Array.from(new Set(arr))
2filter+indexOf: arr.filter((it, index) => index === arr.indexOf(it))
3hash:
function unique(arr) {
    let hash = {};
    return arr.filter((it) => hash[it] ? false : hash[it]=true);
}

- 第一遍 -
// ⭐️ 125最重要,其实 4 和 5 是一个逻辑, 3 和 2 很像
1new Set([...arr]) // ❌ [...new Set(arr)] Array.from(new Set(arr))
2arr.filter((it, index) => arr.indexOf(it) === index);
5function unique(arr){
    let hash = {};
	return arr.filter(it => hash[it] ? false :  hash[it] = true)
}
3function unique(arr) {
    return arr.reduce((pre, cur) => {
        if(!pre.includes[cur]) {
            pre.push(cur);
        }
        return pre;
    }, [])
}
4function unique(arr) {
	let hash = {};
    let res = [];
    arr.forEach(it => {
        if(!hash[it]) {
            res.push(it);
            hash[it] = true;
        }
    })
    return res
}
// 2020.03 模块化
- 第二遍 -
CommonJS同步加载适用于服务端编码输出的是值的拷贝运行时输出require+exports
AMDRequireJS,异步加载并行加载依赖前置加载完了立即执行调用无法按序执行
	define(['a.js', 'b.js'], function(a, b) { a.cb();b.cb() })
CMDSeaJS,异步加载并行加载依赖就近加载后按序执行
	define(function(require, exports, module) { let a = require('a.js'); a.cb(); })
ES6模块化异步加载是客户端和服务端的通用解决方案输出的是值的引用编译时输出import default as export from

- 第一遍 -
CommonJS同步加载适用于服务器端输出的是值的拷贝运行时加载require + exports
AMD异步加载可以并行加载多个模块依赖前置 define(['a.js', 'b.js'], function (a, b) {})
 // 少写了 :加载模块后直接执行,无法保证执行顺序
CMD异步加载可以并行加载多个模块依赖就近 define(function(require, exports, module){
    let a = require('a.js'); a.callback();
})
 // 少写了 :加载后,直到调用才按需执行
ES6模块化异步加载是客户端和服务端的通用解决方案输出的是值的引用编译时加载 import as from default export
// 2021.04 25 HTTP/2 特性
- 第二遍 -
在语义上完全兼容在语法上大力改造
压缩头部信息使用 HAPCK 字典表算法
采用二进制帧 - 双向传输序列虚拟的流 - 一个连接可以处理多个请求响应多个请求响应之间没有了顺序关系 - 解决了队头阻塞的问题
服务端推送数据
设置请求优先级
安全性升级使用HTPPS

- 第一遍 -
和HTTP/1.1相比语义层面完全兼容语法层面大力改造
1压缩头部信息采用 HPACK 字典表算法
2不再使用纯文本传送而是二进制帧 - 双向传输序列 - 形成虚拟的流在一个连接上可以处理多个请求响应多路复用),多个请求响应之间没有了顺序解决了队头阻塞
3设置请求优先级
4采用HTTPS
  // 少写一个 5、服务器推送
// 2021.05 29 WebSocket
- 第二遍 -
全双工双向通讯协议由于 HTTP/1.1 的请求-应答模型限制无法实现实时通讯
 HTTP 差异
 在语义语法上完全不兼容没有同源协议的限制使用二进制帧
 不再使用 ip+端口号 形式发现服务而是 URI
 跑在 HTTP 基础上GET 请求是发送 Connection:Update; 如果响应中带有 update:websocket; 则升级为 websocket
// ⭐️ 协议名称改为 ws:80 wss:443

- 第一遍 -
WebSocket和HTTP/1.1在语法语义上完全不相同HTTP/1.1因为请求-应答的模式无法实现实时通讯
 // 1、学名:全双工 - 双向通信协议
使用不同的协议头 ws:80 wss:443
使用不同的识别方式websocket使用的是URIHTTP使用的是IP+端口号
websocket使用二进制帧不受同源协议的限制
websocket跑在HTTP协议之上使用GET请求进行连接请求头中携带Connection:upgrade如果响应头中返回Upgrate:websocket 则建立连接
// 2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击
- 第二遍 -
网络攻击的根本是因为浏览器给同源协议开的两个后门:①可以引入第三方资源->XSS可以请求第三方数据->CSRF
XSS 跨域脚本攻击在前端页面注入恶意代码以修改界面或者服务端数据存储反射映射
解决方法:①纯客户端渲染服务端对数据进行转义和过滤
渲染时禁止DOM执行js代码例如 src/href 标签属性
 cookie 设置 HttpOnly只有http协议能够访问cookie浏览器 js 引擎会禁止访问 document.cookie
CSP内容安全策略设置白名单规定那些资源是可以信任的Content-Security-Policy 域名
// ⭐️ CSP 是从客户端角度讲:设置那些域名的资源是可以被信任的 srcipt-src 'self'
// ⭐️ CORS 是从服务端的角度讲:设置那些域名可以访问这个资源 Access-Control-Allow-Origin
CSRF 跨站请求伪造仿造用户信息获取服务端信任修改服务端数据
解决方法:①校验请求头中的 referer 确认请求来源
 cookie 设置 samesite只有当前域名能够访问
在请求中校验第三方请求无法伪造的 csrf token
验证码

- 第一遍 -
网络攻击的根本原因是浏览器给同源策略开的两个后门:①可以引入第三方数据 通过CORS向第三方资源发起请求
 // 1、可以引用第三方资源
XSS跨域脚本攻击定义-在前端页面中恶意注入恶意代码修改服务器数据
解决方案 纯前端渲染服务端对输入存储进行转义过滤
 cookie 设置 httpOnly只有 http 协议可以访问 cookiejs 引擎会禁止 js  cookie 的访问例如document.cookie
浏览器提供了白名单CSPcontent-security-policy 设置的是可以访问该资源的域名
 // 2、少了一项:在渲染时禁止标签执行JavaScript的代码,例如a标签script的标签
CSRF跨站请求伪造攻击定义-冒用用户的cookie信息获取服务器的信任修改服务器数据
对请求来源进行校验例如请求头中的 referer
开启 cookie 中的 samesite 配置禁止其他非同源页面对 cookie的访问
CSRF token在请求中校验第三方无法模拟的 token
// 2021.06 跨页面通信
- 第二遍 -
同源页面
	- localStorage只能处理非当前页面的传值[页面A传值 localStorage.setItem('a', 1); 页面B接收 window.onstorage=()=>{}]
	- new BroadCastChannel[页面A传值 let b1 = new BroadCastChannel('b1'); b1.postmessage('aa'); 页面B接收 let b1 = new BroadCastChannel('b1'); b1.onmessage=()=>{}]
非同源页面
	- postMessage(H5 API)[页面A传值 let win = window.open('xxx');win.postmessage('aaa'); 页面B接收 window.onmessage=()=>{}]
	- iframe origin 设置成同源域名使用同源页面的两个方法通信
    
- 第一遍 -
同源页面
1localStorage:只能处理非当前页面
发送 localStorage.setItem(key, value) + 接收 window.onstorage
2BroadChannel: // 构造函数名字记错了:BroadCastChannel
发送 let b = new BroadChannel('one'); b.postMessage(value);
接收 创建同名频道接收数据 b.onmessage
非同源页面
3postMessage(H5 API)
发送 let win = window.open(url); win.postMessage(value)
接收 在打开的页面中直接获取 window.onmessage
4iframe origin 设置为同源然后使用方法1/2
// 作业79:地球四季的成因是什么?
地球绕着太阳公转轨迹是椭圆的但是四季的成因和太阳与地球的距离没啥关系
主要是太阳直射点位置 + 夹角(大气层厚度)
⭐️ // 直射点的移动 + 太阳照射时间的长短;夹角,与地面角度的改变,包括改变了太阳辐射面积 + 经过大气的路程衰减的辐射能量强度
// 作业77:重绘和重排是什么?有什么区别?
浏览器渲染流程解析DOM-计算样式表-布局树 layout-分层树-绘制-栅格化-合成
重绘只需要重新修改每层的样式+合成
	- 不涉及到布局修改的样式字体背景边框颜色
重排需要重新计算布局+修改每层的样式+合成
	- 字体大小padding/margin大小激活伪类style样式的修改resize ⭐️ // 1、页面初始渲染 2、可见DOM的增删 3、位置、内容的修改 4、DOM 布局的查询
// 作业76:显卡的作用?
用于渲染帧
分成前后区浏览器从前区获取显示的帧在后区进行渲染直到渲染完成跟前区交换用于显示新帧
⭐️ // 作用:合成新的图像,并将图像保存到后缓存区
// 作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次?
- 第二遍 -
	在合成事件中的 setState 是异步的setState 批量处理机制 react  setState 进行了优化处理例如一个事件处理函数中会把 setState 放到一个队列中函数执行完毕后统一处理 setState更新一次 state触发一次重新渲染
    ⭐️ // 批量更新机制
    ⭐️ // 异步:合成事件 + 生命周期函数;同步:原生事件,例如 addEventListener、setTimeout/setInterval
	在原生事件中的 setState 是同步的不受 react 管控原生事件 setState 一次就会触发一次渲染

- 第一遍 -
合成事件中的 setState 是异步的冒泡到顶层容器才触发
 // 1、除了合成事件,还有「生命周期函数」中都是异步,原因是 React 对多个 setState 调用进行合并,在一个事件处理函数中,收集多个 setState 到一个更新队列,这个事件处理函数结束后,根据合并后的状态,合并为一个更新,触发一次重新渲染
原生事件中的 setState 是同步的不在 React 管控中捕获时触发 
⭐️ // 2、补充:原生事件,比如 addEventListener、setTimeout/setTimeInterval,在其中调用 setState,状态更新会立即生效,并且会触发组件的重新渲染
React 做了 setState 合并在执行过程中收集变更执行完事件函数后再触发 setState 更新
⭐️ // 3、学名「批量更新机制」:为了减少不必要的重新渲染次数
// 作业61:常见的数据结构有哪些
- 第二遍 -
按照结构分
	线性结构链表数组/队列
    非线性结构

- 第一遍 -
 // 按逻辑结构分为:线性结构和非线性结构
结构型类型数组/队列链表
非结构型类型
// 作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例
- 第三遍 -
高阶函数输入参数或者输出结果是函数的函数
高阶组件输入参数是组件
副作用是函数式编程的概念除了返回值函数会造成其他影响例如修改全局变量修改输入变量IO变化等
const a = (MyComponent, required) => {
	return class extends React.Component {
        constructor(props) {
            super(props);
            this.state = { permission: false };
        }
        componentDidMount() {
            const check = check('id');
            this.setState({ permission: check === required })
        }
        render() {
            if(this.state.permission) {
                return <MyComponent {...this.props} />
            } else {
                return <div> You don't have permission. </div>
            }
        }
    }
}

- 第二遍 -
高阶函数就是输入输出的参数是函数。最常见的就是map。
高阶组件就是输入的参数也是组件。
副作用是函数式编程当中的说法,含义是除了return的返回值。在函数当中还会影响到外部值,比如说传入的参数,全局变量。Io下载等
const my = (MyComponents, requiredType) => {
    return class extends React.Component {
        constructor(props) {
            super(props);
            this.state = { permit: false }
        }
        // ❌❌ 1、这里少写了一个 componentDidMount 用于初始化的时候计算 state 中的值
        componentDidMount() {
            const myPermit = check('id'); // 查询
            this.setState({ permit: myPermit === requiredType })
        }
        render() {
            if(this.state.permit) {
                return this.props.children;
                // ❌❌ 2、子组件作为参数传入了  return <MyComponents {...this.props} />
            } else {
                return <div> Error. </div>
            }
        }
    }
}

- 第一遍 -
高阶函数 - 输入参数是函数的函数 arr.map(it => +it),map 就是高阶函数
❌ // 高阶函数 - 以函数作为【输入或者输出】的函数
高阶组件 - 输入参数是组件的组件,做一些包装,可以统一添加监控、报错等功能
❌ // 高阶组件 - 接收组件作为参数,并返回一个新组件 
// - 常用于1、包装一层功能2、属性代理/组合3、动态行为添加
副作用 - 除了返回值,在函数执行过程中会影响其他值、IO等
❌ // 副作用是函数式编程的概念 - 除了函数的返回值外,还对外部环境产生了影响(例如:1、修改全局变量 2、改变输入参数 3、进行IO操作等)
高阶函数-权限校验示例:
class Wrap extends React.Component {
    constructor(props) {
        super();
    }
    render() {
        return <div>{props}</div>
    }
} // 写法是对的,但是传入的 props 不是明确的组件 ❌
// 作业55:flex 常见缩写
- 第三遍 -
flex: none; // 0 0 auto
flex: 1; // 1 1 0
flex: auto; // 1 1 auto
flex: initial; // 0 1 auto

- 第二遍 -
// 所以我错的不是缩写不清晰,是每次 0 0 auto 的简写是 none ,而我写成 0 
flex: 0; // 0 0 auto ❌❌ 「缩写其实是 flex: none;」
flex: initial; // 0 1 auto
flex: 1; // 1 1 0 
flex: auto; // 1 1 auto

- 第一遍 -
flex: 0; // 0 0 auto ❌ 「缩写其实是 flex: none;」
flex: 1; // 1 1 0
flex: auto; // 1 1 auto
flex: initial; // 0 1 auto
// 作业74:起舞弄清影 是那首诗?谁写的?下一句是什么?
苏轼
不知天上宫阙今夕是何年
起舞弄清影何似在人间

/** ⭐️ 欣赏一下原文
《水调歌头 明月几时有》
明月几时有?把酒问青天。不知天上宫阙,今夕是何年。我欲乘风归去,又恐琼楼玉宇,高处不胜寒。起舞弄清影,何似在人间。
转朱阁,低绮户,照无眠。不应有恨,何事长向别时圆?人有悲欢离合,月有阴晴圆缺,此事古难全。但愿人长久,千里共婵娟。
*/
// 作业54:举杯邀明月,对影成三人。的上一句
- 第二遍 -
花间一壶酒独酌无相亲

- 第一遍 -
起舞弄清影独酌无相亲  花间一壶酒独酌无相亲
// 作业49:牛顿三大定律、热力学两大定律
- 第二遍 -
惯性定律加速度定律 a=F/m作用力与反作用力
能量守恒熵增定律

- 第一遍 -
 + 加速度 a=F/m + 作用力与反作用力  惯性定律
能量守恒 + 熵增定律
// 作业25:简单写下 请求创建过程
- 第三遍 -
function request(url){
    const xhr = new XMLHttpRequest();
    xhr.ontimeout = () => {};
    xhr.onerror = () => {};
    xhr.onreadystatechange = function(res) {
        switch(res.readyState) {
            case 0: // 请求没开始
                break;
            case 4:
                if(this.status === 200 || this.status === 304) {
                    console.log(this.responseText);
                }
                break;
        }
    }
    xhr.open('GET', url, true);
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader('animal', 'qiu');
    xhr.send();
}

- 第二遍 -
function request(url) {
    let xhr = new XMLHttpRequest();
    xhr.onerror = () => {};
    xhr.ontimeout = () => {};
    xhr.onreadystatechange = function(res) {
        switch(res.state) { ❌❌ // 1、res.readyState
            case 0: // 请求未开始
                break;
            case 4:
                if(this.status === 200 || this.status === 304) {
                    console.log(this.responseType);
                }
                break;
        }
    }
    xhr.open('GET', url,true);
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader('a', 'qiu');
    xhr.send();
}

- 第一遍 -
function require(url) {
    let  xhr = new XMLHttpRequest(); // 1、实例 ⭐️「使用const」
    // 2、回调
    xhr.onerror = () => {};
    xhr.ontimeout = () => {};
    xhr.onreadyStateChange = function (res) { // ⭐️「原生函数全小写字母 onreadystatechange」
        switch(res.readyState) {
            case 0: // 未开始
                break;
            case 4:
                if(this.status === 200 || this.status === 304) {
                    console.log(this.responseText);
                }
                break; // ⭐️「记得写 break」
        }
    }
    // 3、创建
    xhr.open(url, 'GET', true); // true 表示异步 ❌ 『xhr.open('GET', url, true)』
    // 4、属性设置
    xhr.timeout = 3000;
    xhr.responseType = 'text';
    xhr.setRequestHeader(key, value);
    // 5、发送请求
    xhr.send();
}
// 作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?
- 第二遍 -
arguments 和命名参数始终同步例如修改 arguments[0] 也会改变第一个命名参数
但是 arguments 和设置了默认值的命名参数不同步修改了 arguments[0] 并不能修改到命名参数
数组传入 - arguments=[[1,2,3]]
arguments是数组 - (...arr)使用扩展运算符
收集独立参数 - (first, ...rest)使用扩展运算符 + 最后一个参数rest 是数组
对象字面量 {...obj} - 创建一个新对象拷贝所有可枚举属性
数组字面量 [...obj] - 将可迭代对象一一传入

- 第一遍 -
arguments 默认同步命名参数比如在函数内修改了 arguments[0]如果对应的是命名参数那么值会同步
但是 arguments 不同步设置了默认值的命名参数
数组作为参数 - arguments:[1,2,3]❌『[[1,2,3]]
可以用扩展运算符转换 (...arr)
收集独立参数 (first, second, ...rest) -> rest 是一个数组 只能作为最后一项收集
扩展运算符 - 对象字面量 {...obj} 浅拷贝对象中所有实例属性❌『创建一个新对象并复制所有可枚举属性
扩展运算符 - 数组字面量 [...obj] 将可迭代对象一一返回

(LTN2 盒子) 2025.1.9 - 2h13m

✅作业10:分别用两种方式新建一个映射实例、一个集合实例。 ✅作业29:Promise的进度通知 ✅作业44:怎么能够不把所有事情、语言往心里去 ✅作业45:原生+canvas 显示一张png格式的图片 ❌作业47:2D绘图上下文(坐标原点、基本操作、唯一形状)和3D上下文(坐标原点、定义视口) ✅作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置 ✅作业50:学习的感觉是? ✅作业51: 如何停止过度思考(当下的力量) ✅作业52:道可道,非常道 翻译一下 ❌作业53:胜利者一无所获 说的是什么 ✅作业56:对那些没有 deadline 的事情,怎么解决拖延症 ✅2024.12 模拟实现instanceof ❌2024.10 怎么实现跨域 ❌2021.07 bind、apply/call三者异同+apply/call实现bind ❌2021.07 数组扁平化(一层、全部展开、指定深度) ❌2021.04 05-3 白屏优化? ✅2021.04 02 网络协议 - IP/UDP/TCP协议 ✅2019.07 h5新标签和语义化,块/行内元素

// 2019.07 h5新标签和语义化,块/行内元素
语义化1样式不同 2更好的SEO
h5新标签header footer aside article address nav datalist fieldSet/legend detail time hgroup section
input 新属性placeholder autocomplete autofocus accesskey required multiple
input type 新属性tel email url number date time datetime week month range search
块元素: div table h1-h6 td ol li ul dd dt table-caption p // ⭐️ tr dl caption address hr
行内元素: span br i u a strong em select textarea button // ⭐️ b label
行内块元素: input img td
// 2021.04 02 网络协议 - IP/UDP/TCP协议
应用层HTTP
传输层TCPUDP - 定位某一个应用端口
网络层IP - 定位某一台机器 ip
网际链路层MAC - 底层在网络中定位域
data 传输流程 data]TCP -> data]TCP]IP ->....-> data]TCP -> data
// 2024.12 模拟实现instanceof
function fakeInstanceOf(target, Ctor) {
    let proto = Object.getPrototype(target); // 等价于 target.__proto__
    while(proto) {
        if(proto === Ctor.prototype) {
            return true;
        }
        proto = Object.getPrototype(proto);
    }
    return false;
}
// 作业56:对那些没有 deadline 的事情,怎么解决拖延症
deadline 是第一生产力
但是人生很多事情是不去尝试就无法推进的比如创业比如换工作
但是要意识到人类出生都是向死而生的我们并没有永恒的生命如果你不做自己想做的事情那就又少活了一天
// 作业52:道可道,非常道 翻译一下
如果真理是可以被说出来的那它就不是永恒不变的道了
形容语言的局限性很多道理难以言传
// 作业51: 如何停止过度思考(当下的力量)
思考是一种上瘾行为我们要是沉迷于思考就失去了对时间的控制
解决方法抽离出来看到自己脑海中的思想get it不跟着走let it go回到当下
// 作业50:学习的感觉是?
学如飞鸟过脑内了无痕
// 作业48: input 和 textarea 对比(宽度设置,初始值设置);option 标签的初始值设置
input单行标签size 设置宽度单位为字符数value 设置初始值maxLength 设置最大长度
textarea多行cols 设置宽度单位为字符数标签中间的内容就是初始值rows 设置高度
option 初始值有 value  value  没有 value 取标签中间内容
// 作业45:原生+canvas 显示一张png格式的图片
html 部分<canvas width=200 height=200 id='drawing'></canvas>
js 部分
const drawing = document.getElementById('drawing');
if(drawing.getContext) {
    const imgURI = drawing.toDataURL('image/png');
    const dom = document.createElement('img');
    dom.src = imgURI;
    ducoment.body.appendChild(dom);
}
// 作业44:怎么能够不把所有事情、语言往心里
1It's not about me. 看看别人说这句话的目的
2、It's about me. 被说到心里了才会痛苦 - 真诚地做自己不怕展示自己的软弱学会表述自己的感受不指责
you'll always keep your value. 你不会因外部流言蜚语有任何改变,你一直是你
// 作业29:Promise的进度通知
class MyPromise extends Promise {
    constructor(executor) {
        const handlers = [];
        super((resolve, reject) => {
            return executor(resolve, reject, (notes) => {
                handlers.forEach(it => it(notes));
            })
        });
        this.handlers = handlers;
    }
    notify(fn) {
        this.handlers.push(fn);
    }
}
const p1 = new MyPromise((resolve, reject, notifyCb) => {
    function count(n) {
        if(n>0) {
            notifyCb(`${n*20} remained`);
            setTimeout(() => count(n - 1), 0);
        } else {
            resolve()
        }
    }
    count(5);
})
p1.notify((it) => console.log(`${it} ---`));
p1.then(() => console.log('completed'));
// 作业10:分别用两种方式新建一个映射实例、一个集合实例
const m1 = new Map([['a', 1], ['b', 2]]);
const m2 = new Map().set('a', 1).set('b', 2);
const s1 = new Set([1, 2, 3]);
const s2 = new Set({*[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
}})

(LTN3 盒子)2025.1.8 - 1h56min

❌作业13:什么是原型链?原型链继承、组合继承、寄生组合式继承,分别有什么优缺点?分别是怎么实现的 ✅作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器 ✅作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码。 ✅作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接? ✅作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义? ❌2024.05 rem是基于什么原理进行适配的? ✅2021.07 柯里化函数实现 ❌2020.07 对象分类、Promise按序执行、实现map ❌2019.10 两栏,左边固定,右边自适应的布局 ❌2019.10 三栏,两边固定,中间自适应 ✅2019.07 引用css,link和@import的区别 ✅2019.07 隐式转换:{}+{}=?

// 2019.07 隐式转换:{}+{}=?
'[object Object][object Object]'
// 2019.07 引用css,link和@import的区别
linkxml标签没有兼容问题可以处理 rss 等其他事务浏览器可以解析到预加载js可以正常修改DOM
@importcss2.0 提出低版本需要兼容代码只能加载 css遇到再下载由于加载顺序问题js很难修改到样式
// 2021.07 柯里化函数实现
function curry(fn, ...args1) {
    if(args1.length >= fn.length) {
        return fn.apply(this, args1); // ⭐️ 这里可以直接写成 return fn(...args1)
    } else {
        return (...args2) => { // ⭐️ 函数体只有一条指令,可以省略花括号{}
            curry(fn, ...args1, ...args2);
        };
    }
}
// 作业33:Cookie 怎么设置有效期?有优先级吗?HttpOnly 是什么含义?
Cache-control: max-age=2000; // 单位是秒(优先级高)
Expires: deadline; // 具体时间
HttpOnly 的含义是只有 HTTP 协议可以访问当前 Cookie浏览器不可以访问js引擎会禁用方法例如 document.cookie
// 作业31:什么是长连接?长连接的属性怎么写?哪个版本开始启用长连接?
长连接浏览器和服务器三次握手建立 TCP 连接后不断开可以处理多个请求响应
Connection: keep-alive
HTTP/1.1 后默认开启长连接
// 作业30:简单写出一个请求头和响应头。HTTP/1.1中唯一要求请求头必须提供?写出常见状态码
请求头 GET /index HTTP/1.1
响应头 HTTP/1.1 200 OK
HTTP/1.1中唯一要求请求头必须提供 Host
常见状态码
200 OK 请求成功
204 No Content 没有响应体
301 Moved Permanently 永久重定向
302 Moved Templately 临时重定向
304 Not Modified 缓存内容不变可以继续使用
If-none-match/ETagIf-modified-since/Last-modified
404 Not Found 请求资源在服务器上找不到
400 Bad Request 请求头有语法错误
403 Forbidden 请求资源禁止访问
500 服务器错误
// 作业14:Proxy 的原型是什么?怎么撤销代理关联?是否可逆?写几个常见的代理模式对应的捕获器
Proxy 的原型是 undefined, 所以不能使用 instanceof 来查询代理对象会报错
const { proxy, revoke } = Proxy.revocable(target, handler); revoke();
撤销关联不可逆
const handler = {
    get(...args) { return Reflect.get(...args) },
    set: Reflect.set,
    has // 对应 in 操作符
    apply // 对应执行函数
    constructor // 对应实例化 new 操作符
}

(LTN4盒子) 2025.1.7 - 45min

✅作业15:类由哪些组成,不同定义的内容在继承时有什么特点?类是怎么实现继承的? ✅2021.07 怎么判断数组类型?4种方法 ✅2021.04 03 为什么很多网站第二次打开速度会很快 ✅2019.07 for循环+计时器,如何实现i按序输出 ✅2019.06 函数参数传值修改 ✅2019.06 检测数据类型 + 布尔转换为0的值

// 2019.06 检测数据类型 + 布尔转换为0的值
typeof 检测基本数据类型为字符串对于 ArrayObject 都返回 'object'对函数返回'function'
instanceof 检测对象的原型链中是否有指定构造函数的原型对象一般用于检测引用类型
Object.prototype.toString.call 可以将对象转换成 '[object 类型]' 的字符串(可被改写)
constructor 指向构造函数(可被改写)
Array.isArray 可以准确检测数组
布尔值转换为false的值''空字符串undefinednullNaN0false
// 2019.06 函数参数传值修改
[1,2,3]
{a:1}
100 100 100 Error 55 100
// 2019.07 for循环+计时器,如何实现i按序输出
1let - 会为每次的循环保留上下文
for(let i = 0; i<10; i++) { setTimout(() => console.log(i), 0) }
2立即执行(闭包)
for(var i = 0; i<10; i++) { 
	(function(a) { setTimout(() => console.log(a), 0) })(i)
}
3setTimout 参数(闭包)
for(var i = 0; i<10; i++) { setTimout(() => console.log, 0, i) }
// 2021.04 03 为什么很多网站第二次打开速度会很快
1DNS缓存 - 查询一次后在本地会缓存
2同源站点可以复用渲染进程HTTP/1.1 后TCP连接都是默认长连接节省了建立连接的时间
3GET 类型的请求在第一次打开后浏览器会默认缓存(也可以自定义设置)第二次可以直接复用缓存
4还有一些资源在第一次请求后本地有缓存但缓存过期会触发协商缓存请求如果资源没过期-接口返回304浏览器可以直接使用本地资源刷新缓存时间而不需要等待接口返回内容重新下载
// 2021.07 怎么判断数组类型?4种方法
1Array.isArray() - 最准确
2instanceof - 查找对象的原型链上是否有指定构造函数的原型对象在多个全局上下文时判断可能有误
3Object.prototype.toString.call(obj) === '[object Array]' - 可被改写
4obj.constructor === Array - 可被改写
typeof 主要用于检测基础类型检测数组会得到 'object'无法区分
// 作业15:类由哪些组成,不同定义的内容在继承时有什么特点?类是怎么实现继承的?
类由 constructorstatic 静态函数原型函数
constructor用于创建对象的构造函数每个子类继承时都被实例化
static 静态函数只属于当前类的方法不被继承
原型函数子类可以继承的方法
通过 extends 关键词继承 constructor和原型函数在子类的 constructor 可以通过 super 调用父类构造函数

LTN ①② 错题重做 2024.12.31

(错题重做,做对了不记录,记录二次错)

LTN ①②

2024.12.26 - 12.30 做LTN1 - 123+min

✅作业17:对斐波那切数列进行尾调用优化 ✅作业20:浏览器打开一个 URL 发生了什么 ✅作业26:给出一个场景,要求A、B请求执行结束后,再执行C请求,其中A、B请求同时开始,怎么实现 ✅作业34:写出强缓存、协商缓存的流程及相关属性 作业36:多分支项目的大致情况 作业37:大盘的大致情况 作业38:部署两个项目的大致情况 ❌作业55:flex 常见缩写 ✅作业57:“假装成功”的意义是什么
❌作业58:React和Vue是怎么描述UI的 ❌作业59:什么是高阶函数?什么是高阶组件?什么是副作用?简单举例 ❌作业60:虚拟DOM在 Vue 和 React 中的使用?分别有什么提升性能的方法? ❌作业61:常见的数据结构有哪些 ❌作业62:fiber架构优点? 是怎么实现的? ❌作业63:React渲染怎么实现的 ❌作业64:Vue渲染怎么实现的? ❌作业65: ①computed/watch/methods 的区别?②created和mounted之间的区别 ③父子组件挂载顺序 ④vue组件中的data为什么是函数 ❌作业66:React如何处理错误 ❌作业67:React 怎么做的性能优化? ❌作业68:React hooks 的原理是什么? ❌作业69:useEffect useState模拟实现 ❌作业70:Vue - patch 和 React 的reconciliation - Diff 算法 ❌作业71:React为什么要合成事件?和原生事件有什么不同?原理是什么? ❌作业72:react 的 生命周期有哪些,在不同生命周期中做什么事情? ❌作业73:react 的 setState 是同步还是异步?为什么React有时候有两次 setState,却只执行一次? ✅2024.12 模拟实现jsonp ❌2024.12 模拟实现promise ❌2024.11 观察者模式 EventEmitter ✅2021.09 Object属性排序、与Map的区别 ✅2021.07 防抖节流 ✅2021.07 常见Webpack问题 ❌2021.06 跨页面通信 ✅2021.06 BFC特点、触发、存在/解决的问题 ❌ 2021.05 XSS跨域脚本攻击 和 CSRF跨站请求伪造攻击 ❌2021.05 29 WebSocket ✅2020.03 改变原数组+结束循环+性能排序 ❌2019.07 深拷贝和浅拷贝的区别?怎么实现深拷贝?

// 2021.07 常见Webpack问题
/** 1、webpack的几个核心概念理解:Chunk、Module、plugin
2、常见配置项:entry、output、module、resolve等
自定义 loader 怎么配置?
3、Code Splitting 和 Tree Shaking 的区别?懒加载怎么实现?
4、html-webpack-plugin 作用?
5、sourceMap不同环境的区别?怎么开启配置?
6、热更新怎么实现?
7、webpack原理/执行过程?
开发插件的桥梁?*/
1Chunk由多个Module组成
Module万物皆模块
plugin在构建过程中特定时机注入代码执行
2entry[配置入口文件]: 'src/main.js'; 或者 { main: 'src/main.js', sub: 'src/sub.js' }
output[配置输出]: { filename: '[name]-[hash].js', path: path.resolve(_dirname, 'dist') }
module[依赖解析转换配置]: { rules: [ { test: '/.\js/', use: ['style-loader', 'css-loader', 'sass-loader'] }, { test: '/.\css/' } ] }
resolve[依赖查找配置]:{ 
	alias: { "@c": 'src/component' },
    extensions: ['jsx', 'js', 'josn'],
    modules: [path.resolve(_dirname, 'src/my'), 'node_modules']
}
配置自定义 loader 的两种方法
如上 resolveModules 中添加自定义 loader 的路径 + 然后直接在 module.rules 直接引用即可 use:['style-loader', 'my-loader']
 use 中具体地址引用use:['style-loader', path.resolve(_dirname, 'src/my/my-loader.js'), 'sass-loader']
3coding splitting 按需加载适用于单页应用在加载首页时以entry配置入口分组只加载当前使用的代码其他的延迟/操作后再加载提高渲染效率
⭐️ // 1、开启方式是 splitChunk 插件配置 - 加快响应速度
tree shaking 按需打包按照 entry 为入口递归查找实际被引用的代码进行打包其他部分不进行打包减小包大小
懒加载利用 code splitting 按需加载首次渲染需要的代码提高渲染效率
4html-webpack-plugin webpack 全部打包完成后 html 模版中将打包后文件引入方便直接访问
5生产环境devtool:hidden-source-map; 不引入sourcemap
本地/测试以缓解devtool:cheap-eval-source-map; 不提供列信息方便代码调试
6、①在启动 webpack 的时候通过 dev-server 在内存中启动一个服务同时注入 HMR 相关代码使用 websocket 协议 和浏览器保持实时通讯
有代码更新时通过监听 webpack  compiler done 事件在编译结束后得到 mainfest.json  chunk.js 文件通过 websocket  mainfest 配置文件主动推送给 浏览器
浏览器在 mainfest 中获得 ChunkId 等信息发起 ajax 请求向内存中的服务获取 chunk.js 的模块代码文件
执行 window.webpackHotUpdate->执行hotApply热更新函数替换原有模块代码不用刷新整个页面模块代码通过 webpack.__require__来执行
⭐️ // 2、第4步,其实就是开始 render
7webpack原理/执行过程
初始化配置参数将shell脚本和配置文件中的配置整合得到初始化配置
利用得到的参数初始化 webpack Compiler加载所有插件执行 .run 开始构建
根据entry配置为入口依次执行 module.rules 配置的loader转换解析递归遍历所有依赖模块
得到转换后的文件和依赖关系按照entry配置组合成一个一个chunk输出到下载文件列表
根据 output 输出配置将列表中的文件输出到指定位置
Compiler代表了webpack的生命周期整个构建流程中就一个
Compilication代表了一次构建更新一次代码就创建一个
// 作业60:虚拟DOM在 Vue 和 React 中的使用?分别有什么提升性能的方法?
 // 1、主要区别从 虚拟DOM 的「具体实现」体现
React
1先通过渲染得到一个新的 VDOM  
 // 2、需要详细描述一下:React使用JSX描述UI,JSX被编译为React createElement方法 -> 执行后返回一个由 React Element描述的UI - FiberNode构成,作为新的VDOM
2和当前的 VDOM 比较得到需要更新的节点再修改真实 DOM 
 // 3、当前的 VDOM 也是 FiberNode,比较,计算出 FiberNode 描述的 UI 更新 - Diff
可以减少对真实 DOM 的操作和访问减少回流重绘消耗的性能
由于 React 使用 JSX 灵活性比较大编译时优化很难实现在运行时做了很多优化策略 
 // 4、运行时优化是对的,主要策略是 fiber架构(时间分片 + 优先级调度)

Vue
1 React 一样也是通过渲染得到新的 VDOM 
 // 5、一样的问题,要详细描述具体实现:Vue 使用模版语法描述UI,编译为 render 函数 -> 执行后返回 VDOM 描述的 UI「注意这里的差异」
2和当前 VDOM 树比较再修改真实节点
 // 6、描述是对的,专业一点说这里是 patch 的过程:Diff 主要是用的是 双向指针 + 静态解析
由于 Vue 使用的是模版语法在编译时可以通过 AOT 进行优化

编译分为两种
 // 7、简单比较一下两种编译方式:JIT 应用首次加载会比AOT慢,体积也会更大
AOT 预编译标记静态节点运行时直接执行速度比较快占用内存空间小
 // 8、描述错误:编译后,下一步是「代码构建」-也就是准备执行,主语是「宿主环境」获得,宿主环境获得的是编译后的代码,可以直接执行 - 例如Vue3的静态/动态节点分析/跳过
JIT 即时编译运行时需要先编译再执行代码占用的空间会比较大因为会有编译相关的代码需要存储
 // 9、少写了:代码依旧是在「宿主环境」中执行 - React

2024.12.25 +1天 做LTN2 - 140min

✅作业3:迭代器原理 ❌作业25:简单写下 请求创建过程 ✅作业41:TED 如何解决焦虑 ❌作业49:牛顿三大定律、热力学两大定律 ❌ 作业54:举杯邀明月,对影成三人。的上一句 ❌LTN2.1 显示省略号 ✅LTN2.2 position属性 - 都是什么占位?相对于什么定位 ❌LTN2.3 HTTP/2特性 ✅LTN2.4 红宝书19章 ✅LTN2.5 红宝书18章 ❌LTN2.6 模块化 ❌LTN2.7 包管理工具 ✅LTN2.8 给定时间切换状态 ❌LTN2.9 数组去重 ✅LTN2.10 数组乱序 洗牌算法

// LTN2.10 数组乱序 洗牌算法
// 从最后一个元素开始,从数组中随机选出一个位置,交换,直到第一个元素。
function chaos(arr) {
    const length = arr.length;
    for(let i = length -1 ; i >= 0; i--){
		let random = Math.floor(Math.random()*length);
        [arr[random], arr[i]] = [arr[i], arr[random]]
    }
    return arr
}
// while 循环来写
function chaos(arr) {
	let cur = arr.length - 1;
    while(cur >= 0) {
        let random = Math.floor(Math.random()*arr.length);
        [arr[random], arr[i]] = [arr[i], arr[random]];
        cur --;
    }
    return arr
}
// LTN2.8 给定时间切换状态
async function (promise, time) {
    const timePro = new Promise((resolve, reject) => { setTimout(reject, time, new Error('time out!')) });
	return await Promise.race([promise, timePro])
}
// LTN2.2 position属性 - 都是什么占位?相对于什么定位
static - 标准文档流占位
relative - 标准文档流占位相对定位 // ⭐️「相对自己本身的定位」
absolute - 脱离文档流绝对定位相对于上一个 position  static 的父级元素
fixed - 脱离文档流粘性定位相对视口
sticky - 原本是标准文档流超过一个阈值后变成 fixed
// 作业41:TED 如何解决焦虑
1do it badly - anything worth doing worth doing badly the first time
2给自己一些同情我们都不会和一个天天批评自己的朋友长久交往 - 你也不要做自己这样的朋友
3找到意义 - 即使别人不知道自己一定要认可自己做得事情觉得有价值

- 如何停止过度思考 - 
思考和抽烟喝酒一样是一件上瘾行为 - 想要不上瘾就是允许念头在脑中出现get it then let it go
不跟着念头走回到当下
焦虑是一种对失败的恐惧 - 写下可能导致自己失败的事情 - 每个失败的可能都找到应对方案do it
// 作业3:迭代器原理
迭代器调用 next() 将可迭代对象的值一一返回不用关注对象类型只要是可迭代对象都有默认迭代器 [Symbol.iterator]

2024.12.25 先做LTN4 - 62min

✅作业1:写一个只能被迭代指定次数的类 ✅作业7:原始值包装类型和原始类型在使用 typeof 和 instanceof 进行类型判断时,有什么异同? ✅作业9:数组自带排序 sort() 为什么经常要传比较函数?写一个降序的比较函数。如果数组元素全是数值,请给出简写形式 ✅作业11:写出一个用 yield 实现的递归算法,从0数到指定n ✅作业12:画出一个原型链,到 null 为止 ✅作业8:ES6新增了两个创建数组的静态方法:Array.from()和Array.of()两个有什么区别? ✅作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景 ❌作业16(2):arguments 对象对待命名参数和设置了默认值的命名参数有什么不同?数组作为参数传入函数得到的arguments 是什么?怎么实现arguments是数组?怎么收集独立参数?在对象字面量和数组字面量中,扩展运算符有什么不同的表现?

// 作业10:说出WeakMap/WeakSet和Map、Set的区别,为什么有这两个弱类型,经常用在什么场景
WeakMap/WeakSet 分别是 MapSet 的弱类型特点是键/值只能是对象类型并且在对象是空对象没有引用时垃圾回收程序会清理当前弱类型
目的避免内存泄漏
场景比如监听 DOM 节点如果节点被删除弱类型会被清理
// 作业8:ES6新增了两个创建数组的静态方法:Array.from()和Array.of()两个有什么区别?
Array.from() - 用于将类数组(arguments+Nodelist)/可迭代对象转换成数组类型浅拷贝
Array.of() - 用于将一连串参数转换成数组类型主要用于替代 Array.prototype.slice.call(arguments)
// 作业12:画出一个原型链,到 null 为止
function Person() {}
let person = new Person();
person.__proto__ === Person.prototype

Person.prototype.constructor === Person
Person.prototype.__proto__ === Object.prototype

Object.prototype.constructor === Object
Object.prototype.__proto__ === null
// 作业11:写出一个用 yield 实现的递归算法,从0数到指定n
function * nTimes(n) {
    if(n>0) {
        yield* nTimes(n-1);
        yield (n-1);
    }
}
// 作业9:数组自带排序 sort() 为什么经常要传比较函数?写一个降序的比较函数。如果数组元素全是数值,请给出简写形式
sort函数会把数组中的值转换成字符串后比较比如排序后会出现 [1,10,15,3,5]
// 降序
[].sort((a,b) => {
    return a > b ? -1 : a < b ? 1 : 0
})
// 简写
[].sort((a, b) => b - a)
// 作业7:原始值包装类型和原始类型在使用 typeof 和 instanceof 进行类型判断时,有什么异同?
String Boolean Number
原始类型-使用 typeof v 可以得到具体类型的字符串 VS 使用 v instanceof S/B/N 都是 false
原始值包装类型 - 使用 typeof v 得到 'object'  VS  v instanceof S/B/N 得到 true
typeof 用于判断基础数据类型null array object 都是 'object'函数是 'function'
instanceof 在对象的原型链上查找是否有指定构造函数的原型对象 [Symbol.hasinstance]
// 作业1:写一个只能被迭代指定次数的类
class nTimes{
    constructor(n) {
        this.limit = n;
    }
    [Symbol.iterator](){
        let max = this.limit;
        let times = 0;
        return {
            next() {
                if(times < max) {
                    return {value: times++, done: false};
                } else {
                    return {value: undefined, done: true};
                }
            }
            return() {
                return {value: undefined, done: true}
            }
        }
    }
}
// 答题核心:迭代器返回的是迭代器对象(包含next()、return()) + next 返回一个 done+value组成的对象