不积跬步无以至千里,下面的内容是对网上原有的前端面试题集及答案进行了全面修订之后给出的负责任的题目和答案。原来的题目中有很多重复题目和无价值的题目,还有不少的参考答案也是错误的。修改后的前端面试题集去掉了过时的技术和框架,补充了现代前端开发所需的相关知识,包括HTML5、CSS3、JavaScript最新特性和最佳实践、响应式设计、前端性能优化、前端框架等内容。同时,修改后的题目集还对常见的面试算法题和设计模式等进行了深入剖析,相信对准备前端工程师职位的候选人一定有所帮助。
这里会不断收集和更新web前端相关的面试题,目前已收集100题。
转发本文+关注+私信【学习】获取以下完整面试PDF
第1题:写React / Vue项目时为什么要在列表组件中写key,其作用是什么?
key是给每一个vnode的唯一id,可以依靠key,更准确,更快的拿到oldVnode中对应的vnode节点
第2题:['1".'2"."3].map(parselnt) what & why ?
第一眼看到这个题目的时候,脑海跳出的答案是 [1, 2, 3],但是真正的答案是[1,NaN, NaN]。
首先让我们回顾一下,map 函数的第一个参数 callback。这个 callback 一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。
arr.map(callback: (value: T, index: number, array: T[]) => U, thisArg?:any);
而 parseInt 则是用来解析字符串的,使字符串成为指定基数的整数。接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
parseInt(string, radix)
了解这两个函数后,我们可以模拟一下运行情况
parseInt('1', 0) //radix 为 0 时,且 string 参数不以“0x”和“0”开头时,按照 10 为基数处理。这个时候返回 1parseInt('2', 1) //基数为 1(1 进制)表示的数中,最大值小于 2,所以无法解析,返回 NaNparseInt('3', 2) //基数为 2(2 进制)表示的数中,最大值小于 3,所以无法解析,返回 NaN
第3题:什么是防抖和节流?有什么区别?如何实现?
第4题:介绍下Set、 Map. WeakSet和 WeakMap的区别?
Set——对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用
WeakSet——成员都是对象;成员都是弱引用,可以被垃圾回收机制回收,可以用来保存 DOM 节点,不容易造成内存泄漏;
Map——本质上是键值对的集合,类似集合;可以遍历,方法很多,可以跟各种数据格式转换。
WeakMap——只接受对象最为键名(null 除外),不接受其他类型的值作为键名;键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的;不能遍历,方法有 get、set、has、delete。
第5题:介绍下深度优先遍历和广度优先遍历,如何实现?
深度优先遍历——是指从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点的第一个未被访问的邻结点,然后再以此邻结点为顶点,继续找它的下一个顶点进行访问。重复此步骤,直至所有结点都被访问完为止。
广度优先遍历——是从某个顶点出发,首先访问这个顶点,然后找出刚访问这个结点所有未被访问的邻结点,访问完后再访问这些结点中第一个邻结点的所有结点,重复此方法,直到所有结点都被访问完为止。
//1.深度优先遍历的递归写法 function deepTraversal(node) {
let nodes = []
if (node != null) {
nodes.push[node]
let childrens = node.children
for (let i = 0;
i < childrens.length; i++) deepTraversal(childrens[i])
} return nodes}
//2.深度优先遍历的非递归写法 function deepTraversal(node) {
let nodes = []
if (node != null) {
let stack = []
//同来存放将来要访问的节点
stack.push(node)
while (stack.length != 0) {
let item = stack.pop()
//正在访问的节点
nodes.push(item)
let childrens = item.children
for (
let i = childrens.length - 1;
i >= 0;
i--
//将现在访问点的节点的子节点存入 stack,供将来访问 )
stack.push(childrens[i])
}
}
return nodes}
//3.广度优先遍历的递归写法 function wideTraversal(node) {
let nodes = [],
i = 0
if (node != null) {
nodes.push(node)
wideTraversal(node.nextElementSibling)
node = nodes[i++]
wideTraversal(node.firstElementChild)
}
return nodes}//4.广度优先遍历的非递归写法 function
wideTraversal(node) {
let nodes = [],
i = 0 while (node != null) {
nodes.push(node)
node = nodes[i++]
let childrens = node.children
for (let i = 0;
i < childrens.length;
i++) {
nodes.push(childrens[i])
}
}
return nodes
}
第6题:请分别用深度优先思想和广度优先思想实现一个拷贝函数?
let _toString = Object.prototype.toStringlet map = {
array: 'Array',
object: 'Object',
function: 'Function',
string: 'String',
null: 'Null',
undefined: 'Undefined',
boolean: 'Boolean',
number: 'Number'}let getType = (item) => {
return _toString.call(item).slice(8, -1)}let isTypeOf = (item, type)
=> {
return map[type] && map[type] =
}
第7题:ES5/ES6的继承除了写法以外还有什么区别?
1.ES5 的继承实质上是先创建子类的实例对象,然后再将父类的方法添加到 this 上(Parent.apply(this)).
2.ES6 的继承机制完全不同,实质上是先创建父类的实例对象 this(所以必须先调用父类的 super()方法),然后再用子类的构造函数修改 this。
3.ES5 的继承时通过原型或构造函数机制来实现。
4.ES6 通过 class 关键字定义类,里面有构造方法,类之间通过 extends 关键字实现继承。
5.子类必须在 constructor 方法中调用 super 方法,否则新建实例报错。因为子类没有自己的 this 对象,而是继承了父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类得不到 this 对象。
6.注意 super 关键字指代父类的实例,即父类的 this 对象。
7.注意:在子类构造函数中,调用 super 后,才可使用 this 关键字,否则报错。
第8题: setTimeout、Promise、Async/Await的区别
https://blog.csdn.net/yun_hou/article/details/88697954
第9题:Async/Await如何通过同步的方式实现异步
async 起什么作用——输出的是一个 Promise 对象
第10题:异步笔试题请写出下面代码的运行结果
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')}async function async2()
{
console.log('async2')}console.log('script
start')setTimeout(function()
{
console.log('setTimeout')},
0) async1()new Promise(function(resolve)
1) {
console.log('promise1')
resolve()}).then(function()
{
console.log('promise2')})console.log('script end')
//输出
//script start
//async1 start
//async2
//promise1
//script end
//async1 end
//promise2
//setTimeout
第11题:算法手写题
已知如下数组,编写一个程序将数组扁平化去并除其中重复部分数据,最终得
到一个升序且不重复的数组
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
答:使用 Set 方法去重,flat(Infinity)扁平化
Array.from(new Set(arr.flat(Infinity))).sort((a,b)=>{ return a-b})//[1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
第12题:JS 异步解决方案的发展历程以及优缺点。
1、回调函数(callback)
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
2、Promise
优点:解决了回调地狱的问题
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
3、Generator
特点:可以控制函数的执行,可以配合 co 函数库使用
4、Async/await
优点:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使
用 await 会导致性能上的降低。
第13题:Promise构造函数是同步执行还是异步执行,那么then方法呢?
const promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)})promise.then(() => {
console.log(3)})console.log(4)
执行结果是:1243,promise 构造函数是同步执行的,then 方法是异步执行的
第14题:情人节福利题,如何实现一个new
第15题:简单讲解一下http2的多路复用
HTTP2 采用二进制格式传输,取代了 HTTP1.x 的文本格式,二进制格式解析更高效。
多路复用代替了 HTTP1.x 的序列和阻塞机制,所有的相同域名请求都通过同一个 TCP 连接并发完成。
在 HTTP1.x 中,并发多个请求需要多个 TCP 连接,浏览器为了控制资源会有 6-8个 TCP 连接都限制。
HTTP2 中
同域名下所有通信都在单个连接上完成,消除了因多个 TCP 连接而带来的延时和内存消耗。
单个连接上可以并行交错的请求和响应,之间互不干扰
第16题:谈谈你对TCP三次握手和四次挥手的理
第17题:A、B机器正常连接后,B机器突然重启,问A此时处于TCP什么状态
如果 A 与 B 建立了正常连接后,从未相互发过数据,这个时候 B 突然机器重启,问 A 此时处于 TCP 什么状态?如何消除服务器程序中的这个状态?(超纲题,了解即可)
因为 B 会在重启之后进入 tcp 状态机的 listen 状态,只要当 a 重新发送一个数据包(无论是 syn 包或者是应用数据),b 端应该会主动发送一个带 rst 位的重置包来进行连接重置,所以 a 应该在 syn_sent 状态
第18题: React 中setState什么时候是同步的,什么时候是异步的?
1、由 React 控制的事件处理程序,以及生命周期函数调用 setState 不会同步更新 state 。
2、React 控制之外的事件中调用 setState 是同步更新的。比如原生 js 绑定的事件,setTimeout/setInterval 等。
第19题: React setState笔试题,下面的代码输出什么?
class Example extends React.Component {
constructor() {
super()
this.state = {
val: 0
}
}
componentDidMount() {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
// 第 1 次 log
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
// 第 2 次 log
setTimeout(() => {
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
// 第 3 次 log
this.setState({ val: this.state.val + 1 })
console.log(this.state.val)
// 第 4 次 log
}, 0)
}
render() {
return null
}
}
答:
0, 0, 2, 3
第20题:介绍下npm模块安装机制,为什么输入npm install就可以自动安装对应的
- 发出 npm install 命令 1 查询 node_modules 目录之中是否已经存在指定模块
- 若存在,不再重新安装
- 若不存在
- npm 向 registry 查询模块压缩包的网址
- 下载压缩包,存放在根目录下的.npm 目录里
- 解压压缩包到当前项目的 node_modules 目录
第21题:有以下3个判断数组的方法,请分别介绍它们之间的区别和优劣
第22题:介绍下重绘和回流(Repaint & Reflow),以及如何进行优化
第23题:介绍下观察者模式和订阅-发布模式的区别,各自适用于什么场景
第24题:聊聊Redux和Vuex的设计思想
不管是 Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。什么是共享状态?比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态。
父子组件之间,兄弟组件之间共享状态,往往需要写很多没有必要的代码,比如把状态提升到父组件里,或者给兄弟组件写一个父组件,听听就觉得挺啰嗦。如果不对状态进行有效的管理,状态在什么时候,由于什么原因,如何变化就会不受控制,就很难跟踪和测试了。如果没有经历过这方面的困扰,可以简单理解为会搞得很乱就对了。
在软件开发里,有些通用的思想,比如隔离变化,约定优于配置等,隔离变化就是说做好抽象,把一些容易变化的地方找到共性,隔离出来,不要去影响其他的代码。约定优于配置就是很多东西我们不一定要写一大堆的配置,比如我们几个人约定,view 文件夹里只能放视图,不能放过滤器,过滤器必须放到filter 文件夹里,那这就是一种约定,约定好之后,我们就不用写一大堆配置文件了,我们要找所有的视图,直接从 view 文件夹里找就行。
根据这些思想,对于状态管理的解决思路就是:把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。根据这个思路,产生了很多的模式和库,我们来挨个聊聊。
第25题:说说浏览器和Node事件循环的区别
第26题:介绍模块化发展历程
可从 IIFE、AMD、CMD、CommonJS、UMD、webpack(require.ensure)、ES Module、
<script type="module"> 这几个角度考虑。
https://blog.csdn.net/dadadeganhuo/article/details/86777249
第27题:全局作用域中,用const和 let声明的变量不在window 上,那到底在哪
第28题:cookie和token都存放在header 中,为什么不会劫持token?
1、攻击者通过 xss 拿到用户的 cookie 然后就可以伪造 cookie 了。
2、或者通过 csrf 在同个浏览器下面通过浏览器会自动带上 cookie 的特性在通过 用户网站-攻击者网站-攻击者请求用户网站的方式 浏览器会自动带上cookie但是 token
1、不会被浏览器带上 问题 2 解决
2、token 是放在 jwt 里面下发给客户端的 而且不一定存储在哪里 不能通过document.cookie 直接拿到,通过 jwt+ip 的方式 可以防止 被劫持 即使被劫持也是无效的 jwt
第29题:聊聊Vue的双向数据绑定,Model如何改变View, view又是如何改变
1、从 M 到 V 的映射(Data Binding),这样可以大量节省你人肉来 update View的代码
2、从 V 到 M 的事件监听(DOM Listeners),这样你的 Model 会随着 View触发事件而改变
第30题:两个数组合并成一个数组
第31题:改造下面的代码,使之输出0 -9,写出你能想到的所有解法。
第32题: virtual DOM真的比操作原生DOM快吗?谈谈你的想法。
1. 原生 DOM 操作 vs. 通过框架封装操作。
2. 对 React 的 Virtual DOM 的误解。
3. MVVM vs. Virtual DOM
4. 性能比较也要看场合
5. 总结
第33题:下面的代码打印什么内容,为什么?
var b = 10;
(function b(){
b = 20;
console.log(b);
})();
答:
? b(){
b = 20;
console.log(b);
}
首先函数声明比变量要高,其次 b = 20 没有 var 获取其他,说明是 window 最外层定义的变量。
js 作用域中,先找最近的 那就是 b fn ,直接打印了,如果 b = 20 有 var 那就是打印 20
第34题:简单改造下面的代码,使之分别打印10和20.
第35题:浏览器缓存读取规则
可以分成 Service Worker、Memory Cache、Disk Cache 和 Push Cache,那请求的时候 from memory cache 和 from disk cache 的依据是什么,哪些数据什么时候存放在 Memory Cache 和 Disk Cache 中?
https://www.jianshu.com/p/54cc04190252
第36题:使用迭代的方式实现flatten 函数。
var arr=[1,2,3,[4,5],[6,[7,[8]]]]/** * 使用递归的方式处理 * wrap 内保
存结果 ret * 返回一个递归函数 * * @returns */function wrap(){
var ret=[];
return function flat(a){
for(var item of
a){ if(item.constructor===Array){
ret.concat(flat(item))
}else{
ret.push(item)
}
}
return ret
}}console.log(wrap()(arr));
第37题:为什么Vuex的mutation和 Redux的reducer中不能做异
第38题:(京东)下面代码中a在什么情况下会打印1?
第39题:介绍下BFC及其应用。
BFC 就是块级格式上下文,是页面盒模型布局中的一种 CSS 渲染模式,相当于一个独立的容器,里面的元素和外部的元素相互不影响。
创建 BFC 的方式有:
- html 根元素
- float 浮动
- 绝对定位
- overflow 不为 visiable
- display 为表格布局或者弹性布局
BFC 主要的作用是:
- 清除浮动
- 防止同一 BFC 容器中的相邻元素间的外边距重叠问题
第40题:在Vue中,子组件为何不可以修改父组件传递的Prop
第41题:下面代码输出什么
var a = 10;(function () {
console.log(a)
a = 5
console.log(window.a)
var a = 20;
console.log(a)})()
分别为 undefined 10 20,原因是作用域问题,在内部声名 var a = 20;相当于先声明 var a;然后再执行赋值操作,这是在IIFE内形成的独立作用域,如果把 var a=20 注释掉,那么 a 只有在外部有声明,显示的就是外部的A变量的值了。结果A会是 10 5 5
第42题:实现一个 sleep函数
比如 sleep(1000) 意味着等待 1000 毫秒,可从 Promise、Generator、Async/Await等角度实现
const sleep = (time) => {
return new Promise(resolve => setTimeout(resolve,
time))}sleep(1000).then(() => {
// 这里写你的骚操作})
第43题:使用sort()对数组[3,15,8,29,102,22]进行排序,
输出:[102, 15, 22, 29, 3, 8]
解析:根据 MDN 上对 Array.sort()的解释,默认的排序方法会将数组元素转换为字符串,然后比较字符串中字符的 UTF-16 编码顺序来进行排序。所以'102' 会排在 '15' 前面。
第44题:介绍HTTPS握手过程
1、clientHello
2、SeverHello
3、客户端回应
4、服务器的最后回应
第45题:HTTPS 握手过程中,客户端如何验证证书的合法性
1.校验证书的颁发机构是否受客户端信任。
2.通过 CRL 或 OCSP 的方式校验证书是否被吊销。
3.3 对比系统时间,校验证书是否在有效期内。
4.通过校验对方是否存在证书的私钥,判断证书的网站域名是否与证书颁发的域名一致
第46题:输出以下代码执行的结果并解释为什么
var obj = {
'2': 3,
'3': 4,
'length': 2,
'splice': Array.prototype.splice,
'push':
Array.prototype.push}obj.push(1)obj.push(2)console.log(obj)
结果:[,,1,2], length 为 4
伪数组(ArrayLike)
第47题:双向绑定和vuex是否冲突
第48题: call和apply的区别是什么,哪个性能更好一些
1.Function.prototype.apply 和 Function.prototype.call 的作用是一样的,区别在于传入参数的不同;
2.第一个参数都是,指定函数体内 this 的指向;
3.3 第二个参数开始不同,apply 是传入带下标的集合,数组或者类数组,apply 把它传给函数作为参数,call 从第二个开始传入的参数是不固定的,都会传给函数作为参数。
4.call 比 apply 的性能要好,平常可以多用 call, call 传入参数的格式正是内部所需要的格式
第49题:为什么通常在发送数据埋点请求的时候使用的是1x1像素的透明gif图片?
1.没有跨域问题,一般这种上报数据,代码要写通用的;(排除 ajax)
2.不会阻塞页面加载,影响用户的体验,只要 new Image 对象就好了;(排除 JS/CSS 文件资源方式上报)
3.在所有图片中,体积最小;(比较 PNG/JPG)
第50题:(百度)实现(5).add(3).minus(2)功能。
例: 5 + 3 - 2,结果为 6
答:
Number.prototype.add = function(n)
{ return this.valueOf() + n;
};Number.prototype.minus = function(n) {
return this.valueOf() - n;
};
end
转发本文+关注+私信【学习】获取以下完整面试PDF
本文暂时没有评论,来添加一个吧(●'◡'●)