面试题
最近公司有要招人,然后叫我负责一下技术方面的面试,然后问着问着,好像发现有一些知识点我也不是记得特别清楚。所以有必要列一下自己面试需要问的问题,并且把所有问题点给摸索清楚。不然被问住了就很尴尬了。。。
HTML
初级
1. HTML5有哪些新特性,你用过哪一些?
语义化标签、audio、video、canvas、geolocation、web worker、websocket
cookie、localStorage、sessionStorage的区别
cookie:每次请求都会被发送到服务器。不大于4k。可以自定义过期时间;服务端可以设置httponly,前端不可访问。
sessionStorage:页面关闭后失效(window.open新页面还会存在)
localStorage:永久存储
中级
如何在移动设备实现物理1像素的线
单边:1px的线然后再缩放 dpr 倍
四边:实现一个与目标元素dpr倍宽、dpr倍高的伪元素,然后整体缩放dpr倍。让其刚好覆盖到目标元素上。(兼容圆角)
网上的方案还有:border-image、viewport+rem、box-shadow来实现。但是最实用的还是上述方法。
升级:结合vue的指令可以更优雅的实现一像素问题。见项目笔记
高级
如何在页面间传值
- 监听storage事件,获取localStorage、sessionStorage 变化,从而传参(跨域限制)
- postMessage(无跨域限制、可用于iframe、openner)
- sharedWorker(高级、兼容性差)
CSS
初级
css选择器有哪些?以及他们的优先级
1.id选择器( # myid)
2.类选择器(.myclassname)
3.标签选择器(div, h1, p)
4.相邻选择器(h1 + p)
5.子选择器(ul > li)
6.后代选择器(li a)
7.通配符选择器( * )
8.属性选择器(a[rel = “external”])
9.伪类选择器(a:hover, li:nth-child)
id > class > 属性 > 标签 > 通配符
解释一下rem、em、px、vh、vw的区别
- rem:相对于<htm>的
font-size
:来定义的单位。 - em:相对于父元素的
font-size
来定义的单位 - px:物理像素,基本单位
- vh:100vh === 1浏览器可视区域高度
- vw:100vw === 1浏览器可视区域宽度
高级:
- vmin:相对于视口的宽度或高度中较小的那个。其中最小的那个被均分为100单位的vmin
- vmax:相对于视口的宽度或高度中较大的那个。其中最大的那个被均分为100单位的vmax
- rpx:微信小程序使用的单位,750rpx等于屏幕宽度
对盒子模型的理解,IE盒子模型和标准模型有什么区别
盒模型: 内容(content)、填充(padding)、边界(margin)、 边框(border)
其实区别在于box-sizing属性:
content-box:标准盒子模型;
border-box:IE盒子模型;content部分把 border 和 padding计算了进去
中级
高级
JS
初级
js基本数据类型
undefined、null、boolean、string、Number、Symble(ES6)
事件冒泡与事件捕获的区别
IE使用的是事件冒泡、另一种是事件捕获
事件冒泡:事件开始于最具体的元素、然后逐级向上传播到较为不具体的节点
事件捕获:由不太具体的节点应该更早接收到事件、而最具体的节点最后接收到事件
通过addEventListener的第三个参数来启用监听事件冒泡还是事件捕获:true,事件捕获;false:事件冒泡;所以平时使用的都是false居多
场景提问:如果有一个列表,有数千个选项要绑定事件,应该如何处理;
判断一个变量是不是function
typeof variable === ‘function’
是不是array
Object.prototype.toString.call(variable) === ‘[object Array]‘
中级
宏任务与微任务
1 | console.log(1); |
打印顺序:1、3、5、4、2
setTimeout是宏任务
,Promise是微任务
,微任务会比宏任务快,可以简单的理解,微任务是vip吧;
概念大概是这样的:js有个执行堆栈
和排队队列
的概念。如果是同步任务,则直接进入堆栈
。如果是异步任务,则进入队列
等待;
如果堆栈为空,则先查找微任务内的队列任务,再去查找宏任务的队列任务;
比较特殊的是,Node的process.nextTick()
;和前端的MessageChannel
(Vue的nextTick是利用MessageChannel来实现的)。该方法是直接把任务插入到堆栈底部,会比微任务与宏任务都快;
属于微任务的有:Promise、MutationObserver、process.nextTick(Node)
箭头函数与普通函数的区别
- this指向不一样。无法通过call、apply、bind从新修改this
- 没有arguments、也没有caller和callee;
- 不可以使用new 命令,会抛出错误
- 不可以使用yield命令,因此不能用作 Generator 函数。
高级
Set和Map数据结构
Vue
初级
组件的生命周期,加上keep-alive、vue-router之后会新增什么钩子函数
beforeCreate、created、beforeMounte、mounted、beforeUpdate、updated、beforeDestroy、destroyed
keep-alive第二次进入不会触发beforeCreate、created、beforeMounte、mounted、beforeDestroy、destroyed
;
新增activated
和 deactivated
加上vue-router之后会新增:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
created与mounted的区别,能不能在created里访问dom
created是组件实例加载完成,可访问data、prop、methods等等
mounted可以访问除了created能访问的东西 + dom
不能再created里访问dom
keep-alive
从场景中提问:比如一个分页组件,已经跳转到第三页,然后跳转到其他路由,再跳转回来需要还保留在第三页,应该用keep-alive;
中级
vue-router的不刷新加载实现原理是什么
vue-router有两种模式:hash和history;
hash:修改window.location.hash时,不会发生页面刷新、会给路由历史新增记录。且可以监听hashchange来监听hash的变化,以此来实现页面不刷新渲染页面。
history:利用history.pushstate方法来实现。也是不刷新页面、给路由历史新增记录。更多的是,也可以修改url的相对路径且不发生页面刷新加载;然后可以通过监听popstate事件来实现监听路由变化。
自定义指令
通过Vue.directive全局注册自定义指令或者directives: {} 局部注册自定义指令;
自定义指令用于给某个dom扩展特定的功能,自定义指令当中允许对当前dom进行直接操作;
钩子函数:
bind:被绑定到元素时
inserted:绑定元素被插入到父节点时(仅保证父节点存在)
update:所在组件的vNode更新时
componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
unbind:只调用一次,指令与元素解绑时调用。
钩子函数参数:
el:指令所绑定的元素,可以用来直接操作 DOM
binding:一个对象包含各种属性:name、value、oldValue、expression、arg、modifiers
vNode:
oldVnode:
minxins
在混入和组件数据冲突时以组件数据优先;
钩子函数会被合并、先调用混入的钩子函数
组件v-model
允许一个自定义组件在使用 v-model 时定制 prop 和 event。默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event
使用model选项可以修改默认的prop和event
model: {
prop: 'checked',
event: 'change'
}
高级
Vue.use一个插件到底做了什么
Vue.use用于安装插件,该方法需要传入一个function或者一个对象。如果传入的是function,则调用function。如果传入的是一个对象,则调用对象中的install方法。
Vue.use还做了一件事是防止重复安装插件
Vue.extends
Vue.extends接受一个对象,与Vue、Vue.components接收的参数完全一致。返回的是一个构造函数(constructor);new 该构造函数则返回一个组件实例(instance)。
该实例与new Vue生成的Vm实例、Vue.components生成的组件实例完全一致;不同的是一份配置=》一份构造函数=》多份实例。从而实现复用;
其他
初级
在提升代码性能做过哪些事情
发散、很多
中级
解释import的各种用法的区别
1 | import defaultExport from "module-name"; |
https与http的区别,怎样做到加密的
https是http + ssl;
然后:
- 客户端像服务器发送请求
- 服务器向客户端发送公钥
- 客户端先验证公钥有效性
- 通过后生成一个随机值(私钥),然后用公钥对随机值进行加密
- 服务器用公钥解析随机值,拿到私钥;这样双方都有了唯一私钥
- 则双方利用私钥对数据进行对称加密,进行数据传输
高级
CommonJs与ES6 module的区别
CommonJs:规范出来之前的野生规范,遵守CommonJS规范。在import规范出来之前被大规模使用
ES6 Module:ES6官方模块管理规范
使用:
- import 必须写在模块头部,必须在模块最外层;require可以在任何地方require;可以export多个代码
- 可以require export出来的模块;但是import不能引用module.export的模块;虽然可以,但是强烈不建议使用;只能定义一个modules.exports
原理:
- import是引用;require是立即运行
webpack怎样实现tree shaking(去除不必要的代码)
应该结合ES6的imoprt、export、DefinePlug、sideEffects和uglifyjs-webpack-plugin来实现;
原理是,用import、export或者DefinePlugin来定义部分不能访问的代码。然后uglifyjs-webpack-plugin在压缩js时识别不能访问的代码,从而去除掉多余的代码;
而sideEffects可以参考:Webpack 中的 sideEffects 到底该怎么用;
2022年8月11更新
tree shaking与sideEffect应该分开理解。
从tree shaking本身,其实不会有太多学问。只要代码中使用的是ESM模块,webpack就能分析出哪部分代码是不需要的。一般项目上有问题,大多是babel把ESM模板转化为commonJS。所以我们只需要指定babel不发生转换就行
.babelrc
1 | { |
代码我们自己可以控制,但是node_module里的第三方库里往往就有很多commonJS的代码。如果第三方库也提供ESM的导出的话,最好引用ESM的,否则只能全量加载。
以redux为例,其发布到Npm上的目录结构为:
1 | node_modules/redux |
package.json有两个字段:
1 | { |
则我们可以在webpack中配置resolve.mainFields
来指定优先加载哪种模块
1 | module.exports = { |
关于sideEffect
其实就是除了静态能分析之外做的其他额外操作,这种情况下webpack会尽可能的保留其副作用
。
如
1 | // a.js |
如果没有console.log(b(1))
的情况下,b方法是不会被打包的。有了console.log(b(1))
。则会输出一段
1 | console.log(function (v){return v}(1)) |
另一个例子。以 mobx-react-devtool 为例,我们通常这样去用:
1 | import DevTools from 'mobx-react-devtools'; |
这是一个很常见的按需导入场景,然而在没有 sideEffects: false
配置时,即便 NODE_ENV
设为 production
,打包后的代码里依然会包含 mobx-react-devtools
包,虽然我们没使用过其导出成员,但是 mobx-react-devtools
还是会被 import,因为里面“可能”会有副作用。但当我们加上 sideEffects false 之后,tree shaking 就能安全的把它从 bundle 里完整的移除掉了。
所以为了更进一步的tree shaking。这种情况下的sideEffect也应该清除掉。
那么需要做的是
在对应的包的package.json添加sideEffect标记
1 | { |
webpack配置
1 | module.exports = { |
或者直接mode设置为production
webpack就只会打包静态分析出来的模块。不考虑任何副作用。
在我们写模块时,尽可能的不要写有副作用的代码。仅再写一些polyfill 、 shim 或修改全局变量之类的事情时,sideEffect才应该设置为true,否则可以无脑false。