正在待业准备面试,打算通过输出梳理知识点,欢迎大家一起交流~
有什么写得不完整的,欢迎补充👏🏻
今日
- 八股文 - 基础部分
JavaScript规定了几种语言类型JavaScript对象的底层数据结构是什么
- 手写 - 防抖节流
- 算法 - 动态规划
js 手写题 - 防抖与节流
防抖
引用 lodashjs 中的定义是「创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟
wait毫秒后调用func方法。」
即,当我们触发一个动作,短时间内的多次触发,在最后一次时执行。短时间是第二个参数 wait。
- 先以一个实际的场景,写一个用来验证我们的防抖方法的。
比如,当我们写滚动条事件时,往往会多次触发,这时候需要使用防抖。如下。
window.addEventListener(
'scroll',
debounce(() => console.log('滚动条移动了'), 300)
)
- 然后,我们来实现一个简单版本的防抖函数:
- 一个定时器,用来存储定时函数
- 当需要防抖的动作触发后,先判断是否有正在进行中的定时函数,如果有,则清除
- 将定时器赋值新的定时函数
function debounce(fn, delay = 100) {
let timer
return function () {
if (timer) clearTimeout(timer)
const args = arguments
timer = setTimeout(() => {
fn.apply(this, args)
}, delay)
}
}
如上,使用第一步的滚动条场景,即可。
- 但这时候会有个问题,如果动作一直在被触发呢?
方法需要一个超时时间
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方法立即调用。
关于防抖和节流的差异,可以理解为,防抖是后执行,而节流先执行。
- 场景
window.addEventListener(
'scroll',
throttle(() => {
console.log('节流')
}, 5000)
)
- 实现
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 斐波那契数列
状态转移方程:
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]
};