前端面试题及答案整理(2022最新版)

Posted 程序员·小季

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端面试题及答案整理(2022最新版)相关的知识,希望对你有一定的参考价值。

收集整理2022年最新前端面试题及答案,方便平时翻看记忆,欢迎各位大佬们补充。

一般来说,把下面基础中的高频题写熟练就差不多了。当然去面大厂这些远远不够,还要再刷一些算法题。

基础

高频

1.手写 instanceof

// 原理:验证当前类的原型prototype是否会出现在实例的原型链proto上,只要在它的原型链上,则结果都为true
function myinstanceOf_(obj, class_name) 
      // let proto = obj.__proto__;
    let proto = Object.getPrototypeOf(obj)
    let prototype = class_name.prototype
    while (true) 
        if (proto == null) return false
        if (proto == prototype) return true
          // proto = proto.__proto__;
        proto = Object.getPrototypeOf(proto)
    

​2.手写 new 操作符

function myNew()
    //1.创建一个新的对象
    let obj=new Object();
    //获得构造函数
    let con = [].shift.call(arguments); //[]为Array构造函数的实例  将类数组转化为真正的数组
    //2.新对象的隐式原型__proto__链接到构造函数的显式原型prototype
    obj.__proto__ = con.prototype;
    //3.构造函数内部的 this 绑定到这个新创建的对象 执行构造函数
    let result = con.apply(obj, arguments)
    //4.如果构造函数没有返回非空对象,则返回创建的新对象
    return typeof result == 'object' ? result:obj;

var test_create = myNew(Car, 'a', 'b', 'c');
console.log(test_create)

3.手写 call、apply、bind 函数

  • call(thisArg, ...args)
// 给函数的原型添加 _call 方法,使得所有函数都能调用 _call
// thisArg 就是要绑定的那个this;...args 扩展操作符传参,适合不定长参数,args是一个数组
Function.prototype._call = function(thisArg,...args)
    // 1.获取需要执行的函数
    fn = this

    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    // 3.使用 thisArg 调用函数,绑定 this
    thisArg.fn = fn
    let result = thisArg.fn(...args)
    delete thisArg.fn

    // 4.返回结果
    return result
  • apply(thisArg, argsArray)
Function.prototype._apply = function(thisArg,argArray)
    // 1.获取需要执行的函数
    fn = this

    // 2.将 thisArg 转成对象类型(防止它传入的是非对象类型,例如123数字)
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window
    // 判断一些边界情况
    argArray = argArray || []

    // 3.使用 thisArg 调用函数,绑定 this
    thisArg.fn = fn
    // 将传递过来的数组(可迭代对象)拆分,传给函数
    let result = thisArg.fn(...argArray)
    delete thisArg.fn

    // 4.返回结果
    return result
  • bind(thisArg, ...args)
Function.prototype._call = function(thisArg,...args)
    fn = this
    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    thisArg.fn = fn
    let result = thisArg.fn(...args)
    delete thisArg.fn

    return result


// 利用 call 模拟 bind
Function.prototype._bind = function(thisArg,...args)
    let fn = this // 需要调用的那个函数的引用
    // bind 需要返回一个函数
    return function()
        return fn._call(thisArg, ...args)
    

4.手写深拷贝

PS:浅拷贝也可以用一样的模板,当然深拷贝考得多

function deepCopy(object) 
  if (!object || typeof object !== "object") return object;

  let newObject = Array.isArray(object) ? [] : ;

  for (let key in object) 
    if (object.hasOwnProperty(key)) 
      newObject[key] = deepCopy(object[key]);
    
  

  return newObject;

进阶:解决循环引用的深拷贝

function deepClone(obj, hash = new WeakMap()) 
  if (!object || typeof object !== "object") return object;

  // 是对象的话就要进行深拷贝,遇到循环引用,将引用存储起来,如果存在就不再拷贝
  if (hash.get(obj)) return hash.get(obj);
  let cloneObj = Array.isArray(object) ? [] : ;
  hash.set(obj, cloneObj);

  for (let key in obj) 
    if (obj.hasOwnProperty(key)) 
      // 实现一个递归拷贝
      cloneObj[key] = deepClone(obj[key], hash);
    
  
  return cloneObj;

