【JS】前端面试常见手写题总结

本文主要总结从网上看到的各种 JS 手写题,其中应用题居多。实际应用中,需要结合自己的理解而不是背代码。

防抖的功能类似回城功能,多次调用防抖函数只有最后一次被调用的函数会执行。可以用于防止按钮重复点击导致发起多次网络请求等用途。

function debounce(fn, delay) {
	let timer = null
	return function(...args) {
		clearTimeout(timer)
		timer = setTimeout(fn.bind(this, ...args), delay)
	}
}

const fn = debounce(f, 1000)

节流

同一个时间段内,最多只会执行一次功能。可以用于减少监听事件触发的次数,提升性能。

function throttle(fn, delay) {
	let timer;
	return function(...args) {
		if(!timer) {
			timer = setTimeout(() => {
				fn.apply(this, ..args)
				timer = null;
			}, delay)
		}
	}
}

节流 + 防抖

试想只是单纯用节流来减少页面滚动事件回调的触发次数,用户可能在某个间隔内将滚动条滑动到底,而此时并不会触发加载数据回调,可能导致用户体验较差的情况。解决方法是在节流的同时,设置一个超时时间,如果超过超时时间则自动调用回调。

function throttle(fn, delay) {
  let last = 0, timer = null
  return function(...args) {
	const now = new Date()
	if(now - last < delay) {
		clearTimeout(timer)
		timer = setTimeout(() => {
			fn.apply(this, ...args)
			last = now
		})
	} else {
		last = now
		fn.apply(this, ...args)
	}
  }
}

图片懒加载

仅在图片滚动进视口时加载图片,优化性能和体验。

getBoundingClientRect

const imgs = document.querySelectorAll('img[data-src]')

window.addEventListener('scroll', throttle(() => {
  for(let i=0, len=imgs.length; i<len; i++) {
	const { top } = imgs[i].getBoundingClientRect()
	if(top < document.documentElement.clientHeight && !imgs[i].src) {
		imgs[i].src = imgs[i].getAttribute('data-src')
	}
  }
}, 100))

IntersectionObserver

let img = document.getElementsByTagName("img");

const observer = new IntersectionObserver(changes => {
  //changes 是被观察的元素集合
  for(let i = 0, len = changes.length; i < len; i++) {
    let change = changes[i];
    // 通过这个属性判断是否在视口中
    if(change.isIntersecting) {
      const imgElement = change.target;
      imgElement.src = imgElement.getAttribute("data-src");
      observer.unobserve(imgElement);
    }
  }
})
Array.from(img).forEach(item => observer.observe(item));

// 作者:神三元
// 链接:https://juejin.cn/post/6844904021308735502

深拷贝

JSON.stringify

最简单的深拷贝方法,无法拷贝/做到:

  • 函数
  • undefined
  • Symbol
  • 递归引用会出错

等数据类型,因为这些数据类型在 JSON 中没有定义

const new_obj = JSON.parse(JSON.stringify(obj))

递归

  • 注意循环引用的问题
  • Object 和 Array 等引用类型需要拷贝

注意这些边界条件即可

function deepClone(obj, map=new Map()) {
  if(typeof obj !== 'object') {
    return obj
  }

  const ref = map.get(obj)
  
  if(ref) {
    return ref
  }

  map.set(obj, obj)
  const result = Array.isArray(obj) ? [] : {}

  for(const key in obj) {
     result[key] = deepClone(obj[key], map)
  }

  return result
}

有两点可以优化:

  • for in
  • Map 改为 WeakMap

数组扁平化

ES6 自带 flat

const arr = [1, 2, [3, 4, [5, 6]]]
arr.flat(Infinity)

正则表达式替换

const arr = [1, 2, [3, 4, [5, 6]]]
const jsstr = JSON.stringify(arr).replace(/\[|]/g, '')
const a = JSON.parse('[' + jsstr + ']')

contact

while(arr.some(Array.isArray)) {
  arr = [].contact(...arr)
}

递归

const result = []
function flat(arr) {
  if(!Array.isArray(arr)) {
     result.push(arr)
  } else {
    for(const item of arr) {
      flat(item)
    }
  }
}

柯里化

实现形如 sum(1)(2)(3) => 6 的函数,可以看到这种函数调用时能够保存之前传入的参数,且当参数的个数符合调用参数的个数时,就会自动调用该函数。

function curry(fn, ...nargs) {
  const len = fn.length // fn 的参数个数
  return function(...args) {
     const all_args = [...args, ...nargs]
     if(all_args.length < len) {
		return curry(fn, ...all_args)
	 } else {
		return fn(...all_args)
	 }
  }
}

function sum(a, b, c) {
  return a + b + c
}

curry(sum, 1, 2, 3)
curry(sum)(1, 3)(2)
curry(sum, 1)(2, 3)
curry(sum, 1, 2)(3)

const c = curry(sum, 1, 2)
c(3)
c(4)
c(5)

instanceof

instanceof 的原理:obj instanceof 构造函数,不断向上寻找 obj 的 prototype,看是否和构造函数的相等,如果相等则返回 true。

function isInstanceof(obj, target) {
	const targetProto = target.prototype
	
	while(true) {
		const proto = Object.getPrototypeOf(obj)
	
		if(proto === null) {
			return false
		}
		
		if(proto === targetProto) {
			return true
		}
		
		obj = proto
	}
}

数组去重

// set
new Set(...arr)

// indexOf
arr.filter((item, idx) => arr.indexOf(item) === idx)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值