【从0开始的面试准备】 - day1 学习笔记

112 阅读4分钟

正在待业准备面试,打算通过输出梳理知识点,欢迎大家一起交流~

有什么写得不完整的,欢迎补充👏🏻

今日

  1. 八股文 - 基础部分
    1. JavaScript规定了几种语言类型
    2. JavaScript对象的底层数据结构是什么  
  2. 手写 - 防抖节流
  3. 算法 - 动态规划

js 手写题 - 防抖与节流

防抖

引用 lodashjs 中的定义是「创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 wait 毫秒后调用 func 方法。」

即,当我们触发一个动作,短时间内的多次触发,在最后一次时执行。短时间是第二个参数 wait。

  1. 先以一个实际的场景,写一个用来验证我们的防抖方法的。

比如,当我们写滚动条事件时,往往会多次触发,这时候需要使用防抖。如下。

window.addEventListener(
  'scroll',
  debounce(() => console.log('滚动条移动了'), 300)
)
  1. 然后,我们来实现一个简单版本的防抖函数:
  • 一个定时器,用来存储定时函数
  • 当需要防抖的动作触发后,先判断是否有正在进行中的定时函数,如果有,则清除
  • 将定时器赋值新的定时函数
function debounce(fn, delay = 100) {
    let timer
    return function () {
        if (timer) clearTimeout(timer)

        const args = arguments
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, delay)
    }
}

如上,使用第一步的滚动条场景,即可。

  1. 但这时候会有个问题,如果动作一直在被触发呢?

方法需要一个超时时间

function debounce(fn, wait = 100, maxWait = 1000) {
  let timer, time
  return function () {
    if (!time) time = Date.now()
    if (timer) clearTimeout(timer)

    const args = arguments

    timer = setTimeout(
      () => {
        time = undefined
        fn.apply(this, args)
      },
      Math.min(maxWait - (Date.now() - time), wait)
    )
  }
}

好啦,完成~

当然更多包含参数验证的完整版,参考:

www.lodashjs.com/docs/lodash…
github.com/lodash/loda…

节流

接下来看下节流。

创建一个节流函数,在 wait 秒内最多执行 func 一次的函数。 该函数提供一个 cancel 方法取消延迟的函数调用以及 flush 方法立即调用。

关于防抖和节流的差异,可以理解为,防抖是后执行,而节流先执行。

  1. 场景
window.addEventListener(
  'scroll',
  throttle(() => {
    console.log('节流')
  }, 5000)
)
  1. 实现
function throttle(fn, delay) {
  let flag = true
  return () => {
    if (!flag) return
    flag = false
    fn()
    timer = setTimeout(() => {
      flag = true
    }, delay)
  }
}

好啦,完成~

当然更多完整版,参考:

www.lodashjs.com/docs/lodash… github.com/lodash/loda…

八股文

题目参考:一名【合格】前端工程师的自检清单

JavaScript规定了几种语言类型

ECMAScript标准规定了7种数据类型,其把这7种数据类型又分为两种:原始类型和对象类型。

原始类型

  • Null:只包含一个值:null
  • Undefined:只包含一个值:undefined
  • Boolean:包含两个值:true和false
  • Number:整数或浮点数,还有一些特殊值(-Infinity、+Infinity、NaN)
  • String:一串表示文本值的字符序列
  • Symbol:一种实例是唯一且不可改变的数据类型
  • BigInt: ES10 (developer.mozilla.org/zh-CN/docs/…)

对象类型

  • Object:自己分一类丝毫不过分,除了常用的Object,Array、Function等都属于特殊的对象

JavaScript对象的底层数据结构是什么  

堆(Heap) 和 栈( Stack)

栈(Stack) JavaScript基本类型数据都是直接按值存储在栈中的(Undefined、Null、不是new出来的布尔、数字和字符串),每种类型的数据占用的内存空间的大小是确定的,并由系统自动分配和自动释放。这样带来的好处就是,内存可以及时得到回收,相对于堆来说 ,更加容易管理内存空间。 堆(Heap) JavaScript引用类型数据被存储于堆中 (如对象、数组、函数等,它们是通过拷贝和new出来的)。其实,说存储于堆中,也不太准确,因为,引用类型的数据的地址指针是存储于栈中的,当我们想要访问引用类型的值的时候,需要先从栈中获得对象的地址指针,然后,在通过地址指针找到堆中的所需要的数据。

算法 - 动态规划

什么是动态规划

Dynamic Programming,简称DP,将问题分解为互相重叠的子问题,通过反复求解子问题来解决原问题。

解动态规划题目的步骤

  • 根据重叠子问题
  • 寻找最优子结构推导状态转移方程
  • 确定 dp 初始状态
  • 确定输出值

leetcode-509 斐波那契数列

leetcode.cn/problems/fi…

状态转移方程:

dp[0] = 0
dp[1] = 1
dp[i] = dp[i - 1] + dp[i - 2]

1. 暴力递归

const fib = n => {
    if(n === 0) return 0
    if(n === 1) return 1

    return fib(n - 1) + fib(n - 2)
}

2. 递归加记忆

const fib = n => {
    // 缓存,记忆
    const memo = {}

    const getD = x => {
        if (memo[x]) return memo[x]
        if (x <= 1) return x
        memo[x] = getD(x - 1) + getD(x - 2)
        return memo[x]
    }

    return getD(n)
}

3. 动态规划

const fib = n => {
    if(n <= 1) return n

    const dp = [0, 1]
    for(i = 2;i <= n;i++) {
        // 自底向上计算每个状态
        dp[i] = dp[i - 1] + dp[i - 2]
    }
    return dp[n]
}

4. 滚动数组优化

减少空间复杂度

const fib = n => {
    if(n <= 1) return n

    // 只维护长度为2的滚动数组
    const dp = [0, 1]
    let sum = 0
    for(let i = 2;i <= n;i++) {
        sum = dp[0] + dp[1]
        dp[0] = dp[1]
        dp[1] = sum
    }
    return sum
}

5. 动态规划+降维

把维护的滚动数组改为变量。 减少空间复杂度,但不利于程序拓展。

const fib = n => {
    if(n <= 1) return n

    // 把维护的滚动数组改为变量
    let prev1 = 0
    let prev2 = 1
    let sum = 0
    for(i = 2;i <= n;i++) {
        sum = prev1 + prev2
        prev1 = prev2
        prev2 = sum
    }
    return sum
}

leetcode-62 不同路径

leetcode.cn/problems/un… 达到一个格子,只可能从左边往右,或者从上往下走 即

dp[0, 0] = 0
dp[0, y] = y
dp[0, x] = x
dp[x, y] = dp[x - 1, y] + dp[x, y - 1]
/**
 * @param {number} m
 * @param {number} n
 * @return {number}
 */
var uniquePaths = function (m, n) {
    const cur = new Array(n).fill(1)

    for(let i = 1; i < m; i++) {
        for( let r = 1; r < n; r++) {
            cur[r] = cur[r - 1] + cur[r]
        }
    }

    return cur[n - 1]
};