八股文
1.说一说HTML语义化?
对于开发者而言,语义化标签有着更好的页面结构,利于个人的代码编写。 对于用户而言,当网络卡顿时有良好的页面结构,有利于增加用户的体验。 对于爬虫来说,有利于搜索引擎的SEO优化,利于网站有更靠前的排名。 对于团队来讲,有利于代码的开发和后期的维护。
2.说一说盒模型
CSS盒模型本质是一个盒子,包含 padding、border、margin、content。有两种盒模型:W3C标准盒模型和IE怪异盒模型。标准盒模型的width/height不包含padding和border,因此总宽高需要额外加上padding、border和margin的值。怪异盒模型width/height中包含padding和border,因此总宽高只用额外加上margin值即可。默认使用标准盒模型,也可以通过控制box-sizing属性决定盒模型:border-box代表怪异盒模型、content-box代表标准盒模型。
3.说一下浮动
脱离文档流,盒子塌陷,影响其他元素排版
4.说一说样式优先的规则是什么?
!important > 内联样式(style) > ID选择器(id) > 类选择器(class) > 标签选择器
5.说一说CSS尺寸设置的单位
- px:绝对像素 2. rem:相对于根元素像素 3. em:相对于当前元素的字体大小的倍数 4. vw:视口宽度 5. vh:视口高度
6.说一说BFC
1.定义:块级格式化上下文,独立的渲染区域,不会影响边界外的元素 2.形成条件: a.flaot b.position 3. overflow 4.display 3.布局规则: a.区域内box从上到下排列 b.box垂直方向的距离由margin决定 c.同一个bfc内box margin会重叠 d.bfc不会与flaot重叠 e.bfc计算高度 也会计算float元素
7.说几个未知宽高元素水平垂直居中方法
1、使用相对定位,绝对定位来做,元素:position:sbsolute,left:50%,top:50%,tranform:translate(-50%,-50%) 2、 设置元素的父级为弹性盒子`display:flex`,设置父级和盒子内部子元素水平垂直都居中`justify-content:center; align-items:center` , 3.设置元素的父级为网格元素`display: grid`,设置父级和盒子内部子元素水平垂直都居中`justify-content:center; align-items:center` ,这种方式代码简介,但是兼容性ie 10以上支持 4. 设置元素的父级为表格元素`display: table-cell`,其内部元素水平垂直都居中`text-align: center;vertical-align: middle;` ,设置子元素为行内块`display: inline-block
8.说一说三栏布局的实现方案
1.左右浮动 设置宽高 中间设置高 宽度自适应 2.bfc:左右浮动 中间overflow:hidden 3.flex布局 父元素设置flex 中间元素 设置flex-grow:1
9.说一说js数据类型有哪些,区别是什么?
原始数据类型:number、string、boolean、undefined、null、symbol、bigint 引用数据类型:object(包括Array,Function、Object类等) 区别:前七中原生数据类型的存放在栈中,引用数据类型存放在堆中,它的地址在栈中,一般我们访问就是它的地址
10.说一说null和undefined的区别,如何让一个属性变为null
null 是定义 并赋值null undefined是定义未赋值
11.说一说JavaScript有几种方法判断变量的类型?
- typeof(根据二进制判断),不能判断数据类型:null和object 2. intanceof(根据原型链判断),原生数据类型不能判断 3. constructor.name(根据构造器判断),不能判断null数据类型 4. Object.prototype.toString.call()(用Object的toString方法判断)所有类型数据都能判断,记住判断结果打印为:'[object Xxx]'
12.说一说数组去重都有哪些方法?
1.new Set(): let array = [...new Set(arr)] 2.indexOf: if(array.indexOf(arr[i]) == -1){ array.push(arr[i])} 或者 arr5.filter((item, index, self) => {return self.indexOf(item) === index}) 3.使用reduce+includes:return arr.reduce((prev,curr) => prev.includes(curr) ? prev : [...prev,curr],[])
13.说一说伪数组和数组的区别?
- 伪数组的特点:类型是object、不能使用数组方法、可以获取长度、可以使用for in遍历 2. 伪数组可以装换为数组的方法:a. Array.prototype.slice.call() b.Array.from() c. [...伪数组] 3. 有哪些是伪数组:函数的参数arguments,Map和Set的keys()、values()和entires()
14.说一说map和forEach的区别?
直白点说,forEach是针对数组中每一个元素,提供一个可执行的函数操作,因此它(可能)会改变原数组中的值。不会返回有意义的值,或者说会返回undefined;而map是会分配内存空间创建并存储一个新的数组,新数组中的每一个元素由调用的原数组中的每一个元素执行所写的函数得来,返回的就是新数组,因此不会改变原数组的值;
个人感觉map更加贴近于函数式编程的特点,而且执行起来也会比forEach快很多,所以我在二者都可的情况下会更推荐map
15.说一说es6中箭头函数?
1.写法简洁 2.无自己的this,继承上一个作用域的this(全局或上一个函数) 3.内部this无法被改变 4.arguments的特殊性(window下保存,this指向上一个函数则arguments表示上一个函数的参数) 5.不能作为构造函数(无自己的this、constructor) 6.无自己的prototype
16.事件扩展符用过吗(...),什么场景下?
1.数组克隆 let a = [1,2,3];let b = [...a] 2.数组合并 let a = [1,2,3];let b = [4,5,6];let c = [...a,...b] 3.类数组转成真正的数组 let a = new Set([1,2,3]); let b = [...a]
17.说一说你对闭包的理解?
闭包形成的原理:作用域链,当前作用域可以访问上级作用域中的变量 闭包解决的问题:能够让函数作用域中的变量在函数执行结束之后不被销毁,同时也能在函数外部可以访问函数内部的局部变量。 闭包带来的问题:由于垃圾回收器不会将闭包中变量销毁,于是就造成了内存泄露,内存泄露积累多了就容易导致内存溢出。 加分回答 闭包的应用,能够模仿块级作用域,能够实现柯里化,在构造函数中定义特权方法、Vue中数据响应式Observer中使用闭包等。
18.说一说JS变量提升?
函数和var声明的变量会有变量提升,js在预编译阶段会将函数和var声明的变量提升至最前面先执行,之后再按顺序执行代码块进行赋值,而let和const声明的变量只是创建提升,在预编译中将其创建,形成暂时性死区,不能提前访问和调用变量,只能在赋值之后进行调用和访问
19.说一说this指向(普通函数、箭头函数)?
普通函数执行指向window,箭头函数中的this指向上一级作用域中的this,构造函数this指向实例对象
20.说一说call apply bind的作用和区别?
首先,call apply bind三个方法都可以用来改变函数的this指向,具体区别如下:
1、fn.call (newThis,params) call函数的第一个参数是this的新指向,后面依次传入函数fn要用到的参数。会立即执行fn函数。
2、fn.apply (newThis,paramsArr) apply函数的第一个参数是this的新指向,第二个参数是fn要用到的参数数组,会立即执行fn函数。
3、fn.bind (newThis,params) bind函数的第一个参数是this的新指向,后面的参数可以直接传递,也可以按数组的形式传入。 不会立即执行fn函数,且只能改变一次fn函数的指向,后续再用bind更改无效。返回的是已经更改this指向的新fn
21.说一说js继承的方法和优缺点?
1.原型链继承
缺点:Parent 中的引用属性会被每个子类示例共享
2.借用构造函数继承
优点:
1避免了子类示例共享引用属性的情况
2可以在实例化时给Parent构造函数传递参数
缺点:
1如果Parent中存在一个函数,那么每次实例化Children的时候,都会创建一个同样函数,函数的复用性就难以体现
3组合继承:原型链 + 构造函数 (这是js中最常见的继承方式)
优点:
1 避免了子类共享引用属性同时避免了父类构造函数重复对function属性的创建
4.原型式继承(注意:是原型式而非原型链,这种方法使用较少)
缺点:
1和原型链继承一样,后代实例会共享父类引用属性
5寄生式继承(个人感觉就是定义了一个方法,复制了一个对象,让后在复制的对象上添加属性和方法,然后return)
缺点:
1和原型链继承一样,parent中的引用属性,会被所有示例共享
6寄生组合式继承 (寄生+组合(原型链+借用构造函数))
优点:
1和组合继承一样,只不过没有组合继承的调用两次父类构造函数的缺点
22.说一说new会发生什么?
看下new的实现代码就知道有以下四步:1. 创建一个新对象 2. 将新对象的__proto__(原型)指向构造函数的prototype(原型对象)3. 构造函数绑定新对象的this并执行返回结果 4. 判断返回结果是否为null,如果为null,返回新对象,否则直接返回执行结果。
23.说一说defer和async区别?
html文件都是按顺序执行的,script标签中没有加defer和async时,浏览器在解析文档时遇到script标签就会停止解析阻塞文档解析,先加载JS文件,加载完之后立即执行,执行完毕后才能继续解析文档。 而在script标签中写入defer或者async时,就会使JS文件异步加载,即html执行到script标签时,JS加载和文档解析同时进行,而async是在JS加载完成后立即执行JS脚本,阻塞文档解析,而defer则是JS加载完成后,在文档解析完成后执行JS脚本
24.说一说promise是什么与使用方法?
- 概念:异步编程的一种解决方案,解决了地狱回调的问题 2. 使用方法:new Promise((resolve,reject) => { resolve(); reject(); })里面有多个resovle或者reject只执行第一个。如果第一个是resolve的话后面可以接.then查看成功消息。如果第一个是reject的话,.catch查看错误消息。
25.说一说JS实现异步的方法
1.promise 2 定时器 3 回调函数
26.说一说cookie sessionStorage LocalStorage区别?
1.都是浏览器存储 2.都存储在浏览器本地 区别: 1.cookie由服务器写入, sessionStorage以及localStorage都是由前端写入 2.cookie的生命周期由服务器端写入时就设置好的,localStorage是写入就一直存在,除非手动清除,sessionStorage是由页面关闭时自动清除 3.cookie存储空间大小约4kb, sessionStorage及localStorage空间比较大,大约5M 4.3者的数据共享都遵循同源原则,sessionStorage还限制必须是同一个页面 5.前端给后端发送请求时,自动携带cookie, session 及 local都不携带 6.cookie一般存储登录验证信息或者token,localStorage常用于存储不易变动的数据,减轻服务器压力,sessionStorage可以用来监测用户是否是刷新进入页面,如音乐播放器恢复进度条功能
27.说一说如何实现可过期的localstorage数据?
一种是惰性删除:惰性删除是指获取数据的时候,拿到存储的时间和当前时间做对比,如果超过过期时间就清除Cookie。 另一种是定时删除:每隔一段时间执行一次删除操作,并通过限制删除操作执行的次数和频率,来减少删除操作对CPU的长期占用。 LocalStorage清空应用场景:token存储在LocalStorage中,要清空
28.说一下token能放在cookie中吗?
不能。token本身就是用来鉴权的,防止CSRF攻击。如果将token放在cookie中,则token还是会随cookie自动携带至请求中,防止不了CSRF攻击。token一般存储在sessionStorage/localStorage里面。token的出现就是为了解决用户登录后的鉴权问题,如果采用cookie+session的鉴权方式,则无法有效地防止CSRF攻击,同时,如果服务端采用负载均衡策略进行分布式架构,session也会存在一致性问题,需要额外的开销维护session一致性。所以token是专门为了鉴权而生,常见的token为JWT(JSON Web Token),用户通过账户密码登入系统后,服务端会生成一个jwt。jwt一般包含三个部分header、payload和signature,header包括两个字段说明token的类型和采用的签名算法,payload包含用户的一些身份权限信息但不包含敏感信息,signature是服务端的签名由前两个部分采用base64编码后再经过签名算法加密生成,签名算法的私钥由服务器保管。服务端生成jwt后返回给客户端。客户端下次调用api的请求头中放入token用于鉴权,服务端会通过jwt的前两个部分和私钥经过签名算法生成一个签名,判断与jwt第三部分的签名是否一致,如果一致就认证通过。
29.说一说axios的拦截器原理及应用?
axios拦截器分为响应和请求拦截器,请求拦截器 在请求发送前进行必要操作处理,例如添加统一cookie、请求体加验证、设置请求头等,相当于是对每个接口里相同操作的一个封装; 响应拦截器 同理,响应拦截器也是如此功能,只是在请求得到响应之后,对响应体的一些处理,通常是数据统一处理等,也常来判断登录失效等。
30.说一说创建ajax过程?
(1)创建异步对象,即 XMLHttpRequest 对象。 (2)使用 open 方法设置请求参数。open(method, url, async)。参数解释:请求的方法、请求的 url、是否异步。第三个参数如果不写,则默认为 true。 (3)发送请求:send()。 (4)注册事件:注册 onreadystatechange 事件,状态改变时就会调用。如果要在数据完整请求回来的时候才调用,我们需要手动写一些判断的逻辑。 (5)服务端响应,获取返回的数据。
31.说一下fetch请求方式?
1.加载速度,第一个请求响应时间,页面加载时间,交互动作的反馈时间,帧率fps,异步请求完成时间 2.性能指标:速度指数,speed index 第一个请求响应时间 TTFB 页面加载时间
32.说一下有什么方法可以保持前后端实习通信?
sorket
33.说一下浏览器输入URL发生了什么?
1、URL解析:判断浏览器输入的是搜索内容还是URL;2、查找缓存:如果能找到缓存则直接返回页面,如果没有缓存则需要发送网络请求页面;3、DNS域名解析;4、四次握手建立TCP连接;5、发起HTTP请求;6、服务器响应并返回结果;7、通过四次握手释放TCP连接;8、浏览器渲染;9、js引擎解析
34.说一下浏览器如何渲染页面的?
1.解析HTML,生成DOM树
2.解析CSS,生成CSSOM树
3.两棵树结合,生成Render树
4.计算布局,绘制页面的所有节点
5.绘制布局
35.说一下重绘、重排区别如何避免?
重绘不一定引起重排、但重排一定会引起重绘。避免:使用translate替代top、使用visibility代替display:hidden、none、DOM离线后修改、使用class去修改样式、使用 absolute 或 fixed 脱离文档流 、使用GPU加速提升网站的动画渲染性能:transform、transform: translateZ(0); 或 transform: translate3d(0,0,0);不要选择table布局
36.说一下浏览器垃圾回收机制?
有两种机制:1、标记清除:对所有活动对象进行标记,清除阶段会将没有标记的对象清除;标记整理算法:标记结束后,算法将活动对象压入内存一端,则需要清理的对象在边界,直接被清理掉就行。(效率低) 2、引用计数:将对象是否不再需要简化定义为有没有其他对象引用它,如果没有引用指向这个对象,则会被垃圾回收机制回收。(内存空间不连续)
37.说一说事件循环Event loop,宏任务与微任务?
js是单线程的,主线程在执行时会不断循环往复的从同步队列中读取任务,执行任务,当同步队列执行完毕后再从异步队列中依次执行。宏任务与微任务都属于异步任务,再执行上微任务的优先级高于宏任务,因此每一次都会先执行完微任务在执行宏任务。宏任务有定时器,Dom事件,ajax事件,微任务有:promise的回调、MutationObserver 的回调 ,process.nextTick
38.说一说跨域是什么?如何解决跨域问题?
- 跨域的概念:浏览器不能执行其它的网站的脚本,这由浏览器的同源策略造成的,也是浏览器施加安全的限制。 2. 跨域解决方案: - jsonp - 前端proxy后端cors - 线上Nginx - websockt
39.说一说钩子函数?
1、概念:Vue实例创建和销毁过程中自动执行的函数;2、常见的生命周期中的钩子函数:创建阶段:beforeCreate,create,beforeMount,mount;更新阶段:beforeUpdate,update,activeted;销毁阶段:beforeDestroy,destroy。3、完整的父子组件生命周期执行顺序:- 加载渲染过程:父beforeCreate ---> 父created ---> 父beforeMount ---> 子beforeCreate ---> 子created ---> 子beforeMount ---> 子mounted ---> 父mounted - 子组件更新过程:父beforeUpdate ---> 子beforeUpdate ---> 子updated ---> 父updated - 父组件更新过程:父beforeUpdate ---> 父updated - 销毁过程:父beforeDestroy ---> 子beforeDestroy ---> 子destroyed ---> 父destroyed
40.说一说组件通信的方式?
1、父子组件通信常用props和emit还有refs;2、兄弟组件通信常用定义的公共事件bus的on、emit;3、祖先和子孙组件通信常用attrs和listener、provide和inject;4、复杂通信常用vuex
41.说一说computed和watch的区别?
computed是计算属性,依赖其他属性值,并且有缓存,只有当依赖的值发生了变化之后才会重新计算,不支持异步操作,而watch更多的是监听,当监听的值发生了变化,会立即执行回调进行操作,支持异步
42.说一说v-if和v-show区别?
v-show是操作display:none,而v-if是操作Dom直接进行删除和添加,在性能上不及v-show
43.说一说vue的keep-alive?
1、keep-alive是vue的内置组件,能在组件切换过程中将状态保留在内存中,相当于缓存,防止DOM的重复渲染;2、keep-alive有三个属性:include(只有名字匹配的才会被缓存)、exclude(任何名字匹配的都不会被缓存)、max(最多可以缓存多少个组件)。3、在路由router的中:相应组件下规定meta属性,定义keep-alive:true;4、可以结合Vue组件实例加载顺序讲解,VNode->实例化->_updata->真实Node,在实例化的时候会判断该组件是否被keep-alive保存过,是的话则直接拿其中的DOM进行渲染。
44.说一说Vue中$nextTick作用与原理?
Vue更新dom节点是异步操作,即数据更新之后,视图不会马上更新,所以修改数据后,在方法中获取到的dom节点不是更新后的dom节点,只有在nextTick里面才能获取到更新后的dom节点
45.说一说vue列表为什么加Key?
Vue列表加key的目的是为diff算法添加标识,因为diff算法判断新旧VDOM是否相同的依据是节点的tag和key。如果tag和key相同则会进一步进行比较,使得尽可能多的节点进行复用。此外,key绑定的值一般是一个唯一的值,比如id。如果绑定数组的索引index,则起不到优化diff算法的作用,因为一旦数组内元素进行增删,后续节点的绑定的key也会发生变化,导致diff进行多余的更新操作。
46.说一说vue-router实现懒加载的方法?
懒加载:不用到不加载
实现懒加载:import(ES6)require+resolve(Vue)
47.说一说HashRouter和HistoryRouter的区别和原理?
1.history和hash都是利用浏览器的2种特性实现前端路由,history是利用浏览历史记录栈的API实现,hash是监听location hash值变化事件来实现 2.history的url没有#号,hash有#号 3.相同的url,history会触发添加到浏览器历史记录栈中,hash不会触发,history需要后端配合,如果后端不配合刷新页面会出现404,hash不需要 hashRouter原理:通过window.onhashchange获取url中hash值 historyRouter原理:通过history.pushState,使用它做页面跳转不会触发页面刷新,使用window.onpopstate监听浏览器的前进和后退
48.说一说Vuex是什么,每个属性是干嘛的,如何使用?
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
算法题
1.无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
第1行:函数定义
var lengthOfLongestSubstring = function(s) {
第2行:创建魔法笔记本
const charLastSeen = new Map();
第3行:初始化窗户左边
let left = 0;
第4行:初始化最大长度
let maxLen = 0;
第5行:开始移动右边框
for (let right = 0; right < s.length; right++) {第6行:获取当前字母
const char = s[right];
第7-9行:检查重复并移动左边框
if (charLastSeen.has(char) && charLastSeen.get(char) >= left) {
left = charLastSeen.get(char) + 1;
}
第10行:更新笔记本
charLastSeen.set(char, right);
第11-14行:计算并更新最大长度
const currentLen = right - left + 1;
if (currentLen > maxLen) {
maxLen = currentLen;
}
第15行:结束扫描
}
第16行:返回结果
return maxLen;
javascript
/**
* @param {string} s
* @return {number}
*/
var lengthOfLongestSubstring = function(s) {
const charLastSeen = new Map();
let left = 0;
let maxLen = 0;
for (let right = 0;right < s.length; right++ ) {
const char = s[right];
if(charLastSeen.has(char) && charLastSeen.get(char) >= left){
left = charLastSeen.get(char) + 1;
}
charLastSeen.set(char, right);
const currentLen = right - left + 1;
if (currentLen > maxLen ) {
maxLen = currentLen;
}
}
return maxLen;
};
2.LRU缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity)以 正整数 作为容量capacity初始化 LRU 缓存int get(int key)如果关键字key存在于缓存中,则返回关键字的值,否则返回-1。void put(int key, int value)如果关键字key已经存在,则变更其数据值value;如果不存在,则向缓存中插入该组key-value。如果插入操作导致关键字数量超过capacity,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。