2022前端面试:我又又又换工作了(含前端面试题及答案)

678 阅读9分钟

2022前端最新面经,已拿offer。包含js基础、es6、浏览器原理、http、webpack、框架、算法、项目等内容。个人答案仅供参考,偏颇之处,欢迎留言补充及指正

找工作第一步肯定是投递简历了,下面这些是自己在boss上投递简历,曾被问过的点

  • 第一条:工作年限
  • 第二条:学历(本科及以上,学信网截图)
  • 第三条:英语是否过四六级
  • 第四条:leetcode做题截图

js基础:

js继承的多种方式以及优缺点

[由浅入深:彻底弄懂JS原型与继承​

](mp.weixin.qq.com/s/lw_Ewfc-p…)

作用域

阅读以下代码,说出输出结果

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

输出5个5

追问:如果希望按顺序输出01234,应该怎么修改,尽可能写出多种方式

第一种:
 for (let i = 0; i < 5; i++) {
    setTimeout(function() {
       console.log(i);
    }, 1000);
 }
 第二种:
  for (var i = 0; i < 5; i++) {
       (function(j) {  
        setTimeout(function() {
            console.log(j);
        }, 1000);
       })(i);
  }
  第三种:
  for (var i = 0; i < 5; i++) {
     setTimeout(function(j) {
        console.log(j);
     }, 1000, i);
  }

说一说事件代理,优缺点是什么?实现原理是什么?

事件代理是指将事件绑定到目标元素的父元素上,利用冒泡机制触发该事件

优点:

  • 可以减少事件注册,节省大量内存占用
  • 可以将事件应用于动态添加的子元素上

缺点: 使用不当会造成事件在不应该触发时触发

ulEl.addEventListener('click', function(e){
    var target = event.target || event.srcElement;
    if(!!target && target.nodeName.toUpperCase() === "LI"){
        console.log(target.innerHTML);
    }
}, false);

Event Loop

阅读以下代码,说出输出结果,并解析其原因

   console.log('a');
    setTimeout(function () {
        console.log('b');
        new Promise(function (resolve) {
            console.log('c');
            resolve();
        }).then(function () {
            console.log('d')
        })
    })
    new Promise(function (resolve) {
        console.log('e');
        resolve();
    }).then(function () {
        console.log('f')
    })

//aefbcd

这道题主要考的是Event Loop(事件循环),有不懂的地方,可以阅读这篇文章。

[面试官常问的Event Loop,你真的懂吗?​

](mp.weixin.qq.com/s/UBCa-kYgS…)

es6篇:

async/await能否单独使用?

async作为一个关键字放到函数前面,async函数执行会返回一个promise对象,并且把内部的值进行promise的封装。如果只是async, 和promise 差不多,但有了await就不一样了, await 关键字只能放到async 函数里面,await是等待的意思。它后面可以放任何表达式,不过我们更多的是放一个返回promise 对象的表达式,它等待的是promise 对象的执行完毕,并返回结果。 所以async可以单独使用,await不能,会报错

es6模块化如何使用?

export default  xxx
import xxx from './'

export xxx
import {xxx} from './'

追问:CommonJS 、 AMD、CMD 模块化的区别?

commonJs应用于服务器端,NodeJS是CommonJS规范的实现。AMD、CMD应用于客户端,AMD(异步模块定义)规范, CMD (通用模块定义)规范

手写一个promise?