5.手写防抖节流

function debounce(func, delay) 

  // 这里使用了闭包,所以 timer 不会轻易被销毁
  let timer = null

  // 生成一个新的函数并返回
  return function (...args) 
    // 清空定时器
    if (timer) 
      clearTimeout(timer)
    
    // 重新启动定时器
    timer = setTimeout(() => 
      func.call(this, ...args)
    , delay)
  


function throttle(func, delay) 
  let timer = null
  // 在 delay 时间内,最多执行一次 func
  return function (...args) 
    if (!timer) 
      timer = setTimeout(() => 
        func.call(this, ...args)
        // 完成一次计时,清空,待下一次触发
        timer = null
      , delay)
    
  

6.手写Ajax请求

function ajax(url) 
    // 创建一个 XHR 对象
    return new Promise((resolve,reject) => 
        const xhr = new XMLHttpRequest()
        // 指定请求类型,请求URL,和是否异步
        xhr.open('GET', url, true)
        xhr.onreadystatechange = funtion() 
          // 表明数据已就绪
            if(xhr.readyState === 4) 
                if(xhr.status === 200)
                    // 回调
                    resolve(JSON.stringify(xhr.responseText))
                
                else
                    reject('error')
                
            
        
        // 发送定义好的请求
        xhr.send(null)
    )

7.手写数组去重

// 1.Set + 数组复制
fuction unique1(array)
    // Array.from(),对一个可迭代对象进行浅拷贝
    return Array.from(new Set(array))


// 2.Set + 扩展运算符浅拷贝
function unique2(array)
    // ... 扩展运算符
    return [...new Set(array)]


// 3.filter,判断是不是首次出现,如果不是就过滤掉
function unique3(array)
    return array.filter((item,index) => 
        return array.indexOf(item) === index
    )


// 4.创建一个新数组,如果之前没加入就加入
function unique4(array)
    let res = []
    array.forEach(item => 
        if(res.indexOf(item) === -1)
            res.push(item)
        
    )
    return res

进阶:如果数组内有数组和对象,应该怎么去重(此时对象的地址不同,用Set去不了重)

需要开通正版 WebStorm 的可以联系我,56元一年,正版授权激活,官网可查有效期,有需要的加我微信:poxiaozhiai6,备注:915。

8.手写数组扁平

// 方法1-3:递归
function flat1(array)
    // reduce(): 对数组的每一项执行归并函数,这个归并函数的返回值会作为下一次调用时的参数,即 preValue
    // concat(): 合并两个数组,并返回一个新数组
    return array.reduce((preValue,curItem) => 
        return preValue.concat(Array.isArray(curItem) ? flat1(curItem) : curItem)
    ,[])


function flat2(array)
    let res = []
    array.forEach(item => 
        if(Array.isArray(item))
            // res.push(...flat2(item))
        // 如果遇到一个数组,递归
            res = res.concat(flat2(item))
        
        else
            res.push(item)
        
    )
    return res


function flat3(array)
    // some(): 对数组的每一项都运行传入的函数,如果有一项返回 TRUE,则这个方法返回 TRUE
    while(array.some(item => Array.isArray(item)))
    // ES6 增加了扩展运算符,用于取出参数对象的所有可遍历属性,拷贝到当前对象之中:
        array = [].concat(...array)
        console.log(...array)
    
    return array


// 方法4、5:先转成字符串,再变回数组
function flat4(array)
    //[1,[2,3]].toString()  =>  1,2,3
    return array.toString().split(',').map(item => parseInt(item))


function flat5(array)
    return array.join(',').split(',').map(item => Number(item))

9.手写数组乱序

// 方法1: sort + Math.random()
function shuffle1(arr)
    return arr.sort(() => Math.random() - 0.5);// 


