Skip to content

Commit

Permalink
feat: 0318
Browse files Browse the repository at this point in the history
  • Loading branch information
niaogege committed Mar 18, 2024
1 parent 2841490 commit f631c25
Show file tree
Hide file tree
Showing 11 changed files with 477 additions and 66 deletions.
79 changes: 66 additions & 13 deletions docs/interview/experiences/13202307handwriting.md
Original file line number Diff line number Diff line change
Expand Up @@ -747,28 +747,81 @@ revserLink(node);

```js
class MockPromise {
this.data = undefined;
this.onResolvedCbs = [];
this.onRejectedCbs = []
this.status = 'pending'
constructor(exeFn) {
this.data = undefined;
this.cbs = [];
const resolve = function (val) {
setTimeout(() => {
this.data = val;
this.cbs.forEach((cb) => cb());
if(this.status == 'pending') {
this.data = val;
this.status = 'resolved'
this.onResolvedCbs.forEach((cb) => cb(val));
}
});
};
exeFn(resolve);
const reject = function (reason) {
setTimeout(() => {
if(this.status == 'pending') {
this.data = reason
this.status = 'rejected'
this.onRejected.forEach((fn) => fn(reason))
}
})
}
exeFn(resolve, reject);
}
then(onFulfilled, onReject) {
return new MockPromise((resolve) => {
this.cbs.push(() => {
var val = onFullfilled(this.data);
if (val instanceof MockPromise) {
val.then(resolve);
} else {
resolve(val);
let p2 = new MockPromise((resolve) => {
if(this.status == 'pending') {
this.cbs.push(() => {
var x = onFullfilled(this.data);
this.parsePromise(p2,x, resolve, reject)
});
this.onRejected.push(() => {
let x = onReject(this.data)
this.parsePromise(p2,x, resolve, reject)
})
}
if(this.status == 'resolved') {
try {
let x = onFulfilled(this.data)
this.parsePromise(p2,x, resolve, reject)
} catch(e) {
reject(e)
}
});
}
if(this.status === 'rejected') {
try {
let x = onRejected(this.data)
this.parsePromise(p2,x, resolve, reject)
} catch(e) {
reject(e)
}
}
});
return p2
}
parsePromise(p,x,resolve,reject) {
if(x == p) {
reject(new TypeError('chaining circle'))
}
try {
if(x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch(e) {
reject(e)
}
}
done() {
this.catch((e) => {
console.log('done')
throw e
})
}
}
```
Expand Down
62 changes: 33 additions & 29 deletions docs/interview/experiences/15202309handwriting.md
Original file line number Diff line number Diff line change
Expand Up @@ -1858,45 +1858,48 @@ numToString(360); // 'a0'

## 54.手写实现 lodash.get/lodash.set

### lodash.set

```js
// lodash.set(obj,path,value)
// lodash.get(obj, path)
function flattenObj(obj, res = {}, path = '', isArray = false, ans = '', p = '') {
for (let [k, val] of Object.entries(obj)) {
if (Array.isArray(val)) {
let tmp = isArray ? `${path}[${k}]` : `${path}${k}`;
flattenObj(val, res, tmp, true);
} else if (typeof val === 'object') {
let tmp = isArray ? `${path}[${k}].` : `${path}${k}.`;
flattenObj(val, res, tmp, false);
function lodashSet(obj, path, val) {
let paths = path.replace(/\[(\d+)\]/g, '.$1').split('.');
paths.reduce((cur, pre, index, arr) => {
// 最后一个 直接赋值
if (index == arr.length - 1) {
cur[pre] = val;
return null;
} else if (pre in cur) {
return cur[pre]; // key的值
} else {
let tmp = isArray ? `${path}[${k}]` : `${path}${k}`;
if (p && ans) {
res[p] = ans;
} else {
res[tmp] = val;
}
return (cur[pre] = {});
}
}
return res;
}
function mockLodashGet(obj, path) {
const newObj = flattenObj(obj);
return newObj[path];
}, obj);
}
let obj = {
// test
var obj = {
foo: {
bar: {
baz: 'Hello, World!',
},
},
};
mockLodashGet(obj, 'foo.bar.baz');
lodashSet(obj, 'foo.bar.baz', 'cpp');
```

// function mockLodashSet(obj, path, val) {
// flattenObj(obj, {}, '', false, val, path);
// }
// mockLodashSet(obj, 'foo.bar.baz', 'cpp');
### lodahs.get(get(object: object, path: [never]): never)

