前端面试题
JavaScript问题:
判断一个值的类型都有哪些方法?区别是什么?
typeof:用于判断基本数据类型,判断null、{}、[]时返回的都是"object",判断函数时返回"function";
instanceof:instanceof
只能用来判断两个对象是否属于实例关系,而不能判断一个对象实例具体属于哪种类型;
constructor:指向构造函数,除了undefined与null,可判断9种类型,null与undefined没有构造函数,所以判断不了;
Object.prototype.toString.call:可判断所有的数据类型。
==、===、object.is的区别?
==:等同,只判断值不判断类型;
===:恒等,判断值也判断类型,有一个不相同则是false;
object.is:ES6新增方法,与===作用相同。
闭包、闭包的使用场景与优缺点?
闭包:通过调用一个函数返回另一个函数,用以读取函数内局部变量的方法叫做闭包。
使用场景:用于保护变量不被污染,访问局部变量并持续保存在内存中。
优点:可读取函数内的局部变量,保护其不被外部变量污染。
缺点:闭包由于执行函数返回的函数被外部全局变量引用,所以不会被垃圾回收机制回收,有可能会造成内存泄漏,如在低版本ie中垃圾回收机制使用的是引用计数进行垃圾回收的会出现内存泄漏问题。
事件循环机制(event loop)
js是单线程的,这是为了避免多个线程同时操作dom,出现混乱。由于是单线程的,在处理IO等耗时较多的操作时就会阻塞,js通过异步处理与事件循环机制来实现非阻塞。
当脚本第一次执行同步代码时将同步代码按照顺序加入到执行栈中,然后从头开始执行,如果遇到的是方法,又在执行栈中添加这个方法的执行环境,而后进入其中继续执行其中的代码,执行完成后返回并销毁执行环境;如果遇到异步代码,则将其挂起,然后继续执行栈里面的其他任务,当异步事件返回结果后加其加入到与执行栈不同的事件队列中,等待执行栈中所有任务都执行完毕而后再去查找事件队列中是否有任务,有的话从其中取出第一位事件加入到执行栈中然后执行其中的同步代码,如此反复,这个过程就叫事件循环。
垃圾回收机制
js在控制内存分配时会定期查找一些已经分配的内存且该不再使用时回收该内存称之为垃圾回收机制。
垃圾回收机制最初使用的是引用计数法,通过判断对象有没有其他对象引用到它来判断是否需要回收该对象内存;但是有一个循环引用问题,如在一个函数中定义了两个变量,两个变量互相引用,但是在函数执行完成后这两个变量就不再需要,应该回收该内存时,由于它们互相引用了一次而导致内存无法被回收:
function f(){
var o = {};
var o2 = {};
o.a = o2; // o 引用 o2
o2.a = o; // o2 引用 o
return "azerty";
}
f();
标记-清除法:从根(root)全局对象查找开始引用的对象,找到所有可获得的对象与不可获得的对象,以对象是否可以获得来确定哪些对象需要被回收内存。如:
function aTestFC() {
var abTest = "abTest";
return function ab() {
console.log(abTest);
}
}
aTestFC();
const aRes = aTestFC();
我们定义aTestFC函数,在浏览器中aTestFC会被挂载到window对象中,这时window对象就是我们的根(root)全局对象,所以aTestFC会一直保留在我们的内存当中,而当aTestFC执行完成后abTest变量与ab函数内存都将被回收,因为它们在执行完成后并没有被全局对象或其引用对象引用,所以从全局对象开始查找时并不能获取到这两个对象。
我们在上面再加多一条赋值引用 const aRes = aTestFC(); 后由于函数ab被外部全局变量引用,也就挂载到了根(root)全局对象中了,所以ab函数内存便不会被回收,这是也闭包内存不会被回收的原因。
proto与prototype的区别与关系?
每个对象中都有一个私有属性隐性原型__proto__,而显性原型prototype属性只有函数拥有;对象的__proto__属性指向的是其构造函数的prototype属性。
原型、原型链、及其作用场景
每个对象中都有一个私有属性__proto__称之为隐性原型__proto__指向它的构造函数的原型对象prototype,该构造函数的原型又有一个自己的__proto__指向其构造函数原型对象,以此类推,就形成了一个原型链。直到Object构造函数中,Object中__proto__指向null,便不再查找下去。
如在普通对象中其构造该对象的的构造函数是Object,则普通对象的__proto__指向Object.prototype。
var obj = {};
obj.__proto__ = Object.prototype; //true
new一个对象发生了什么?
- 创建一个空对象, 将它的引用赋给 this, 继承函数的原型
- 通过 this 将属性和方法添加至这个对象
- 最后返回 this 指向的新对象, 也就是实例 (如果没有手动返回其他的对象)
es6都有哪些新增语法?
模块化:import、export;
定义变量:let、const;
多行字符串,模版变量:${}
;
解构赋值:let {a,b} = {a:123,b:321}
;
块级作用域;
箭头函数:()=>{}
;
函数默认参数:(a=0)=>{}
;
可选链:a.?b
;
深拷贝与浅拷贝
浅拷贝:增加一个指针指向已有的内存地址;
深拷贝:增加一个指针并申请了一个新的内存地址,并将该指针执行新的内存地址。
浅拷贝做法有:Object.assign、赋值
深拷贝做法有:JSON.stringify、递归算法
框架问题:
什么是渐进式框架?
一种一开始不用掌握完全的功能特性,后续逐步增加功能,没有多做职责之外的事情的框架。
什么是单向数据流?
数据流向是单一的,只能从一个方向修改数据;对应到vue或react中就是,父组件传递给子组件的信息不允许在子组件中修改,只能子组件向父组件通知修改该信息。
首屏优化问题
- router路由懒加载,减少首屏需要加载的页面代码
- ssr服务端渲染,提前在服务端渲染好页面
- webpack压缩文件大小、开启gzip压缩、按需加载ui库等以达到缩小请求文件大小
- 骨架屏在渲染完成前展示给用户看
- 图片懒加载
- cdn加速
- js脚本异步或延迟加载
vue是mvvm架构嘛?react是什么架构?
vue是mvvm架构,mvvm的特点是数据驱动视图,视图响应数据,中间通过vm来观察数据变化通知视图更改与监听视图变化通知数据变化,vue符合该特别,所以vue是mvvm架构。
react只是view层,react是单向数据流,状态驱动视图。
从props传值与在router中传值有什么区别?
props可通过状态提升,把公共状态提升到父组件中实现状态共享;而且props不单单只传字符串,也可以传方法。
vue的diff算法是如何实现的?
先根据真实dom树生成一个虚拟dom树,在修改了某个数据后,虚拟dom树发生变化,而后通过对比新旧两颗虚拟dom树查找修改节点,并将修改后的虚拟dom更新到真实dom树上。
vue的diff算法通过同层级对比新旧两个虚拟dom树来检查是否发生改变。
vue的diff算法与react的diff算法有什么区别?
vue与react都是同层级对比新旧两个虚拟dom树来检查是否发生改变,但是vue对比节点,在节点元素类型相同,但是classname不同时,会认为是不同的元素,会删除重新创建,而react会认为是同一个元素,只是属性发生改变,进行修改操作。在列表对比上,vue采取从两端到中间两个指针对比,两两比较;而react则是从左到右进行对比。
react hook解决了什么问题?
解决了之前的生命周期限制问题。
react的setState是异步的还是同步的?
在react的合成事件中的异步的,在原生js事件或异步事件中的同步的,但是代码执行始终是同步的。
vue的computed与watch的区别与运用场景?
computed支持缓存,只有在依赖发生改变时才会重新进行计算,watch则是在值发生改变后都会执行;
computed不支持异步,watch支持异步;
vue3与vue2的区别?
新的响应方案:使用Proxy替换了Object.defineProperty;
新增的compostion Api;
全局Api改变,vue2全局Api挂载在vue上,vue3全局Api挂载在vue的实例app上。
谈谈对vue生命周期的理解
vue2生命周期包括创建前后:beforeCreate、Create;挂载前后:beforeMount、Mount;更新前后:beforeUpdate、Update;销毁前后:beforeDestroy、Destroy。缓存组件keep-live的激活与失活:activated、deactivated。捕获子孙组件错误的生命周期:errorCaptured。
父子组件生命周期的执行顺序是:
父组件 beforeCreate、父组件 created、父组件 beforeMount、子组件
beforeCreate、子组件 created、子组件 beforeMount、子组件 mounted、父组件
mounted
父组件 beforeUpdate、子组件 beforeUpdate、子组件 updated、父组件 updated
父组件 beforeDestroy、子组件 beforeDestroy、子组件 destroyed、父组件
destroyed
vue3的cpmpostion
Api中生命周期没有了创建前后:beforeCreate、Create两个生命周期。
vue-router中常用的hash与history路由模式的实现原理
hash路由模式用的是通过监听location.hash发生改变而进行路由改变;
history路由模式用的是h5的history
Api,通过popstate事件监听路由的前进后退。
http问题
TCP传输的三次握手,四次挥手
第一次握手,客户端发送包到服务端,请求建立连接;
第二次握手,服务端验证客户端发送的包后确定客户端发送没有问题,发送包到客户端,并请求建立连接;
第三次握手,客户端接收到服务端的包后确认发送与接收都没有问题,对服务端确认应答。
第一次挥手,客户端发送包,请求断开连接;
第二次挥手,服务端针对客户端的请求确认应答;
第三次挥手,服务端向客户端请求断开连接;
第四次挥手,客户端针对服务端的请求确认应答;
常用的http code状态码
200成功状态嘛,201已创建,301重定向,304缓存,400错误请求,401权限错误,404未找到,500服务端错误。
强缓存与协商缓存
强缓存:服务端通过在请求的响应头中添加cache-control并设置最大缓存时间,而后在这个时间范围内的这个接口请求都将通过磁盘获取资源,不再发送请求到服务端,响应码是200;
协商缓存:服务端通过在请求的响应头中添加etag与last-modified来与接下来的请求中对比资源是否发生变化,如无变化则返回304,客户端到磁盘中获取资源,如变化则返回200,并返回最新的资源。
网页从输入网址到完成页面渲染都经历了哪些过程?
- DNS解析url
- 通过解析到的ip地址与端口号发起请求
- 向服务器发起三次握手建立连接
- 请求响应成功获取资源后,通过解析html生成DOM树,解析css和js,渲染页面,直至显示完成
cookie 如何跨域使用?
1.Path,一个路径,在这个路径下面的页面才可以访问该
Cookie,一般设为"/",以表示同一个站点的所有页面都可以访问这个 Cookie。
2.Domain,子域,指定在该子域下才可以访问 Cookie,例如要让 Cookie 在
a.test.com 下可以访问,但在 b.test.com 下不能访问,则可将 domain 设置成
a.test.com。