LTN做题

Posted by CodingWithAlice on October 1, 2024

LTN做题

LTN ①③ - ①⑥记录

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组成的对象