class Promise{
  constructor(executor){
    // 初始化state为等待态
    this.state = 'pending';
    // 成功的值
    this.value = undefined;
    // 失败的原因
    this.reason = undefined;
    let resolve = value => {
      // state改变,resolve调用就会失败
      if (this.state === 'pending') {
        // resolve调用后,state转化为成功态
        this.state = 'fulfilled';
        // 储存成功的值
        this.value = value;
      }
    };
    let reject = reason => {
      // state改变,reject调用就会失败
      if (this.state === 'pending') {
        // reject调用后,state转化为失败态
        this.state = 'rejected';
        // 储存失败的原因
        this.reason = reason;
      }
    };
    // 如果executor执行报错,直接执行reject
    try{
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
}

这只是其基本内容,还有它的.then、链式调用等,大家尝试扩展下。但面试时间有限,能写到这样就可以了。

浏览器原理篇:

浏览器垃圾回收机制

JS的回收机制分两种:1.标记清除 2.引用计数。各大浏览器常用的是前者

在浏览器中输入一个网址后,发生了什么?

  1. 第一步 浏览器通过DNS查找该域名的 IP 地址
  2. 第二步 浏览器根据解析得到的IP地址向 web 服务器发送一个 HTTP 请求
  3. 第三步 服务器收到请求并进行处理
  4. 第四步 服务器返回一个响应
  5. 第五步 浏览器对该响应进行解码,解析html为dom、解析css 为css-tree、dom+ css 生成render-tree 绘图
  6. 第六步 页面显示完成后,浏览器发送异步请求。
  7. 第七步 整个过程结束之后,浏览器关闭TCP连接。

如何最小化重绘和重排?

  • 需要要对元素进行复杂的操作时,可以先隐藏(display:"none"),操作完成后再显示
  • 需要创建多个 DOM 节点时,使用 DocumentFragment 创建完后一次性的加入 document
  • 缓存 Layout 属性值,如:var left = elem.offsetLeft; 这样,多次使用 left 只产生一次回流
  • 尽量避免用 table 布局(table 元素一旦触发回流就会导致 table 里所有的其它元素回流)
  • 避免使用 css 表达式(expression),因为每次调用都会重新计算值(包括加载页面)
  • 尽量使用 css 属性简写,如:用 border 代替 border-width, border-style, border-color批量修改元素样式;elem.className 和 elem.style.cssText 代替 elem.style.xxx

说一下浏览器缓存?

强缓存和协商缓存,具体内容可以看这篇文章,有详细讲解

[【前端面试必问】浏览器缓存原理?---送你满分答案​

](mp.weixin.qq.com/s/7U-poWxaq…)

http篇:

常用的 http 状态码,表达含义是什么?

状态代码有三位数字组成,第一个数字定义了响应的类别,且有五种可能取值:

  • 1xx:指示信息--表示请求已接收,继续处理

  • 2xx:成功--表示请求已被成功接收、理解、接受

  • 3xx:重定向--要完成请求必须进行更进一步的操作

  • 4xx:客户端错误--请求有语法错误或请求无法实现

  • 5xx:服务器端错误--服务器未能实现合法的请求

常见状态代码、状态描述、说明:

  • 200 OK //客户端请求成功

  • 400 Bad Request //客户端请求有语法错误,不能被服务器所理解

  • 401 Unauthorized //请求未经授权,这个状态代码必须和WWW-Authenticate报头域一起使用

  • 403 Forbidden //服务器收到请求,但是拒绝提供服务

  • 404 Not Found //请求资源不存在,eg:输入了错误的URL

  • 500 Internal Server Error //服务器发生不可预期的错误

  • 503 Server Unavailable //服务器当前不能处理客户端的请求,一段时间后可能恢复正常

post请求常见的数据格式(content-type的几种取值)

Content-Type: application/json : 请求体中的数据会以json字符串的形式发送到后端 Content-Type: application/x-www-form-urlencoded:请求体中的数据会以普通表单形式(键值对)发送到后端 {0}. Content-Type: multipart/form-data: 它会将请求体的数据处理为一条消息,以标签为单元,用分隔符分开。既可以上传键值对,也可以上传文件。

封装过axios实例吗?大概说一下

这是我简单封装的代码,主要就是请求拦截和响应拦截,全局token的封装以及全局报错提示

import axios from 'axios';
const instance = axios.create({
    baseURL: "",
    timeout: 5000, // 请求超时时间
    headers: {
        "Access-Control-Allow-Origin": "*"
    },
});
instance.interceptors.request.use(
    config=> {
      //全局请求头中加token
      const token = window.localStorage.getItem('access_token')
      token && (config.headers.authorization = `Bearer ${token}`)
      return config;
    },
     error => {
        return Promise.reject(error);
    }
);
//响应拦截器
instance.interceptors.response.use(
   response => {
     //根据返回的code值,进行一些业务的处理
    const dataAxios = response.data
    const {code} = dataAxios
    if (code === undefined) {
      return dataAxios
    } else {
      if(code===200||(code>401&&code<500)||code===400){
          return dataAxios
      }else if(code===401){
          if (__CLIENT__) {
              cookies.remove('access_token');
              cookies.remove('refresh_token');
              window.localStorage.removeItem("access_token");
              window.localStorage.removeItem("refresh_token");
          }else {
              errorCreat(`${dataAxios.code}: ${response.config.url}`, dataAxios.code);
          }
      }else{
          errorCreat(`${dataAxios.code}: ${response.config.url}`, dataAxios.code);
      }
   }
   error => {
   }
)
export default instance;

为什么get请求适合缓存,而post请求不适合

get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。 post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存

webpack篇:

是否使用过priofill?

babel-polyfill:(解决浏览器不支持es6的问题)

使用方法:

  • 1 npm i babel-polyfill --save

  • 2 在main.js中 import ‘babel-polyfill’

原理就是把es6的语法转换成es5的语法

lodaer 与 plugins 的区别?

loader是在打包构建过程中用来处理源文件的(JSX,Scss,vue等),一次处理一个。plugins并不直接操作单个文件,它直接对整个构建过程其作用。

追问:如何实现一个 lodaer和plugins?

Loader就是⼀个声明式函数,不能⽤箭头函数。拿到源代码,作进⼀步的修饰处理,再返回处理后的源码
module.exports= function(source){
  //source就是传递进来的源码
  //this.query 就是loader使用时传递进来的options的参数
  console.log(source, this ,this.query)
  return source.replace('webpack',this.query.name)
}

plugin是用来扩展webpack功能,他在构建流程里注入钩子来实现,为webpack带来了很大的灵活性。在webpack运行的生命周期中会广播许多事件,plugin可以监听这些事件,在特定的时刻调用webpack提供的API执行相应的操作。

const pluginName = 'ConsoleLogOnBuildWebpackPlugin';

class ConsoleLogOnBuildWebpackPlugin {
  // 构造器参数,用于传递options
  constructor(options) {
    console.log("current plugin option is" + JSON.stringify(options))
  }
  // apply 方法是一个插件所必须的
  apply(compiler) {
    // compiler 继承自 tapable
    // tapable  提供了多种 hooks  https://github.com/webpack/tapable#hook-types
    // run      是 AsyncSeriesHook实例 [tapable提供的多种hooks的一种]
    compiler.hooks.run.tap(pluginName, compilation => {
      console.log('webpack 构建过程开始!');
    });
  }
}

module.exports = ConsoleLogOnBuildWebpackPlugin

框架篇:

vuecli3配置webpack的地方?

chainWebpack、configureWebpack

vue计算属性缓存原理?

这涉及到了vue源码,并不是几句话能解析清楚的,推荐大家看这篇文章。

[Vue 的计算属性如何实现缓存?(原理深入揭秘)​

](blog.csdn.net/weixin_3984…)

vuex的数据,刷新页面后为空,在哪个阶段重新赋值?

写mutation里面,根页面调用赋值到state

Vue 组件间通信有哪些方式?

  • props/emit
  • emit/on
  • vuex
  • attrs/listeners
  • provide/inject
  • parent/children与ref

vue组件中 data 什么时候可以使用对象?

  • 组件复用时所有组件实例都会共享 data,如果 data 是对象的话,就会造成一个组件修改 data 以后会影响到其他所有组件,所以需要将 data 写成函数,每次用到就调用一次函数获得新的数据。
  • 当我们使用 new Vue() 的方式的时候,无论我们将 data 设置为对象还是函数都是可以的,因为 new Vue() 的方式是生成一个根组件,该组件不会复用,也就不存在共享 data 的情况了

数据结构和算法:

题目一、

在整数数组 nums 中,是否存在两个下标 i 和 j,使得 nums [i] 和 nums [j] 的差的绝对值小于等于 t ,且满足 i 和 j 的差的绝对值也小于等于 ķ 。

如果存在则返回 true,不存在返回 false。

示例:

输入: nums = [1,2,3,1], k = 3, t = 0
输出: true

leetcode 220题】【中等】

var containsNearbyAlmostDuplicate = function(nums, k, t) {
  if (k < 0 || t < 0) return false
  const getKey = (value) => {
    return Math.floor(value / (t + 1))
  }
  const map = new Map()
  let l = 0
  while (l < nums.length) {
    const key = getKey(nums[l])
    if (map.has(key)) {
      return true
    } else if (map.has(key + 1) || map.has(key - 1)) {
      if (map.get(key + 1) - nums[l] <= t) { return true }
      if (nums[l] - map.get(key - 1) <= t) { return true }
    }
    map.set(key, nums[l])
    if (l >= k) {
      map.delete(getKey(nums[l - k]))
    }
    l++
  }
  return false
}

题目二、

假设你正在爬楼梯。需要 n 阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

**注意:**给定 n 是一个正整数。

示例:

输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

var climbStairs = function(n) {
    const dp = [];
    dp[0] = 1;
    dp[1] = 1;
    for(let i = 2; i <= n; i++) {
        dp[i] = dp[i - 1] + dp[i - 2];
    }
    return dp[n];
};

leetcode 70题】【简单】里面有详细的视频讲解可以查阅

项目篇:

项目篇就是一个开放题了,没有任何标准答案,但大家自己写在简历上的项目一定要理解每个技术点。可以从项目的亮点,还有自己遇到过的有代表性的问题及解决方案等方面准备

自己被问到的几个点,贴出来供大家参考下:

首次加载,优化项目的加载速度

  • 代码:异步加载、图片懒加载

  • 提取公共的js,通过cdn的方式引入

  • 项目打包压缩,gizp

  • 分割js\和css, optimization

  • 浏览器缓存

项目中遇到印象最深的一个问题?

开放题,大家自由发挥即可

小程序分包?

小程序包太大了,超过了最大限制,这时候就需要进行分包处理。

{
  "pages": [
    "pages/Login/xxxx",
  ],
  // 这里就是贴入的代码 root\pages值可以任意改
  "subpackages":[{              
    "root":"packageA",
    "pages":[
      "pages/Login/bbb",
      "pages/Login/ccc",
    ]
  }],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle": "black"
  },    

注意几点:

  1. 不要引用同级分包的内容包括
  2. tabBar不能分包
  3. 每个分包不要超过2M

我个人不推荐背面试题,那是一种骗人骗己的行为。面试题只是让你了解前端行情以及进行自我的查漏补缺,发现当下的前端技术热点以及自我的薄弱点,再进行有侧重点的学习。如果你把前端当作一辈子的事业,那就不要投机取巧,好好学习。当你需要背面试题才能拿到offer,那说明现在的你还远远不够。

关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章

关注公众号前端码头,获取独家学习路线+博主私人微信,更多前端小干货等着你喔