// 方法2:时间复杂度 O(n^2)
// 随机拿出一个数(并在原数组中删除),放到新数组中
function randomSortArray(arr) 
    let backArr = [];
    while (arr.length) 
        let index = parseInt(Math.random() * arr.length);
        backArr.push(arr[index]);
        arr.splice(index, 1);
    
    return backArr;


// 方法3:时间复杂度 O(n)
// 随机选一个放在最后,交换
function randomSortArray2(arr) 
    let lenNum = arr.length - 1;
    for (let i = 0; i < lenNum; i++) 
        let index = parseInt(Math.random() * (lenNum + 1 - i));
        [a[index],a[lenNum - i]] = [a[lenNum - i],a[index]]
    
    return arr;

10.手写 Promise.all()、Promise.race()

PS: 有能力的可以去写下 Promise 和其他的 Promise 方法

function myAll(promises)
    // 问题关键:什么时候要执行resolve,什么时候要执行 reject
    return new Promise((resolve,reject) => 
        values = []
        // 迭代数组中的 Promise,将每个 promise 的结果保存到一个数组里
        promises.forEach(promise => 
            // 如果不是 Promise 类型要先包装一下
            // 调用 then 得到结果
            Promise.resolve(promise).then(res => 
                values.push(res)
                // 如果全部成功,状态变为 fulfilled
                if(values.length === promises.length)
                    resolve(values)
                
                ,err =>  // 如果出现了 rejected 状态,则调用 reject() 返回结果
                reject(err)
            )
        )
    
)

11.手撕快排

PS: 常见的排序算法,像冒泡,选择,插入排序这些最好也背一下,堆排序归并排序能写则写。万一考到了呢,要是写不出就直接回去等通知了。

const _quickSort = array => 
    // 补全代码
    quickSort(array, 0, array.length - 1)
    // 别忘了返回数组
    return array


const quickSort = (array, start, end) => 
    // 注意递归边界条件
    if(end - start < 1)    return
    // 取第一个数作为基准
    const base = array[start]
    let left = start
    let right = end
    while(left < right)
        // 从右往左找小于基准元素的数,并赋值给右指针 array[right]
        while(left < right &&  array[right] >= base)    right--
        array[left] = array[right]
        // 从左往右找大于基准元素的数,并赋值给左指针 array[left]
        while(left < right && array[left] <= base)    left++
        array[right] = array[left]
    
    // 双指针重合处,将基准元素填到这个位置。基准元素已经事先保存下来了,因此不用担心上面的赋值操作会覆盖掉基准元素的值
    // array[left] 位置已经确定,左边的都比它小,右边的都比它大
    array[left] = base
    quickSort(array, start, left - 1)
    quickSort(array, left + 1, end)
    return array

12.手写 JSONP

// 动态的加载js文件
function addScript(src) 
  const script = document.createElement('script');
  script.src = src;
  script.type = "text/javascript";
  document.body.appendChild(script);

addScript("http://xxx.xxx.com/xxx.js?callback=handleRes");
// 设置一个全局的callback函数来接收回调结果
function handleRes(res) 
  console.log(res);


// 接口返回的数据格式,加载完js脚本后会自动执行回调函数
handleRes(a: 1, b: 2);

13.手写寄生组合继承

PS: 组合继承也要能写出来

function Parent(name) 
  this.name = name;
  this.say = () => 
    console.log(111);
  ;

Parent.prototype.play = () => 
  console.log(222);
;
function Children(name,age) 
  Parent.call(this,name);
  this.age = age

Children.prototype = Object.create(Parent.prototype);
Children.prototype.constructor = Children;
// let child = new Children("111");
// // console.log(child.name);
// // child.say();
// // child.play();

14.数组/字符串操作题

可以自己找些基础的练一下,就不一一列举了

15.手写​二分查找​

//  迭代版
function search(nums, target) 
  // write code here
    if(nums.length === 0)    return -1
    let left = 0,right = nums.length - 1
        // 注意这里的边界,有等号
    while(left <= right)
        let mid = Math.floor((left + right) / 2)
        if(nums[mid] < target)    left = mid + 1
        else if(nums[mid] > target)    right = mid - 1
        else    return mid
    
    return -1

