2020前端最新面经:我又又又又换工作了(答案篇)
个人答案仅供参考,偏颇之处,欢迎补充及指正
01js基础:
一、js继承的多种方式以及优缺点?
答案:可参阅这篇文章:
由浅入深:彻底弄懂JS原型与继承
二、阅读以下代码,说出输出结果?
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);
四、阅读以下代码,说出输出结果,并解析其原因?
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,你真的懂吗?
02es6篇:
一、async/await能否单独使用?
async作为一个关键字放到函数前面,async函数执行会返回一个promise对象,并且把内部的值进行promise的封装。如果只是async, 和promise 差不多,但有了await就不一样了, await 关键字只能放到async 函数里面,await是等待的意思。它后面可以放任何表达式,不过我们更多的是放一个返回promise 对象的表达式,它等待的是promise 对象的执行完毕,并返回结果。
所以async可以单独使用,await不能,会报错
二、es6模块化如何使用?
export default xxximport xxx from './'export xxximport {xxx} from './'
追问:CommonJS 、 AMD、CMD 模块化的区别?commonJs应用于服务器端,NodeJS是CommonJS规范的实现。AMD、CMD应用于客户端,AMD(异步模块定义)规范, CMD (通用模块定义)规范
三、手写一个promise?
03class 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执行报错,直接执行rejecttry{executor(resolve, reject);} catch (err) {reject(err);}}}
浏览器原理篇:
一、说下浏览器垃圾回收机制?
JS的回收机制分两种:1.标记清除 2.引用计数。各大浏览器常用的是前者
二、在浏览器中输入一个网址后,发生了什么?
第一步 浏览器通过DNS查找该域名的 IP 地址
第二步 浏览器根据解析得到的IP地址向 web 服务器发送一个 HTTP 请求
第三步 服务器收到请求并进行处理
第四步 服务器返回一个响应
第五步 浏览器对该响应进行解码,解析html为dom、解析css 为css-tree、dom+ css 生成render-tree 绘图
第六步 页面显示完成后,浏览器发送异步请求。
第七步 整个过程结束之后,浏览器关闭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
四、说一下浏览器缓存机制?
答案:可参阅这篇文章
04http篇:
一、常用的 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的几种取值)
1. Content-Type: application/json :请求体中的数据会以json字符串的形式发送到后端
2. Content-Type: application/x-www-form-urlencoded:请求体中的数据会以普通表单形式(键值对)发送到后端
3. 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=> {//全局请求头中加tokenconst 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.dataconst {code} = dataAxiosif (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请求适合于请求缓存
05webpack篇:
一、是否使用过priofill?
babel-polyfill:(解决浏览器不支持es6的问题)
使用方法:1 npm i babel-polyfill --save2 在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)}
06plugin是用来扩展webpack功能,他在构建流程里注入钩子来实现,为webpack带来了很大的灵活性。在webpack运行的生命周期中会广播许多事件,plugin可以监听这些事件,在特定的时刻调用webpack提供的API执行相应的操作。const pluginName = 'ConsoleLogOnBuildWebpackPlugin';class ConsoleLogOnBuildWebpackPlugin {// 构造器参数,用于传递optionsconstructor(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源码,并不是几句话能解析清楚的,推荐大家看这篇文章。(https://blog.csdn.net/weixin_39843414/article/details/106152585)
三、vuex的数据,刷新页面后为空,在哪个阶段重新赋值?
写mutation里面,根页面调用赋值到state
四、vue中怎么重置data?
Object.assign(this.$data, this.$options.data())五、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 falseconst getKey = (value) => {return Math.floor(value / (t + 1))}const map = new Map()let l = 0while (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 阶答案:【leetcode 70题】【简单】里面有详细的视频讲解可以查阅08var 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];};
项目篇:
项目篇就是一个开放题了,没有任何标准答案,但大家自己写在简历上的项目一定要理解每个技术点。可以从项目的亮点,还有自己遇到过的有代表性的问题及解决方案等方面准备。自己被问到的几个点,贴出来供大家参考下一、首次加载,优化项目的加载速度
代码:异步加载、图片懒加载
提取公共的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"},
不要引用同级分包的内容包括
tabBar不能分包
每个分包不要超过2M
愿你遇贵人
面试是一个双向选择的过程,在面试的时候不要把自己当成一个被挑选的弱势者。前端知识杂却多,有时面试结果不理想,不要怀疑自己。只是在某个方面自己还有欠缺,之后补上就可以了。我们不是在祈求别人给我们一份工作,我们只是在找寻一个适合我们的公司。不妄自菲薄更不骄傲自负,整理好心态,你一定能找到适合自己的公司!!!加油啊

重大消息前端小班课(2-6人),前端一对一技术解答、面试指导,都可以添加前端码头小助的微信,进行咨询
往期推荐0102
28岁程序员郭宇身价过亿从字节跳动退休,又是心态蹦了的一天?
觉得有用
记得点个在看哦~???
