【2022年前端踩坑记】一些日常疑惑和解决方案

7.19 星期二

1. map/forEach 中使用 await 无效

  • Q1: map 返回的并不是一个 promise 对象, 而是一个包含 promise 对象的数组[promise, promise, promise],
    其中每个 promise 对象都是循环迭代产生的结果。而 await 是处理不了数组的, 它只能处理 promise 对象。

  • Q2: forEach 是并行的对每个元素执行函数, 所以 await 不会阻碍循环和代码的执行。
    使用 foreach 就相当于调用了一个封装了 while 或者 for 循环的函数, 该函数本身并没有使用 async/await 来处理异步,
    所以使用时在回调函数里面加上 async/await 是没有作用的。

  • A1: 直接使用循环, 如 forwhileforof

  • A2: 结合 promise.all 使用 map/forEach

    await Promise.all(asyncFuncs);
    
  • A3: 使用 reduce

    const arr = [1, 2, 3];
    
    await arr.reduce(async (memo, i) => {
      await memo;
      await sleep(10 - i);
      console.log(i);
    }, undefined);
    
    // 1
    // 2
    // 3
    
    console.log("Finished async");
    // Finished async
    
related articles:
  1. 在 foreach 中使用 async/await 的问题
  2. 如何在 Array.forEach 中正确使用 Async
  3. JS 循环中的中断、异步 for…in、forEach、 map

2. 同一个组件中多个 useEffect 的执行顺序

React will apply every effect used by the component, in the order they were specified.

3. 何时使用 promise?运用时应该注意什么问题

  1. promise 可解决回调地狱 (常常第一个的函数的输出是第二个函数的输入)
  2. promise 可以支持多个并发的请求, 获取并发请求中的数据。
    (Promise.all()Promise.race() 是并行运行异步操作的两个组合式工具。)

7.22 星期五

1. useRef 为何可读取最新值 (ahooks 中 useLatest 和 useMemorizedFn 的实现原理)

  • useRef 创建的是一个普通 Javascript 对象, 而且会在每次渲染时返回同一个 ref 对象, 当我们变化其 current 属性时, 对象的引用都是同一个,
    所以能读到最新的值。

  • const refContainer = useRef(initialValue);
    useRef 返回一个可变的 ref 对象, 其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内持续存在。

related articles:
  1. ahooks 是怎么解决 React 的闭包问题的?
  2. 从 react hooks“闭包陷阱”切入, 浅谈 react hooks

7.25 星期一

1. Map 类及 Set 类

  • Set 是 es6 新增的数据结构,似于数组,但它的一大特性就是所有元素都是唯一的,没有重复的值,我们一般称为集合。
    实例方法:add/clear/delete/has
    迭代方法:keys/values/entries/forEach

应用场景:数组去重、数据重组

  • Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
    实例方法:clear/delete/get/has/set
    迭代方法:keys/values/entries/forEach
related articles:
  1. Set,Map 的区别

8.2 星期二

1. 深拷贝和浅拷贝的区别及各自的应用场景

  • A、区别: 浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制。

  • B、概念理解及实现方式:
    ① 浅拷贝: 增加了一个指针指向已存在的内存。 Object.assign() ; 扩展运算符…; Array.prototype.concat() ; Array.prototype.slice()
    ② 深拷贝: 增加一个指针,并申请一个新的内存。 JSON.parse(JSON.stringify()); 递归赋值; lodash–cloneDeep

注意:
Object.assign 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身;但 obj 只有一层时,是深拷贝。

  • C、使用场景:
    使用某对象时,不想修改原对象,就用深拷贝。
related articles:
  1. 浅拷贝与深拷贝

2. 深比较 VS 浅比较

  • 浅比较:又称引用相等,检查左右两边是否是同一个对象的引用。 === ; Object.is

注意:Object.is 浅比较, react中的useEffect、useMemo、useCallback都是用Object.is比较依赖的。

表达式结果
+0 === -0true
NaN === NaNfalse
Object.is(+0, -0)false
Object.is(NaN, NaN)true
  • 深比较:又称原值相等,检查两个对象的所有属性是否都相等。递归比较; lodash–isEqual; fast-deep-equal

3. Object.defineProperty VS Proxy

使用 Object.defineProperty 的时候,我们遇到的问题有:

  1. 一次只能对一个属性进行监听,需要遍历来对所有属性监听。这个我们在上面已经解决了。
  2. 在遇到一个对象的属性还是一个对象的情况下,需要递归监听。
  3. 对于对象的新增属性,需要手动监听
  4. 对于数组通过 pushunshift 方法增加的元素,也无法监听
    这些问题在 Proxy 中都轻松得到了解决
related articles:
  1. 一文搞懂 Object.defineProperty 和 Proxy,Vue3.0 为什么采用 Proxy?
  2. JS 进阶 | Proxy 代理对象

4. typescript----何时使用泛型

  • A、定义
    泛型表示泛指某一种类型,开发者可以指定一个表示类型的变量,用它来作为实际类型的占位符,用尖括号来包裹类型变量 。
    泛型的主要作用是创建可重用的组件,从而让一个组件可以支持多种数据类型,它可以作用在接口、类、函数或类型别名上。

T(Type): 常用类型变量名称;
K(Key):表示对象中的键类型;
V(Value):表示对象中的值类型;
E(Element):表示元素类型。

  • B、适用场景
    ① 当函数、接口或类将处理多种数据类型时;
    ② 当函数、接口或类在多个地方使用该数据类型时

  • C、运用–到函数
    举个栗子:—> 将类型链式传递给参数类型和返回类型

function test<T>(value: T): T {
  return value;
}

