前端面试题(2020-2021)第一组
一,前端解决跨域问题(常用)1,后端设置cors允许跨域,一般指定ip,也可以允许全部2,jsonp,利用浏览器对script加载完自动执行的特新来实现的,需要客户端和服务器端两端的同时配合,因此需要特别处理的接口可以使用,一般传统的后台接口不会采用这种方式进行跨域。3,Nginx反向代理,一般利用vue-cli全家桶,开发时在vue.config.js中设置接口在同一个后端域名和端口,正式服务则
一,前端解决跨域问题(常用)
1,后端设置cors允许跨域,一般指定ip,也可以允许全部
2,jsonp,利用浏览器对script加载完自动执行的特新来实现的,需要客户端和服务器端两端的同时配合,因此需要特别处理的接口可以使用,一般传统的后台接口不会采用这种方式进行跨域。
3,Nginx反向代理,一般利用vue-cli全家桶,开发时在vue.config.js中设置接口在同一个后端域名和端口,正式服务则使用Nginx确保在统一域名下。
二,常见web安全及防护
防护:
1,对用户输入内容进行限制和校验
2,对所有关键数据进行加密
3,接口提交方式严格规范,数据提交不使用get
攻击方式:
sql注入,xss(恶意注入js和网页元素),csrf(代替用户完成交互并访问危险网站)
三,js垃圾回收机制
js的垃圾回收机制是自动执行的,且不可发现的。垃圾回收的对象分为:全局变量和局部变量(函数内变量)。全局变量只有在页面被关闭时才被回收,局部变量在所属函数调用结束后回收。回收方式分为两类一种是标记清除,一种是引用计数。
四,前端性能优化
1,尽量压缩css和js文件大小
2,采用less或者sass
3,图片懒加载
4,精灵图和雪碧图
5,接口尽可能做到重复使用
6,数据请求异步操作,防止页面卡顿
五,闭包理解
闭包就是一个函数,两个函数嵌套在一起,内部函数就是闭包。形成闭包的条件:内部函数需要通过return返回出来。
function f1(){
function f2(){
alert("我是js闭包!");
}
return f2;
}
var f=f1();
f(); //弹出:我是js闭包!
内部函数可以调用外部函数的参数。
用得非常多,包括异步请求,vue的函数嵌套,非常常见,有时候写起来,根本想不起来闭包啥的。
六,cookie和session的区别
1,存储位置有区分,cookie存在浏览器缓存中,session存储在服务器上
2,存储大小和数量 cookie小于session
3,保密性 cookie对游览器和用户可见 session存在服务器上,保密性更佳
4,服务器压力 session对服务器压力更大
重点!!!
在实际开发过程中,cookie和session都很少使用,更为常用的是token,它比上两个优势更大,更好用,安全性高,可扩展性强,多平台跨域,无状态。可实现,单点登录和多点登录等功能。
重点!!!
请不要混淆session和sessionStorage,前者是身份验证(类似于令牌),后者是一种存储方式,数据保存在浏览器缓存中(localStorage存储在本地需要手动删除才会消失),关闭浏览器和标签页就会消失。
重点!!!
有种说法是cookies和localStorage和sessionStorage是同一类,都是储存方式,只是大小,保存位置,保存时间不一样
大小 cookies<sessionStorage=localStorage
保存位置 本地
时间 cookies有指定时间 sessionStorage关闭页面 localStorage不主动清除一直存在
七,那些操作会造成内存泄漏
1,什么是内存泄漏
看上面的垃圾回收机制,当有变量和对象,应该被回收,而没有被回收时,一直占用和停留在堆内存中,这就产生内存泄漏。
2,内存泄漏影响
内存泄漏会导致内存被占用过多无法释放,从而导致系统内存分配不足,造成了内存溢出从而导致应用Crash(浏览器崩溃)。
3,前端造成内存泄漏的操作
1,闭包
由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,过多的使用闭包,不及时释放,会导致内存泄漏。
2,意外全局
函数中定义参数,var 定义,会成为全局变量,建议使用es6的let
3,定时器
4,生命周期中使用全局函数,未释放
5,echarts
6,keep-alive组件
其实在vue中一般这些是不会造成内存泄漏,只有程序猿自己写的bug才会导致,还有对于现在电脑来说,一些内存泄漏并不影响。
八,重构的理解
重构不是重写项目!!!重构不是重写项目!!!重构不是重写项目!!!
重构是一种对软件内部结构的改善,包括去除重复、简化复杂逻辑和澄清模糊的代码,甚至可能是变更一个参数名。
重构和重写之间的差异在于: 重构是在标准化流程和方法的指导下,给正在跑的车换轮子,乘客(业务方)不受影响。重写是推倒重来,比较理想的结果是你把新车造好了让乘客跳过来,但真能做到的团队不多,需要很高的工程管理能力。
九,常见浏览器内核
1,edge的edgeHTML
2,火狐的Gecko
3,Safari的webKit
4,Chrome的Blink
十,DOM创建,添加,移动,复制,查找,删除节点,
1,创建 document.creatElement(‘a’) //创建a元素节点 多用于下载操作 页面显示 <a> </a>
2,添加节点 appendChild()
let ul = document.getElementById('ul');
let li = document.creatElement('li');
ul.appdenChild(li);
一般用于对页面添加文本列表项啥的。
3,删除节点 removeChild()
var ul = document.getElementById("myList"); //获得ul
var lis = ul.getElementsByTagName("li") //获取ul中所有li的集合
ul.removeChild(lis[0]); //移除第一个li,与上面不同,要考虑浏览器之间的差异
4,查找节点(常用)
document.getElementById('box1');//查询Id为box1的元素
document.getElementByClass('box2');//查询class(类名)为box2的元素
5,复制节点 cloneNode()
1 var ul = document.getElementById("myList"); //获得ul
2 var deepList = ul.cloneNode(true); //深复制
3 var shallowList = ul.cloneNode(false); //浅复制
十一,事件模型的三个阶段
一个事件的处理过程主要有三个阶段:捕获,目标,冒泡;
(1)捕获: 当我们在 DOM 树的某个节点发生了一些操作(例如单击、鼠标移动上去),就会有一个事件发射过去。这个事件从 Window 发出,不断经过下级节点直到触发的目标节点。在到达目标节点之前的过程,就是捕获阶段(Capture Phase)。( 所有经过的节点,都会触发这个事件。捕获阶段的任务就是建立这个事件传递路线,以便后面冒泡阶段顺着这条路线返回 Window。 )
(2)目标阶段:当事件不断的传递直到目标节点的时候 ,最终在目标节点上触发这个事件,就是目标阶段。
(3) 冒泡阶段: 事件冒泡即事件开始时,由最具体的元素接收(也就是事件发生所在的节点),然后逐级传播到较为不具体的节点(我们平时用的事件绑定就是利用的事件冒泡的原理)
事件委托就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件
这里得提到阻止事件冒泡
方法一:event.stopPropagation( )
//为所有button元素绑定click事件
$(":button").click( function(event){
alert("button-click");
// 阻止事件冒泡到DOM树上
event.stopPropagation(); // 只执行button的click,如果注释掉该行,将执行button、p和div的click (同类型的事件)
} );
方法二:event.target
$(document).ready(function(){
$('#switcher').click(function(event){
if(event.target==this){//判断是否是当前绑定事件的元素元素触发的该事件
$('#switcher .button').toggleClass('hidden');
}
})
})
还得提到js的事件绑定
这个就是非常基础的东西
1,原生函数
<input onclick="alert('谢谢支持')" type="button" value="点击我,弹出警告框" />
2,自定义函数
<input onclick="myAlert()" type="button" value="点击我,弹出警告框" />
<script type="text/javascript">
function myAlert(){
alert("谢谢支持");
}
</script>
3,js代码绑定
<input id="demo" type="button" value="点击我,显示 type 属性" />
<script type="text/javascript">
document.getElementById("demo").onclick=function(){
alert(this.getAttribute("type")); // this 指当前发生事件的HTML元素,这里是<div>标签
}
</script>
4,事件监听(用的不多)
绑定事件的另一种方法是用 addEventListener() 或 attachEvent() 来绑定事件监听函数。
十二,css盒子模型
标准的盒子模型 元素宽度 = width(内容宽)+ padding(内边距)+border(边框)+margin(外边距)
低版本ie 元素宽度 = width(内容+内边距+边框)+margin(外边距)
十三,js继承
实现继承首先需要一个父类,在js中实际上是没有类的概念,在es6中class虽然很像类,但实际上只是es5上语法糖而已
1,原型链继承(常用,如引用工具类函数)
function Woman(){
}
Woman.prototype= new People();
Woman.prototype.name = 'haixia';
let womanObj = new Woman();
优点:
简单易于实现,父类的新增的实例与属性子类都能访问
缺点:
可以在子类中增加实例属性,如果要新增加原型属性和方法需要在new 父类构造函数的后面
无法实现多继承
创建子类实例时,不能向父类构造函数中传参数
2,借用构造函数继承(伪造对象、经典继承)
复制父类的实例属性给子类
function Woman(name){
//继承了People
People.call(this); //People.call(this,'wangxiaoxia');
this.name = name || 'renbo'
}
let womanObj = new Woman();
优点:
解决了子类构造函数向父类构造函数中传递参数
可以实现多继承(call或者apply多个父类)
缺点:
方法都在构造函数中定义,无法复用
不能继承原型属性/方法,只能继承父类的实例属性和方法
3,实例继承(原型式继承)
4,组合式继承
function People(name,age){
this.name = name || 'wangxiao'
this.age = age || 27
}
People.prototype.eat = function(){
return this.name + this.age + 'eat sleep'
}
function Woman(name,age){
People.call(this,name,age)
}
Woman.prototype = new People();
Woman.prototype.constructor = Woman;
let wonmanObj = new Woman(ren,27);
wonmanObj.eat();
5,寄生组合继承
/父类
function People(name,age){
this.name = name || 'wangxiao'
this.age = age || 27
}
//父类方法
People.prototype.eat = function(){
return this.name + this.age + 'eat sleep'
}
//子类
function Woman(name,age){
//继承父类属性
People.call(this,name,age)
}
//继承父类方法
(function(){
// 创建空类
let Super = function(){};
Super.prototype = People.prototype;
//父类的实例作为子类的原型
Woman.prototype = new Super();
})();
//修复构造函数指向问题
Woman.prototype.constructor = Woman;
let womanObj = new Woman();
十四,浏览器访问页面发生什么
1、DNS查询
2、TCP链接
3、发送HTTP请求
4、Server处理HTTP请求并返回HTTP报文
5、浏览器解析并render页面
6、HTTP连接断开
十五,清除浮动
清除浮动是由于float:left;或float:right;引起的问题。实际运用过程中是不用的或少用,原因是浮动布局造成的影响较多,处理繁琐。更建议使用灵活布局即 display:flex;相关操作可以在网上搜索,不过多赘述。
浮动让元素脱离文档流,按照指定的方向进行运动,遇到相邻的浮动元素或者父元素的边沿时停下,左浮动left;右浮动right
清除浮动的方法
1,父级div定义 height
原理:父级div手动定义height,就解决了父级div无法自动获取到高度的问题。
优点:简单、代码少、容易掌握
缺点:只适合高度固定的布局,要给出精确的高度,如果高度和父级div不一样时,会产生问题
建议:不推荐使用,只建议高度固定的布局时使用
2,结尾处加空div标签 clear:both
原理:添加一个空div,利用css提高的clear:both清除浮动,让父级div能自动获取到高度
优点:简单、代码少、浏览器支持好、不容易出现怪问题
缺点:不少初学者不理解原理;如果页面浮动布局多,就要增加很多空div,让人感觉很不好
建议:不推荐使用,但此方法是以前主要使用的一种清除浮动方法
3,父级div定义 overflow:hidden
原理:必须定义width或zoom:1,同时不能定义height,使用overflow:hidden时,浏览器会自动检查浮动区域的高度
优点:简单、代码少、浏览器支持好
缺点:不能和position配合使用,因为超出的尺寸的会被隐藏。
建议:只推荐没有使用position或对overflow:hidden理解比较深的朋友使用。
知道3个就行,实际运用很少,应付面试即可
十六,数组去重
1,利用Array.from和Set成员的唯一性
ES6 提供了新的数据结构 Set。它类似于数组,但是成员的值都是唯一的,没有重复的值。
let arr = [1, 2, 3, 2, 1];
function unique(arr){
return Array.from(new Set(arr));
}
console.log(unique(arr)) // [1, 2, 3]
2,双重for循环进行去除(es5最多使用)
var arr = [1, 5, 6, 0, 7, 3, 0, 5, 9, 5, 5]
function unique(arr) {
for (var i = 0, iLen = arr.length; i < iLen; i++) {
for (var j = i + 1, jLen = arr.length; j < jLen; j++) {
if (arr[i] === arr[j]) {
arr.splice(j, 1)
j-- // 每删除一个数j的值就减1
jLen-- // j值减小时len也要相应减1(减少循环次数,节省性能)
// console.log(j,jLen)
}
}
}
return arr
}
console.log(unique(arr)) // 1, 5, 6, 0, 7, 3, 9
数组去重是必须掌握的一个方法,实际运用中可能后端都给你处理好了,但是也有可能需要你处理的,第一种方法多用,在笔试题部分可以节省大量时间
十七,promise是什么,可以解决什么问题
ES6中新技术,解决异步回调地域问题。
(1)promise对象表示一个异步操作的最终完成或失败,及其结果值,是一个代理值。
(2)语法上:Promise是一个构造函数,用来生成Promise的实例对象。
(3)功能上:Promise对象用来包裹一个异步操作,并获取成功、失败结果值。
实例
function f1() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(111), 1000);
}).then(data => console.log(data));
}
function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(222), 2000);
}).then(data => console.log(data));;
}
function f3() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(333), 3000);
}).then(data => console.log(data));;
}
f1().then(f2).then(f3)
实际情况下,我可能不会使用promise,更加倾向使用.then(),在then里调用函数
例如
a().then(()=>{
b().then(()=>{
c();
})
})
十八,如何查找当前页面中所有z-index,并找到其中最大值
1,找到所有元素转化维数组
Array.from(document.querySelectorAll('body *'))
2,找到所有元素中的z-index
如果zindex为null则覆盖为0
// 正常写法
Number(window.getComputedStyle(__DOM__).zIndex) || 0
// 简写写法
+window.getComputedStyle(__DOM__).zIndex || 0
3,数组中寻找最大值
Math.max(...arr)
综合起来
var arr = Array.from(document.querySelectorAll('body *')).map(e => Number(window.getComputedStyle(__DOM__).zIndex) || 0)
return arr.length ? Math.max(...arr) : 0
十九,接收服务端消息推送
可以用websocket,主要的就是这样
socket=newWebSocket(url);
//打开websokcet
socket.open=()=>{console.log(“已打开”)};
//接收信息
socket.onmessage=(data)=>{console.log(收到的信息:${data}
)};
//关闭
socket.onclose=()=>{console.log(“关闭”)};
//出错
socket.οnerrοr=()=>{console.log(“出错”)};
二十,数组与类数组区别
类数组
- 拥有length属性
- length属性的类型是Number
二十一,前端浏览器多个标签页通讯?
1,通过localstorage进行存储与读取
2,通过cookie+setInterval。隔段时间读取
二十二,js去除所有空格?
去除字符串内所有的空格:str = str.replace(/\s*/g,"");
二十二,vue单页项目优缺点
优点
良好的交互体验
单页面应用的内容改变不需要重新加载整个页面,数据获取也是通过ajax异步获取的,没有页面之间的切换,就不会出现白屏现象,也不会出现假死并有闪烁现象,页面显示流畅,web更具有响应性
良好的前后端分离工作模式,后端不再负责渲染模板,后端API通用化,即同一套后端程序代码,不用修改就可以用于web界面,手机,平板等多种客户端
减轻服务器压力,服务器只用出数据就可以,不用展示逻辑和页面合成,吞吐能力会提高几倍
缺点
首屏加载慢
如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,拖慢加载速度
通过查看Network,发现整个网站记载时间长达十几秒,加载最长的就是js,css,和媒体文件
解决方案
Vue-router懒加载
vue-router懒加载就是按需加载组件,只有当路由被访问的时候才会加载对应的组件,而不是在加载首页的时候就加载,而不是在加载时候也的时候就加载,项目越大,对首屏加载的速度就提升的越明显
使用CDN加速
项目中用到的库,采用CDN加载可以加快速度
异步加载组件
服务端渲染
服务端渲染还能对SEO优化起到作用,有利于搜索引擎抓取更多的有用信息(如果页面纯前端渲染,搜索引擎抓取到的就只有空白页面
不利于SEO
SEO的本质就是一个服务器向另一个服务器发起请求,解析请求内容,但一般来说搜索引擎不会去执行请求到的js。也就是说,搜索引擎的基础爬虫原理就是抓取url,然后获取html源代码并解析,如果是一个单页面应用,html在服务端还没有渲染部分数据,在浏览器才渲染出部分数据,搜索引擎请求到的html是模型页面并不是最终数据的渲染页面
解决方案
服务端渲染, 服务端合成完整的html文件再输出浏览器
页面预渲染
路由采用h5 history模式
不适合开发大型项目
大型项目可能会涉及到大量的DOM操作,复杂的动画效果,也就不适合用vue,react框架进行开发
更多推荐
所有评论(0)