• 【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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    节流

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

    function throttle(fn, delay) {
    	let timer;
    	return function(...args) {
    		if(!timer) {
    			timer = setTimeout(() => {
    				fn.apply(this, ..args)
    				timer = null;
    			}, delay)
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    节流 + 防抖

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

    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)
    	}
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    图片懒加载

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

    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))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    深拷贝

    JSON.stringify

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

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

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

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

    递归

    • 注意循环引用的问题
    • 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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    有两点可以优化:

    • for in
    • Map 改为 WeakMap

    数组扁平化

    ES6 自带 flat

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

    正则表达式替换

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

    contact

    while(arr.some(Array.isArray)) {
      arr = [].contact(...arr)
    }
    
    • 1
    • 2
    • 3

    递归

    const result = []
    function flat(arr) {
      if(!Array.isArray(arr)) {
         result.push(arr)
      } else {
        for(const item of arr) {
          flat(item)
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    柯里化

    实现形如 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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    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
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    数组去重

    // set
    new Set(...arr)
    
    // indexOf
    arr.filter((item, idx) => arr.indexOf(item) === idx)
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  • 相关阅读:
    抖音短视频实操:抖音热门视频的分类特点,如何选择视频内容(中)
    自研ORM 子查询&嵌套查询
    零基础入门学习Python第一阶10图形用户界面和游戏开发
    Python-Matplotlib 显示中文、中文乱码解决办法
    Electron学习笔记(三)
    二叉树中和为某一值的路径(二)
    算法通关村十三关-青铜:数字与数学基础问题
    Linux系列之链接
    性能测试工具有哪些?原理是什么?怎么选择适合的工具?
    Flink
  • 原文地址:https://blog.csdn.net/qq_39559879/article/details/126705869