作业与总结

一. 完成课堂所有的代码

二. 说说你对防抖、节流的理解,他们的区别,应用场景(面试)

防抖: 将多次执行函数变成最后一次执行 等待固定时间还没有事件触发时执行的函数

  • 应用场景
    • 按钮的点击
    • 屏幕滚动时的复杂计算
    • 输入框输入时进行搜索
    • 用户缩放浏览器的resize事件
  • 简单的防抖函数实现

    1. function myDebounce(execFn, delay) {
    2. let timer = 0
    3. function _debounce(...args) {
    4. if (timer) clearTimeout(timer)
    5. timer = setTimeout(() => {
    6. execFn.apply(this, args)
    7. timer = null
    8. }, delay)
    9. }
    10. return _debounce
    11. }

节流: 按照固定的时间频率(间隔)来执行对应的函数

  • 应用场景:
    • 监听页面的滚动事件 通过节流来降低事件调用的频率
    • 鼠标移动
    • 用户频繁点击按钮的操作
  • 简单实现

    1. function myThrottle(execFn, interval) {
    2. let initTime = 0
    3. function throttle(...args) {
    4. let nowTime = Date.now()
    5. const waitTime = interval - (nowTime - initTime)
    6. if (waitTime <= 0) {
    7. execFn.apply(this, args)
    8. initTime = nowTime
    9. }
    10. }
    11. return throttle
    12. }

三. 说说对象的引用赋值、浅拷贝、深拷贝的区别

对象的引用赋值

  • 把源对象指向自身所在堆内存空间的指针给了新对象 两个对象所指向的内存空间是一样的 修改其中一个的值 另一个也会发生改变

对象的浅拷贝

  • 可以通过{…obj}的方式进行对象的浅拷贝 (Object.assign({},obj))
  • 对于obj中的值是原始数据类型的 将对应的值赋值给了newObj中对应的属性
  • 对于obj中是复杂数据类型的值 把对应在内存中的指针赋值给了newObj中对应的key 对于复杂数据类型的value修改其中一个另一个也发生改变