```js
function lodashGet(object, path, defaultVal = undefined) {
let paths = path.replace(/\[(\d+)\]/g, '.$1').split('.');
let res = object;
for (let p of paths) {
res = Object(res)[p];
if (res == undefined) {
return defaultVal;
}
}
return res;
}
```

## [55.场景应用题 div 拖拽](https://juejin.cn/post/7282951856514793513?searchId=20240315111348596E579AE59B54ED0ACB)
Expand Down Expand Up @@ -2010,4 +2013,5 @@ getP(); // 3
## 参考

- [字节跳动前端面经(3 年工作经验附详细答案)](https://mp.weixin.qq.com/s/MYfuUSNS7xIAT4RgZIMv0g)
- [](https://juejin.cn/post/7004638318843412493?searchId=20240313143205EB66701739E2F9D6599F)
- [最新的前端大厂面经(详解答案)](https://juejin.cn/post/7004638318843412493?searchId=20240313143205EB66701739E2F9D6599F)
- [「2021」高频前端面试题汇总之手写代码篇](https://juejin.cn/post/6946136940164939813?searchId=2024031814180491A668E2D8A6BD15EEE9#heading-2)
74 changes: 64 additions & 10 deletions docs/interview/experiences/20React.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ react 在面试中举足轻重,列举几个有代表性的问题,背一背.
- 7.Redux 实现的是哪种模式?以及 Mobx 实现的是哪种模式。两者之间有什么区别?
- 8.什么是 JSX?
- 9.userMemo 的用途是什么?它是如何起效的?
- diff 算法相关
- 10.diff 算法相关

- 21.react 强制刷新
- 22.React 组件中怎么做事件代理?它的原理是什么?
- 23.

## 0.fiber

Expand All @@ -42,7 +46,7 @@ fiber 是 react16 中引入的一种新的协调算法。v16 之前,react 使

1.从架构角度看,是 react 对调和算法的重写。v16 之前,react 使用的是名为 Stack Reconciler 的旧调和算法,V16 之后改成 fiber Reconciler,实现虚拟 DOM 增量渲染,让 react 在更新过程变得可控(适时让出 CPU 执行权,将控制器交给浏览器,让位给高优先级任务,等浏览器空闲也就是 idleCallback 时在恢复渲染)

2.从编码角度看,fiber 是一种数据结构,是 Fiber 树结构的节点单位,也就是 React 16 新架构下的"虚拟 DOM"。一个 fiber 就是一个 JavaScript 对象,包含 child/return/sibling 等属性
2.从编码角度看,fiber 是一种数据结构,是 Fiber 树结构的节点单位,也就是 React 16 新架构下的"虚拟 DOM"。一个 fiber 就是一个 JavaScript 对象,包含 **child/return/sibling** 等属性

### React Fiber 是如何实现更新过程可控?

Expand Down Expand Up @@ -82,6 +86,12 @@ useLayoutEffect: 与 useEffect 类似,但效果会在所有 DOM 更改之后

这些 hooks 提供强大的工具,可用于管理状态、处理 side effects 和重用 React 函数组件当中的逻辑。

### React Hooks 在平时开发中需要注意的问题和原因?

- 1)不要在循环,条件或嵌套函数中调用 Hook,必须始终在 React 函数的顶层使用 Hook

- 善用 useCallback/合理使用 useContext

## 2. 虚拟 DOM 是什么?

虚拟 DOM 是 React 中提出的概念,用于为实际 DOM(文档对象模型)创建一个轻量化虚拟表示,并将其存储在内存当中。这是一种用于优化 Web 应用程序性能的编程技术。
Expand Down Expand Up @@ -262,7 +272,7 @@ var someElement = () => {

这就是所谓 JSX,是一种用于简化代码理解和开发表达的语言扩展。

## 9.userMemo 的用途是什么?它是如何起效的?
## 9.useMemo 的用途是什么?它是如何起效的?

useMemo 用于缓存和记忆计算结果。

Expand Down Expand Up @@ -290,15 +300,17 @@ const callbackValue = useCallback(() => computeFunc(paramA, paramB), [paramA, pa

## 11.React Context 是什么?

React Context 是一项功能,提供一种通过组件树传递数据的方法,避免在每个层次上手动传递 props。它允许我们创建一个全局状态,树中的任何组件无论位置如何、均可以访问该状态。当我们需要在未通过 props 直接连接的多个组件之间共享数据时,就可以使用 Context。
React Context 提供一种通过组件树传递数据的方法,避免在每个层次上手动传递 props。它允许我们创建一个全局状态,树中的任何组件无论位置如何、均可以访问该状态。当我们需要在未通过 props 直接连接的多个组件之间共享数据时,就可以使用 Context。

> Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props 属性。
React Context API 包含以下三个主要部分:

- createContext: 此函数用于创建一个新的 context 上下文对象。
- createContext: 此函数用于创建一个新的 **context 上下文对象**

- Context.Provider: 此组件用于向 context 提供值,其中打包有需要访问该值的组件。
- Context.Provider: 此组件用于向 context 提供值

- Context.Consumer 或 useContext hook: 此组件或 hook 负责使用 context 中的值。它可以在上下文提供方内的任意组件中使用。
- Context.Consumer 或 useContext hook: 此组件或 hook 负责消费 context 中的值。它可以在上下文提供方内的任意组件中使用。

通过使用 React Context,我们可以避免 prop 钻取(即在多个层次的组件间传递项目)并轻松管理更高级别的状态,保证代码更具组织性的执行效率。

Expand Down Expand Up @@ -401,6 +413,7 @@ export const getStaticProps = async ({ params: { slug } }) => {
```

2. getServerSideProps: 此方法用于根据每个请求获取数据,并在服务器端预渲染页面。如果需要获取经常更改、或者仅供特定用户使用的数据,则可以使用此方法。(SSR)

3. getStaticPaths: 此方法用于在**动态路由**当中,指定需要在构建时**预渲染的路径列表**,常用于获取带有参数的**动态路由数据**

```js
Expand Down Expand Up @@ -458,7 +471,7 @@ function useRerender() {

## 17.react 中的 diff

在 React 中,diff 算法需要与虚拟 DOM 配合才能发挥出真正的威力。React 会使用 diff 算法计算出虚拟 DOM 中真正发生变化的部分,并且只会针对该部分进行 dom 操作,从而避免了对页面进行大面积的更新渲染,减小性能的开销。在传统的 diff 算法中复杂度会达到 O(n^3)。React 中定义了三种策略,在对比时,根据策略只需遍历一次树就可以完成对比,将复杂度降到了 O(n)
在 React 中,diff 算法需要与虚拟 DOM 配合才能发挥出真正的威力。React 会使用 diff 算法计算出虚拟 DOM 中真正发生变化的部分,并且只会针对该部分进行 dom 操作,从而避免了对页面进行大面积的更新渲染,减小性能的开销。在传统的 diff 算法中复杂度会达到 O(n^3)。React 中定义了三种策略,在对比时,根据策略只需遍历一次树就可以完成对比,将复杂度降到了 O(n)

## 18.setState 同步异步问题

Expand All @@ -468,7 +481,7 @@ function useRerender() {
1.在**合成事件****生命周期钩子**(除 componentDidUpdate) 中,setState 是"异步"的;

2.在 原生事件 和 setTimeout 中,setState 是同步的,可以马上获取更新后的值;
2.在 **原生事件****setTimeout** 中,setState 是同步的,可以马上获取更新后的值;

### 批量更新

Expand Down Expand Up @@ -509,12 +522,14 @@ React 中 Fiber 树的更新流程分为两个阶段 **render 阶段**和 **comm

### 使用 forwardRef

将组件单独封装成组件 TextInput
通过 forwardRef 将组件单独封装成组件 TextInput

```js
// 子组件
const TextInput = React.forwardRef((props, ref) => {
return <input ref={ref} />;
});
// parent
const TextPar = () => {
const inputEl = useRef(null);
const onClick = () => {
Expand Down Expand Up @@ -586,3 +601,42 @@ React.memo 与 PureComponent 的区别:
- React.memo 只能针对 props 来决定是否渲染

> React.memo 的第二个参数的返回值与 shouldComponentUpdate 的返回值是相反的 React.memo:返回  true  组件不渲染 , 返回 false 组件重新渲染。 shouldComponentUpdate: 返回  true  组件渲染 , 返回  false  组件不渲染
## 21.react 强制刷新

component.**forceUpdate**() 一个不常用的生命周期方法, 它的作用就是强制刷新。

官网解释如下:

- 默认情况下,当组件的 state 或 props 发生变化时,组件将重新渲染。如果 render() 方法依赖于其他数据,则可以调用 forceUpdate() 强制让组件重新渲染。

- 调用 forceUpdate() 将致使组件调用 render() 方法,此操作会跳过该组件的 shouldComponentUpdate()。但其子组件会触发正常的生命周期方法,包括 shouldComponentUpdate() 方法。如果标记发生变化,React 仍将只更新 DOM。

通常你应该避免使用 forceUpdate(),尽量在 render() 中使用 this.props 和 this.state。

shouldComponentUpdate 在初始化 和 forceUpdate 不会执行。

## 22.React 组件中怎么做事件代理?它的原理是什么?

React 基于 Virtual DOM 实现了一个 SyntheticEvent 层(合成事件层),定义的事件处理器会接收到一个**合成事件对象**的实例,它符合 W3C 标准,且与原生的浏览器事件拥有同样的接口,支持冒泡机制,所有的事件都自动绑定在最外层上。

在 React 底层,主要对合成事件做了两件事:

- 事件委托: React 会把所有的事件绑定到结构的最外层(根元素),使用统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部事件监听和处理函数。
- 自动绑定: React 组件中,每个方法的上下文都会指向该组件的实例,即自动绑定 this 为当前组件。

## 23.哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?

### 哪些方法会触发 react 重新渲染?

- setState()方法被调用 setState 是 React 中最常用的命令,通常情况下,执行 setState 会触发 render。但是这里有个点值得关注,执行 setState 的时候不一定会重新渲染。当 setState 传入 null 时,并不会触发 render。

- 父组件重新渲染只要父组件重新渲染了,即使传入子组件的 props 未发生变化,那么子组件也会重新渲染,进而触发 render

### 重新渲染 render 会做些什么

- 会对新旧 VNode 进行对比,也就是我们所说的 Diff 算法。
- 对新旧两棵树进行一个深度优先遍历,这样每一个节点都会一个标记,在到深度遍历的时候,每遍历到一和个节点,就把该节点和新的节点树进行对比,如果有差异就生成一个 patch 补丁
- 遍历差异对象,根据 patch 补丁,应用到真实 dom

React 的处理 render 的基本思维模式是**每次一有变动就会去重新渲染整个应用**。 React 将 render 函数返回的虚拟 DOM 树与老的进行比较,从而确定 DOM 要不要更新、怎么更新。当 DOM 树很大时,遍历两棵树进行各种比对还是相当耗性能的,特别是在顶层 setState 一个微小的修改,默认会去遍历整棵树。尽管 React 已经深度优化 Diff 算法,但是这个过程仍然会损耗性能.
31 changes: 28 additions & 3 deletions docs/interview/experiences/21leetcodeA.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,10 +703,12 @@ function mergeKLinks(head) {
## [27.最长上升子序列](https://leetcode.cn/problems/longest-increasing-subsequence/description/)

```js
/**
子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4
*/

function longestIncreSub(arr) {
let len = arr.length;
Expand Down Expand Up @@ -795,9 +797,8 @@ function water(nums) {
```js
function deletNode(node, k) {
let dummy = { next: node, val: 0 };
let cur = dummy;
let fast = cur;
let slow = cur;
let fast = dummy;
let slow = dummy;
k = k + 1;
while (k-- && fast) {
fast = fast.next;
Expand Down Expand Up @@ -1008,6 +1009,30 @@ function findLongest(nums1, nums2) {
}
```

## [39.二叉树右视图](https://leetcode.cn/problems/binary-tree-right-side-view/submissions/220539265/)

```js
function dfsLight(tree) {
let level = 0;
let ans = [];
let dfs = (node, step) => {
if (!node) return;
if (step > level) {
ans.push(node.val);
level++;
}
if (node.right) {
dfs(node.right, step + 1);
}
if (node.left) {
dfs(node.left, step + 1);
}
};
dfs(node, 1);
return ans;
}
```

## [41.复原 IP 地址](https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE)

```js
Expand Down
2 changes: 1 addition & 1 deletion docs/interview/experiences/23leetcodeC.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ nav:
* 9.打乱数组
* 10.复原IP地址
* 11.回文链表
* 12.
* 12.两两交换链表中的节点
*/
```

Expand Down
Loading

0 comments on commit f631c25

Please sign in to comment.