前端面试手写题系列 X
前端面试 手写题系列 已经完结,包含了以下的题目:
1. 手写拍平数组 flat 2. 手写防抖和节流函数 3. 手写深拷贝 4. 手写快排 5. 手写 call、apply、bind 6. 手写一个 sleep 函数 7. 手写冒泡排序 8. 函数柯里化 9. 对象扁平化 10. 手写 new 过程 11. 求数组中的最大值 12. 手写 instanceof 13. 手写 foreach 函数 14. 手写迭代器 15. 手写 filter 16. 实现一个 compose 函数 17. 正则相关(去哪儿原题) 18. 实现一个任务调度函数(得物原题) 19. 数组转化为 tree 20. 不使用 a 标签,实现 a 标签的功能 21. 手写插入排序 22. LRU 算法 23. 归并排序 24. 求两个数组的交集、并集、补集、差集 25. 提取 url 中的参数 26. 实现一个洗牌函数 shuffle 27. 希尔排序
1.手写拍平数组 flat
拍平数组:就是将 [1, 2, 3, [4, 5]]
变为 [1, 2, 3, 4, 5]
举个实际应用的例子:
const list = [
{
id: 1,
name: '1号线',
tranship: ['2号线', ['3号线', 's8号线']]
},
{
id: 2,
name: '2号线',
tranship: ['1号线', '4号线']
},
{
id: 3,
name: '3号线',
tranship: ['10号线', '4号线']
}
]
// 求所有的能够转到路线
// ['2号线', ['3号线', 's8号线']] + ['1号线', '4号线'] + ['10号线', '4号线'] = [所有的线路]
// 思路: concat
代码:
JavaScript 本身的 flat 函数:
const arr = ['2号线', ['3号线', 's8号线'], '1号线', '4号线', '10号线', '4号线']
console.log(arr.flat())
结果:
PS E:\demo> node test.js
[
'2号线', '3号线',
's8号线', '1号线',
'4号线', '10号线',
'4号线'
]
但是官方的函数只能拍平一层。
自己手写的函数:
const arr = ['2号线', ['3号线', 's8号线', ['s10号线']], '1号线', '4号线', '10号线', '4号线']
function MyFlat(arr) {
while (arr.some(item => item instanceof Array)) {
arr = Array.prototype.concat.apply([], arr)
console.log(arr)
}
return arr
}
console.log(MyFlat(arr))
// console.log([].concat([1, [1, 2]]))
// console.log(Array.prototype.concat.apply([], [1, [1, 2]])) // [] + 1, [1,2]
2.手写防抖和节流函数
防抖:
例子:
游戏中的回城就可以认为是防抖,在回城的读秒过程中,如果再次执行回城操作,那么会重新进行读秒,只有整个读秒过程都没有再次执行回城操作,那么等到读秒结束才能成功回城。
应用场景:
- 输入框频繁输入内容,搜索或者提交信息。
- 频繁点击按钮,触发某个事件。
- 监听浏览器滚动事件。
- 监听用户缩放浏览器resize事件。
function debounce(func, wait = 1000) {
// 设定定时器
let timer = null
// 返回的函数是用户调用的函数
// 如果已经设定过定时器就清空上一次的定时器
// 开启一个新的定时器,延迟执行传入的方法
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(null, arguments)
}, wait)
}
}
function debounce1(func, wait = 1000) {
// 设定定时器
let timer = null
// 返回的函数是用户调用的函数
// 如果已经设定过定时器就清空上一次的定时器
// 开启一个新的定时器,延迟执行传入的方法
return function (args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(null, [args])
}, wait)
}
}
const submit = value => {
console.log(value)
}
// submit(1)
let submit1 = debounce1(submit, 2000)
submit1(1)
节流函数:
例子:
节流类似于技能cd,不管你按了多少次,必须等到cd结束后才能释放技能。也就是说在如果在cd时间段,不管你触发了几次事件,只会执行一次。只有当下一次cd转换,才会再次执行。
function throttle(fn, wait = 1000) {
let timer = null
return function () {
if (timer) {
return
}
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, wait)
}
}
const scroll = val => {
console.log(val)
}
let scroll1 = throttle(scroll, 0.1)
// 模拟滚动条滚动,假设滚动条在滚动,一直在触发 scroll1 函数
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
scroll1(1)
结果:只会执行一次,我们模拟的是一直在滚动滚动条,每隔 0.1ms 执行一次,所以只打印出了一个值,因为 我们模拟的数量过少,这些 scroll1 函数在 0.1ms 之内都完成了。
PS E:\demo> node test.js
1
关于 apply 和 call
其实节流函数的执行也可以使用下面的这种方法,叫做剩余参数的写法:
function throttle1(fn, wait = 1000) {
let timer = null
return function (...arg) {
if (timer) {
return
}
timer = setTimeout(() => {
fn.call(this, ...arg)
timer = null
}, wait)
}
}
剩余参数值得就是 arg,他是一个数组,然后我们可以使用 ...arg
的形式将数组里面的值取出来,所以可以理解 fn.call(this, ...arg)
第二个参数 ...arg
实际上是一个一个单独的参数,而不是和 apply 一样的数组作为参数。
手写题系列文章
前端手写题系列 I
前端手写题系列 II
前端手写题系列 III
前端手写题系列 IV
前端手写题系列 V
前端手写题系列 VI
前端手写题系列 VII
前端手写题系列 VIII
前端手写题系列 IX
前端手写题系列 X