// 递归版
function binary_search(arr, low, high, key) 
    if (low > high) 
        return -1;
    
    var mid = parseInt((high + low) / 2);
    if (arr[mid] == key) 
        return mid;
     else if (arr[mid] > key) 
        high = mid - 1;
        return binary_search(arr, low, high, key);
     else if (arr[mid] < key) 
        low = mid + 1;
        return binary_search(arr, low, high, key);
    
;

16.手写函数柯里化

function sum(x,y,z) 
    return x + y + z


function hyCurrying(fn) 
    // 判断当前已经接收的参数的个数,和函数本身需要接收的参数是否一致
    function curried(...args) 
        // 1.当已经传入的参数 大于等于 需要的参数时,就执行函数
        if(args.length >= fn.length)
            // 如果调用函数时指定了this,要将其绑定上去
            return fn.apply(this, args)
        
        else
            // 没有达到个数时,需要返回一个新的函数,继续来接收参数
            return function(...args2) 
                //return curried.apply(this, [...args, ...args2])
                // 接收到参数后,需要递归调用 curried 来检查函数的个数是否达到
                return curried.apply(this, args.concat(args2))
            
        
    
    return curried


var curryAdd = hyCurry(add1)

curryAdd(10,20,30)
curryAdd(10,20)(30)
curryAdd(10)(20)(30)

其他

1.手写事件委托

2.手写组合函数

3.常见DOM操作

4.手写数组常见方法 Array.filter/map/fill/reduce

5.手写Object.create()

6.手写Object.is()

7.手写Object.freeze()

8.手写列表转树

9.数字千分位分割

10.下划线转驼峰

11.大数相加

场景模拟题

高频

1.实现 sleep 函数

async function test() 
    console.log('开始')
    await sleep(4000)
    console.log('结束')


function sleep(ms) 
    return new Promise(resolve => 
        setTimeout(() => 
            resolve()
        , ms)
    )

test()

2.setTimeout 实现 setInterval

function setInterval(fn, time)
    var interval = function()
    // time时间过去,这个异步被执行,而内部执行的函数正是interval,就相当于进了一个循环
        setTimeout(interval, time);
    // 同步代码
        fn();
    
    //interval被延迟time时间执行
    setTimeout(interval,time); 

3.异步循环打印 1,2,3

var sleep = function (time, i) 
  return new Promise(function (resolve, reject) 
    setTimeout(function () 
      resolve(i);
    , time);
  )
;


var start = async function () 
  for (let i = 1; i <= 3; i++) 
    let result = await sleep(1000, i);
    console.log(result);
  
;

start();

4.循环打印红、黄、绿

function red() 
    console.log('red');

function green() 
    console.log('green');

function yellow() 
    console.log('yellow');


const task = (timer, light) => 
    new Promise((resolve, reject) => 
        setTimeout(() => 
            if (light === 'red') 
                red()
            
            else if (light === 'green') 
                green()
            
            else if (light === 'yellow') 
                yellow()
            
            resolve()
        , timer)
    )

const taskRunner =  async () => 
    await task(3000, 'red')
    await task(2000, 'green')
    await task(2100, 'yellow')
    taskRunner()

taskRunner()

其他

Promise 并发控制相关的题目,例如实现有并行限制的 Promise 调度器

更多 Promise 的面试题在这里:要就来45道Promise面试题一次爽到底,面大厂的兄弟可以看看

进阶

1.手写 Promise(重要)

2.手写发布-订阅模式

3.手写观察者模式

4.手写双向绑定

5.手写 Event Bus

6.手写 LRU

最全Java面试题及答案整理(2023最新版)

所有的面试题目都不是一成不变的,面试题目只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。

前言

面试前还是很有必要针对性的刷一些题,很多朋友的实战能力很强,但是理论比较薄弱,面试前不做准备是很吃亏的。这里整理了很多面试常考的一些面试题,希望能帮助到你面试前的复习并且找到一个好的工作,也节省你在网上搜索资料的时间来学习。

第1-10期Java面试题整理

面试官:有了 for 循环 为什么还要 forEach ?