function identity<T, U>(value: T, message: U): [T, U] {
  return [value, message];
}

identity < number, string > (68, "hi");
identity < boolean, number > (true, 10);
  • C、运用–泛型接口
interface Identies<V, M> {
  value: V;
  label: M;
}

运用–泛型类型

type EventValue<DateType> = DateType | null;
  • D、运用–泛型类
Class IdentityClass<T> {
value:T

constructor(value:T) {
this.value = value;
}

getIdentity():T {
return this.value
}
}
  • E、扩展–泛型约束
    ① 确保属性存在
    <T extends Type1, Type2, Type3>

② 检查对象上的键是否存在
K extends keyof T

function getProperty(T, K extends keyof T)(obj:T, key:K):T[K] {
return obj[key];
}
related articles:
  1. 一文读懂 TypeScript 泛型及应用

5. typescript----interface 和 type 的区别

  • ① 使用类型别名的场景:

    定义基本类型的别名时,使用 type ---- type MyNumber = number;
    定义元组类型时,使用 type ---- type Point = [number, number]
    定义函数类型时,使用 type ---- type Callback = (data: string) => void;
    定义联合类型时,使用 type ---- type StringOrNumber = string | number[];
    定义映射类型时,使用 type ----

  • ② 使用接口的场景:

    需要利用接口自动合并特性的时候,使用 interface
    定义对象类型且无需使用 type 的时候,使用 interface

ps: 相同点: 都可以用来描述对象或函数; 都支持扩展(type—> &(交叉运算符); interface --> extends)

related articles:
  1. type 和 interface 傻傻分不清楚

10.9 星期日

1. typescript----高级类型

1. Record<K,T>

Record<K,T>给定类型 T 的一组属性 K 的类型。在将一个类型的属性映射到另一个类型的属性时,Record 非常方便。

// demo
type petsGroup = "dog" | "cat" | "fish";
interface IPetInfo {
  name: string;
  age: number;
}

type IPets = Record<petsGroup | "otherAnamial", IPetInfo>;

const animalsInfo: IPets = {
  dog: {
    name: "dogName",
    age: 2,
  },
  cat: {
    name: "catName",
    age: 3,
  },
  fish: {
    name: "fishName",
    age: 5,
  },
  otherAnamial: {
    name: "otherAnamialName",
    age: 10,
  },
};
related articles:
  1. Typescript 高级类型 Record

2. Partial

Partial让所有属性可选。

interface IUser {
  name: string;
  age: number;
}

type optionalUser = Partial<Iuser>;

// 等同于
type optionalUser = {
  name?: string,
  age?: number,
};

3. Readonly

Readonly将所有属性定义为自读。

type Coord = Readonly<Record<"x" | "y", number>>;

// 等同于
type Coord = {
  readonly x: number,
  readonly y: number,
}

4. Pick<T,K>

Pick<T,K>从类型定义的属性中,选取指定一组属性,返回一个新的类型定义。

interface Coord {
  x: number;
  y: string;
  z: boolean;
}
type CoordX = Pick<Coord, "x">;

// 等同于
type CoordX = {
  x: number,
};

2.如何避免不必要的状态提升?

1、预留位置执行上层组件的函数
案例: 按钮自动 loading 功能

const { onSave } = props;
const [loading, setLoading] = useState(false); // 子组件自行管理状态,即 loading 状态不取于上层组件
const handleSave = async () => {
  setLoading(true);
  await onSave(); // 预留位置执行上层组件的函数
  setLoading(false);
};

2、预留位置放置children

10.13 星期四

1.遇到的技术难点

  • 1、多级右键菜单:处理弹出框显示位置;
  • 2、导出 pdf:要兼顾不同浏览器、pdf 本身的各种样式问题;
  • 3、处理 react 闭包问题:https://juejin.cn/post/7106061970184339464
  • 4、hook 及组件封装:要同时考虑入参\出参\大小的合理性、可维护性、复用性等多个问题;
  • 5、一键生成新组件(低代码);
  • 6、同一个组件使用多个 useEffect 要处理执行顺序(考点:宏任务、微任务、同步任务执行顺序)

11.7 星期一

1.如何避免使用过多 useState?

(考虑使用 useMemo、useReducer)

① State is reserved only for interactivity, that is, data that changes over time.
② If you want to also display the number of items in the list, don’t store the number of items as another state value—instead, read the length of your array.
③ Can you compute it based on existing state or props in your component? If so, it definitely isn’t state.

related articles:
  1. Thinking in React

12.21 星期三

1.什么时候用类更好?

  1. 类与函数的选择
  • ① 当我们拥有一堆共享状态的函数,或者将相同的参数传递给每个函数时,我们可以重新考虑码使用类。
  • ② 类的“可重用性”意味着我们可以在其他应用程序中重用之前的代码。如果我们在自己的文件中编写了类,则只需将其放在另一个项目中即可使其工作。
  • ③ 函数对于小型项目非常有用,但是一旦项目开始变大,仅使用函数就可能变得混乱。类是组织和简化代码的一种非常好的方法
  • ④ 通常,如果在函数内部找到自写函数,则应考虑编写类。如果我们在一个类中只有一个函数,那么请坚持只写一个函数。
  • ⑤ 如果需要在函数调用之间保留一些状态,那么最好使用带有该函数的类作为方法
related articles:
  1. 【Python】一文说清楚类与函数的选择

2.break, continue 和 return 的用法及区别

return:是函数返回语句,返回的同时函数也会停止执行。
break:语句会跳出循环,但是会继续执行循环之后的代码(跳出循环)。
continue:语句会跳过当前循环,进入下一个循环。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值