对象的深拷贝(真实开发中使用非常少)

  • newObj与obj中的属性值一样 但是是一个全新的对象 与元对象没有任何关系
  • 默认情况下 js没有提供对应的深拷贝的方式 因为深拷贝是非常消耗内存的
  • 有对应的库实现了深拷贝
  • 实现深拷贝

    • JSON.parse(JSON.stringfy(obj))
      • 缺点: 对于某些属性如 undefined,Symbol,function,Symbol 会自动忽略; 对于set map 会转成对象
    • 自己实现(不带循环引用) ```javascript function isObject(obj) { return obj !== null && (typeof obj === “object” || typeof obj === “function”) }

    function deepClone(originValue) { // Symbol类型 if (typeof originValue === “symbol”) {

    1. return Symbol(originValue.description)

    }

    // 判断是否是对象 if (!isObject(originValue)) return originValue;

    // set类型 if (originValue instanceof Set) {

    1. const newSet = new Set()
    2. for (const setItem of originValue) {
    3. newSet.add(deepClone(setItem))
    4. }
    5. return newSet

    }

    // 判断是函数 if (typeof originValue === “function”) {

    1. return originValue

    }

    // 判断返回值是数组还是对象 const newObj = Array.isArray(originValue) ? [] : {} if (Reflect) {

    1. for (let key of Reflect.ownKeys(originValue)) {
    2. {
    3. let value = originValue[key]
    4. // 让 SymbolKey的值不同
    5. if (typeof key === "symbol") {
    6. const newSymbolKey = Symbol(key.description)
    7. // 将原来的值赋值给新生成的Symbol key
    8. value = originValue[key]
    9. key = newSymbolKey
    10. }
    11. newObj[key] = deepClone(value)
    12. }
    13. }

    } else {

    1. for (const key in originValue) {
    2. const value = originValue[key]
    3. newObj[key] = deepClone(value)
    4. }
    5. // 对于Symbol类型的key forin 无法便利出来
    6. const symbolKeys = Object.getOwnPropertySymbols(originValue)
    7. for (const symbolKey in symbolKeys) {
    8. const originSymbolValue = symbolKeys[symbolKey]
    9. const newSymbol = Symbol(originSymbolValue.description)
    10. newObj[newSymbol] = deepClone(originValue[originSymbolValue])

    } }

    1. return newObj
    2. }

    ```

  • 循环引用 ```javascript function isObject(obj) {

    1. return obj !== null && (typeof obj === "object" || typeof obj === "function")

    }

    function deepClone(originValue, map = new WeakMap()) {

    1. // Symbol类型
    2. if (typeof originValue === "symbol") {
    3. return Symbol(originValue.description)
    4. }
    5. // 判断是否是对象
    6. if (!isObject(originValue)) return originValue;
    7. if (map.has(originValue)) {
    8. return map.get(originValue)
    9. }
    10. // set类型
    11. if (originValue instanceof Set) {
    12. const newSet = new Set()
    13. for (const setItem of originValue) {
    14. newSet.add(deepClone(setItem, map))
    15. }
    16. return newSet
    17. }
    18. // 判断是函数
    19. if (typeof originValue === "function") {
    20. return originValue
    21. }
    22. // 判断返回值是数组还是对象
    23. const newObj = Array.isArray(originValue) ? [] : {}
    24. map.set(originValue, newObj)
    25. if (Reflect) {
    26. for (let key of Reflect.ownKeys(originValue)) {
    27. {
    28. let value = originValue[key]
    29. // 让 SymbolKey的值不同
    30. if (typeof key === "symbol") {
    31. const newSymbolKey = Symbol(key.description)
    32. // 将原来的值赋值给新生成的Symbol key
    33. value = originValue[key]
    34. key = newSymbolKey
    35. }
    36. newObj[key] = deepClone(value, map)
    37. }
    38. }
    39. } else {
    40. for (const key in originValue) {
    41. const value = originValue[key]
    42. newObj[key] = deepClone(value, map)
    43. }
    44. // 对于Symbol类型的key forin 无法便利出来
    45. const symbolKeys = Object.getOwnPropertySymbols(originValue)
  1. for (const symbolKey in symbolKeys) {
  2. const originSymbolValue = symbolKeys[symbolKey]
  3. const newSymbol = Symbol(originSymbolValue.description)
  4. newObj[newSymbol] = deepClone(originValue[originSymbolValue], map)
  5. }
  6. }
  7. return newObj
  8. }
  1. <a name="a95699a7"></a>
  2. ## 四. 事件总线的基本实现和使用(重点)
  3. ```javascript
  4. class myEventBus {
  5. constructor() {
  6. this.eventBus = {};
  7. }
  8. on(eventName, eventFn) {
  9. let eventSet = this.eventBus[eventName];
  10. if (!eventSet) {
  11. eventSet = new Set();
  12. this.eventBus[eventName] = eventSet;
  13. }
  14. this.eventBus[eventName].add(eventFn);
  15. }
  16. off(eventName, eventFn) {
  17. if (!this.eventBus[eventName]) {
  18. return;
  19. }
  20. const eventSet = this.eventBus[eventName];
  21. if (eventSet.has(eventFn)) this.eventBus[eventName].delete(eventFn);
  22. // 如果删除函数后 该事件对应的set为空 则删除该事件
  23. if (!this.eventBus[eventName].size) {
  24. delete this.eventBus[eventName];
  25. }
  26. }
  27. emit(eventName, ...payload) {
  28. if (!this.eventBus[eventName]) {
  29. return;
  30. }
  31. for (const item of this.eventBus[eventName]) {
  32. item(...payload);
  33. }
  34. }
  35. }
  36. const mitt = new myEventBus();
  37. mitt.on("wmm", () => {
  38. console.log("123");
  39. });
  40. const fn = () => {
  41. console.log("456");
  42. };
  43. mitt.on("wmm", fn);
  44. mitt.on("wmm", () => {
  45. console.log("789");
  46. });
  47. setTimeout(() => {
  48. // 移除事件
  49. mitt.off("wmm", fn);
  50. }, 1000);
  51. setTimeout(() => {
  52. mitt.emit("wmm");
  53. }, 2000);
  54. // 123
  55. // 789