面试官:jwt 是什么?java-jwt 呢?懵逼了。。。

面试官:谈谈为什么要限流,有哪些限流方案?

面试官:HTTPS 是如何保证传输安全的?又被问了!

面试官:说说 WebSocket 和 Socket 及 Http 的区别?

面试官:RocketMQ 如何保证消息不丢失,如何保证消息不被重复消费?

面试官:ElasticSearch是什么?应用场景是什么?

面试官:说说Java并发运行中的一些安全问题

面试官:你了解 QPS、TPS、RT、吞吐量 这些高并发性能指标吗?

面试官:集群高并发环境下如何保证分布式唯一全局ID生成?

第11-20期Java面试题整理

面试官:思考Tomcat 类加载器为什么要违背双亲委派模型?

面试官:Java8 lambda 表达式 forEach 如何提前终止?

面试官:说一下SpringBoot的自动配置原理

面试官:线程池灵魂8连问,你挡的住吗?

面试官:熟悉设计模式吗?谈谈简单工厂模式和策略模式的区别

面试官:Kafka 为什么会丢消息?

面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!

面试官:Java 序列化和反序列化为什么要实现 Serializable 接口?

面试官:如何正确的停掉线程?

面试官:线程池执行过程中遇到异常会发生什么,怎样处理?

第21-30期Java面试题整理

面试官: 美团外卖的分库分表怎么设计?

面试官:MySQL 中 InnoDB 的索引结构以及使用 B+ 树实现索引的原因

面试官:JDK1.8 创建线程池有哪几种方式?

面试官:说说Java线程的状态及转换

面试官:Hash 碰撞是什么?如何解决?

面试官:说一下TCP和UDP协议的原理和区别?

面试官:线程池中线程抛了异常,该如何处理?

面试官:什么是微服务 ?

面试官:将 Bean 放入 Spring 容器中的方式有哪些 ?

面试官:当你的JVM 堆内存溢出后,其他线程是否可继续工作?

第31-40期Java面试题整理

面试官:Java反射和new效率对比,差距有多大?

面试官:谈谈过滤器和拦截器的区别?

面试官:说一下Redis中有哪些阻塞点以及如何解决?

面试官:说一下加密后的数据如何进行模糊查询?

面试官:电商库存扣减如何设计?如何防止超卖?

面试官:MySQL分表后,如何做分页查询?

面试官:select......for update会锁表还是锁行?

面试官:ReadWriteLock读写之间互斥吗?

面试官:线程池中多余的线程是如何回收的?

面试官:说一下SSO 单点登录和 OAuth2.0 的区别

第41-50期Java面试题整理

面试官:业务开发时,接口不能对外暴露怎么办?

面试官:为什么不建议在 MySQL 中使用 UTF-8 ?

面试官:MySQL 上亿大表如何优化?

面试官:你说说 Mysql 索引失效有哪些场景?

面试官:Java 类加载过程是怎么样的?

面试官:线程池执行的用户任务抛出异常会怎样?

面试官:线上MySQL的自增id用尽怎么办?

面试官:为什么HashMap 使用的时候指定容量?

面试官:百万数据的导入导出解决方案,怎么设计?

面试官:说一下海量请求下的接口并发解决方案

第51-60期Java面试题整理

面试官:说一下线程、线程锁与线程池 !

面试官:说说你了解的分布式 ID 生成方案

面试题:接口幂等性是什么?如何设计?

面试官:有一个 List 对象集合,如何优雅地返回给前端?

面试官:Spring @bean 和 @component 注解有什么区别?

面试官:在 Java 中 new 一个对象的流程是怎样的?彻底被问懵了。。

面试官:说一下公司常用MySQL分库分表方案

面试官:你知道 Java 中的回调机制吗?

面试官:Spring 用到了哪些设计模式?

面试官:MyBatis批量插入的五种方式,哪种最强?

第61-70期Java面试题整理

面试官:你怎么理解System.out.println() ?

面试官:数据库日期类型字段,需要兼容不同数据库,应该如何选择?

面试官:Java 对象不使用时,为什么要赋值 null ?

