腾讯面经学习

腾讯面经学习

刷牛客看到一篇腾讯的面经,感觉很多可以学的
https://www.nowcoder.com/feed/main/detail/2658f105a4ab499e8d930b93101cfd17

深拷贝

前面几个问题是问的深浅拷贝,需要自己手写实现,还需要解决循环引用的问题

深浅指的是什么

我们都知道,js 存储引用类型是使用的指针,所以下面 a 和 b 实际是引用的同一个地址的对象。

const a = { name: 'a' }
const b = a
console.log(a === b) // true

那我修改 b 的时候,其实 a 的内容也会变,这就不符合拷贝这个概念了,我们期望的是复制一份出来,改动其中一个不应该对另一个造成影响,所以需要实现深拷贝。

浅拷贝

相对于深拷贝来说,浅拷贝比较好实现,而且场景也很多。

const arr = [1, 2, 3]
const copyArr = [...arr]
// arr.slice()
// arr.concat()
// Array.from(arr)

const obj = { name: 'a', age: 12 }
const copyObj = { ...obj }
// Object.assign({}, 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、使用场景,这个直接复习看就行

闭包、作用域、内存泄漏

内存回收机制

数组

  • 有什么方法
    • 太多了,一个个说吧
  • 怎么去重、去重各自的时间复杂度
    • 可以研究下