腾讯面经学习
刷牛客看到一篇腾讯的面经,感觉很多可以学的
https://www.nowcoder.com/feed/main/detail/2658f105a4ab499e8d930b93101cfd17
深拷贝
前面几个问题是问的深浅拷贝,需要自己手写实现,还需要解决循环引用的问题
深浅指的是什么
我们都知道,js 存储引用类型是使用的指针,所以下面 a 和 b 实际是引用的同一个地址的对象。
const a = { name: 'a' } const b = a console.log(a === b)
|
那我修改 b 的时候,其实 a 的内容也会变,这就不符合拷贝这个概念了,我们期望的是复制一份出来,改动其中一个不应该对另一个造成影响,所以需要实现深拷贝。
浅拷贝
相对于深拷贝来说,浅拷贝比较好实现,而且场景也很多。
const arr = [1, 2, 3] const copyArr = [...arr]
const obj = { name: 'a', age: 12 } const copyObj = { ...obj }
const copyObj2 = {} for (const [k, v] of Object.entries(obj)) { copyObj2[k] = v }
const shallowCopy = obj => { if (typeof obj !== 'object' || obj === null) return obj const newObj = obj instanceof Array ? [] : {} for (const k in obj) { if (obj.hasOwnProperty(key)) { newObj[k] = obj[k] } } return newObj }
|
深拷贝
对于数组、对象,也会有嵌套的情况,这时候想要完全拷贝成一个新的,就需要实现深拷贝了。
序列化+反序列化
const copyObj = JSON.parse(JSON.stringify(obj))
一行代码就能实现,但是有缺点的,部分类型会丢失,详细看这个JS 中的深浅拷贝
- 函数 、undefined、symbol 直接丢失
- NaN、Infinity 和-Infinity 会变成 null
- Date 会变成字符串
- Map、RegExp、Set、Error 变成空对象
- 循环引用会报错
手写递归
因为有多层嵌套关系,所以可以用递归来实现每一层都复制,但是其实也是有缺陷的。
- 不支持循环引用,栈溢出
- 特殊对象也需要处理
- 原型链丢失
所以需要对这些情况进行完善
- 循环引用:用 WeakMap 存储
- 其他类型:
- 基本类型直接返回
- 函数返回内存地址就行了
- map、set、array、object、weakMap,需要继续遍历拷贝
- Number、String、Boolean、Date、Error,构造返回,不遍历
const obj = { a: 1, b: '2', c: new Date(), d: new Set([2, 3, 1, 1]), e: new Map([[1, 111]]), f: undefined, g: null, h: Number(3), i: [1, 3, 4], j: { name: { zh: '名字', }, }, } console.log('🚀 ~ obj:', obj)
const getType = target => Object.prototype.toString.call(target).slice(8, -1)
const deepClone = (target, wm = new WeakMap()) => { if (typeof target !== 'object' || !target || typeof target === 'function') { return target }
const type = getType(target) const Ctor = target.constructor if ( ['Date', 'Error', 'String', 'Boolean', 'Number', 'Symbol'].includes(type) ) { return new Ctor(target) }
const newTarget = new Ctor() if (wm.has(target)) { return wm.get(target) } else { wm.set(target, newTarget) }
if (type === 'Map' || type === 'WeakMap') { target.forEach((v, k) => { newTarget.set(k, deepClone(v, wm)) }) return newTarget } if (type === 'Set' || type === 'WeakSet') { target.forEach((v, k) => { newTarget.add(k, deepClone(v, wm)) }) return newTarget }
for (const k in target) { newTarget[k] = deepClone(target[k]) } return newTarget }
console.log(deepClone(obj))
|
structuredClone API
https://developer.mozilla.org/zh-CN/docs/Web/API/structuredClone
JS 的原生 API,支持循环引用,也有限制
- 不保留原型链
- 对象里面的需要可以序列化,Function、DOM 就不行
ts
问的可多了
类、接口、模块
类:就是 js 的类,面向对象
接口:定义对象的结构,也可以描述类、函数
模块:前端模块化啊,cjs 和 mjs
泛型
就相当于函数的变量,可以用它来接受可变类型
null、undefined
let x: number | null = 10 interface Obj { a?: number } let obj: Obj = {}
|
this
this 怎么切换作用域、call、bind、apply、使用场景,这个直接复习看就行
闭包、作用域、内存泄漏
内存回收机制
数组