面试官:为什么生产环境中,建议禁用 Redis 的 keys 命令?

面试官:为什么大家都说 Java 反射慢,它到底慢在哪?

面试官:你工作中做过 JVM 调优吗?怎么做的?

面试官:说说什么是本地缓存、分布式缓存以及多级缓存,它们各自的优缺点?

面试官:什么是自旋锁?自旋的好处和后果是什么呢?

面试题:说一下MyBatis动态代理原理?

面试官:说一下你对 OAuth2 协议原理的理解?

第71-80期Java面试题整理

面试官:Spring 中获取 Bean 的方式有哪些?

面试官:MySQL自增主键为什么不是连续的?

面试官:什么是负载均衡?常见的负载均衡策略有哪些?

面试官:Docker 有几种网络模式?很多5 年工作经验都答不上来。。

面试官:项目中如何解决跨域问题(HttpClient、注解、网关)

面试官:说说 Cookie、Session、Token、JWT?

面试官:为什么要用多线程?公司项目中用到了哪些场景?

面试官:为什么 wait/notify 必须与 synchronized 一起使用??

面试官:MySQL为什么选择B+树作为索引结构

面试官:千万量级数据中查询 10W 量级的数据有什么方案?

第81-90期Java面试题整理

面试官:为什么数据库连接池不采用 IO 多路复用?

面试官:说一下Nginx静态压缩和代码压缩

面试官:多线程事务怎么回滚?

面试官:Java Switch 是如何支持 String 的,为什么不支持 long

面试官:公司规定所有接口都用 post 请求,这是为什么?

面试官:Redis的keys命令到底有多慢?

面试官:为什么说MySQL单表行数不要超过2000w ?

面试官:Java 内存泄漏了,怎么排查?

面试官:Spring IOC 为什么能降低耦合?

面试官:说说布隆过滤器与布谷鸟过滤器?应用场景?

第91-100期Java面试题整理

面试官:MySQL 日期时间类型怎么选?

面试官:用户在浏览器输入 URL 回车后,会发生什么?

面试官:Java有线程安全的set吗?

面试官:HashMap 为什么不能一边遍历一遍删除

面试官:Java 是如何实现线程间通信的?

面试官:为啥索引可以让查询变快?

面试官:ThreadLocal 搭配线程池时为什么会造成内存泄漏?

面试官:MySQL 中的 distinct 和 group by 哪个效率更高?

面试官:说说 @Configuration 和 @Component 的区别

面试官:如何停止一个正在运行的线程?

总结

所有的面试题目都不是一成不变的,面试题目只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。

特意整理出了有分类目录的Java面试题,方便大家平时复习和收藏。希望正在准备面试的朋友们能顺顺利利找到自己心仪的工作!!!

  • Java基础面试题(一)

  • Java 基础面试题(二)

  • Java 多线程与并发编程

  • JVM面试题

  • MySQL面试题

  • Spring面试题

  • Spring Boot 面试题

  • Spring Cloud面试题

  • Redis 面试题

  • MyBatis 面试题

  • ZooKeeper 面试题

  • Dubbo 面试题

  • Elasticsearch 面试题

  • kafka 面试题

  • RabbitMQ 面试题

  • Linux 面试题

  • 微服务面试题

这些Java面试题都整理打包好了→:点击下载

所有的面试题目都不是一成不变的,面试题目只是给大家一个借鉴作用,最主要的是给自己增加知识的储备,有备无患。

希望正在准备面试的朋友们能顺顺利利找到自己心仪的工作!!!

以上是关于前端面试题及答案整理(2022最新版)的主要内容,如果未能解决你的问题,请参考以下文章

vue3面试题:2022 最新前端 Vue 3.0 面试题及答案(持续更新中……)

2022最新Android面试题及答案整理(共计4176页PDF)包含腾讯字节百度小米阿里等大厂面试真题

JVM面试题及答案整理(最新版)

前端面试题及答案整理(转)

前端面试题及答案整理(转)

Java面试题,Java面试题及答案,2021最新的,都整理好了