前端面经,持续更新中。。。

295 阅读24分钟

前端面经

一.html

1.回流与重绘

​ 回流:当渲染的一部分元素宽高发生变化等,导致重新构建布局,就产生了回流。

​ 重绘 :当一个元素自身宽高没有发生变化,只改变了背景颜色,发生重绘。

解决办法:不要一条一条地修改 DOM 的样式。与其这样,还不如预先定义好 css 的 class,然后修改 DOM 的 className。

​ 建立图层,为动画的 HTML 元件使用 fixed 或 absoult 的 position,那么修改他们的 CSS 是不会 reflow 的。

​ 千万不要使用 table 布局。因为可能很小的一个小改动会造成整个 table 的重新布局

2.几种常见的状态码

1xx:指示信息–表示请求已接收

2xx:成功-请求已接受并返回响应头 200:请求已成功,返回响应头。

201 Created 请求成功并且服务器创建了新的资源 202 Accepted 服务器已接受请求,但尚未处理

3xx:重定向–要完成请求必须进行更进一步的操作。 301:请求的资源已被永久移到新位置。 302:临时重定向

303:See Other 临时性重定向,且总是使⽤ GET 请求新的 URI 。 304:缓存的内容并未改变。

4xx:客户端错误–请求有语法错误或请求无法实现。 400: 请求无效,前端提交数据的字段名称或者是字段类型和后台的实体类不一致。 401:请求要身份验证。

403 Forbidden 禁⽌访问

404:找不到页面。

5xx:服务器端错误–服务器未能实现合法的请求。 500:服务器遇到未知状况,无法处理。 503:服务器过载或维护。

3.移动端1px问题

首先明白:物理像素:移动设备出厂时,不同设备自带的不同像素,也称硬件像素; 逻辑像素: 即css中记录的像素。 window.devicePixelRatio是代表物理像素与css逻辑像素的比值。 iPhone的 devicePixelRatio==2,所以你设置1px的边框,实际显示物理像素2px显示,在iphone中会比较粗。

解决方法:

  1. 用小数写,但在ios和安卓低版本不适用
  2. 使用淘宝的flexible.js去适配。 核心好像是利用devicePixelRatio即物理像素与css逻辑像素的比值,假如是2,我就设置viewport中的scale为0.5,使它的物理像素等于css写的1px像素。

4.从输入url到显示页面都发生了什么

1.输入url,常见的http协议的端口号是80,https是443 2.查看浏览器是否有缓存,其中分为强制缓存和相对缓存 3.dns查询,用迭代查询和递归查询结合的方式查询 4.TCP三次握手建立连接 5.浏览器向服务器发送HTTP请求 6.浏览器接收响应 服务器在收到浏览器发送的HTTP请求之后,处理完的结果以HTTP的Response对象返回,主要包括状态码,响应头, 响应报文三个部分。 7.页面渲染,涉及浏览器的渲染过程和回流,重绘

5.浏览器渲染过程

  1. 解析HTML生成DOM树
  2. 解析CSS生成CSS规则树
  3. 将DOM树和CSS规则树合并生成渲染树
  4. 遍历渲染树开始布局,计算每一个节点的位置信息,将渲染树中每个节点绘制到屏幕上。

6.web性能优化

代码层面:css解析从右往左,合理设置选择器。css放上面,script放下面 缓存利用:使用CDN加速,http缓存头等 请求数量:合并css样式和js脚本,使用css图片精灵,图片懒加载 请求带宽:压缩文件,开启GZIP

  1. 减少HTTP请求。 图片Base64编码,只能用在大小小的图片。
  2. 图片懒加载
  3. 压缩图片资源文件,//可以用雪碧图
  4. CDN加速:分布在不同位置的服务器,存放静态资源,以便用户访问最近的节点资源。

7.CSRF 和 XSS

XSS

全称Cross Site Scripting,名为跨站脚本攻击,黑客将恶意脚本代码植入到页面中从而实现盗取用户信息等操作。

常见的攻击情景

1、用户A访问安全网站B,然后用户C发现B网站存在XSS漏洞,此时用户C向A发送了一封邮件,里面有包含恶意脚本的URL地址(此URL地址还是网站B的地址,只是路径上有恶意脚本),当用户点击访问时,因为网站B中cookie含有用户的敏感信息,此时用户C就可以利用脚本在受信任的情况下获取用户A的cookie信息,以及进行一些恶意操作。

这种攻击叫做反射性XSS

2、假设网站B是一个博客网站,恶意用户C在存在XSS漏洞的网站B发布了一篇文章,文章中存在一些恶意脚本,例如img标签、script标签等,这篇博客必然会存入数据库中,当其他用户访问该文章时恶意脚本就会执行,然后进行恶意操作。

这种攻击方式叫做持久性XSS,将携带脚本的数据存入数据库,之后又由后台返回。

预防措施:

1、对输入、输出结果进行过滤和必要的转义

2、尽量使用post,使用get方式时对路径长度进行限制

3、使用httponly禁止黑客通过脚本获取用户cookie数据,但这样无法完全阻止xss攻击,因为发送http请求并不需要主动获取cookie

CSRF

全称cross-site request forgery,名为跨站请求伪造,顾名思义就是黑客伪装成用户身份来执行一些非用户自愿的恶意以及非法操作

常见攻击情景:

用户A经常访问博客网站B,用户C发现网站B存在CSRF漏洞,想尽了各种办法勾引用户A访问了C写好的危险网站D,而此时用户A的cookie信息还没有失效,危险网站D中有向网站B求请求的非法操作,这样用户在不知情的情况下就被操控了。

这个时候就会有一个疑问,浏览器本身有同源策略啊,为什么在网站D还可以请求网站B的api,要记住浏览器对img、iframe和script的src是没有同源限制的!所以黑客完全可以利用动态添加这些标签的方法来实现跨站请求。 预防措施:

1、验证码

2、tokenId令牌

3、判断请求的Referer是否正确

CSRF和XSS的区别: 1、CSRF需要登陆后操作,XSS不需要

2、CSRF是请求页面api来实现非法操作,XSS是向当前页面植入js脚本来修改页面内容。

8.节流防抖

节流

用在连续点击div时, 是为了限制函数一段时间内只能执行一次,在延时的时间内,方法若被触发,则直接退出方法。

防抖

用在搜索输入时, 函数防抖在执行目标方法时,会等待一段时间。当又执行相同方法时,若前一个定时任务未执行完,则 clear 掉定时任务,重新定时。

二.CSS

1.CSS父子margin塌陷

原因:如果子div中有margin-top会外层div塌陷。

解决方法:1.给父盒子border

​ 2.给父盒子上padding-top

​ 3.给父盒子 overflow:hidden

2.CSS优先级

  1. 在属性后面加!important会覆盖任何样式
  2. 作为style属性写在元素内的样式
  3. id选择器
  4. 类选择器
  5. 标签选择器
  6. 通配选择器

3.css水平垂直居中

水平居中: 1.行内元素 text-align: center;

2.有宽度的块元素 margin: 0 auto;

垂直居中:

1.单行内容垂直居中:line-height: height;

2.绝对定位

position: absolute;
top: 50%;
transform: translate(0, -50%);

3.flex布局

display: flex;
align-items:center;(水平居中)
justify-content: center;

4.淘宝前端适配方案

适配物理像素。

dpr是设备物理像素与逻辑像素的比值,告诉浏览器应该使用多少个屏幕的实际像素来绘制单个 CSS 像素。 当dpr为2时,设置viewport的scale为0.5,实现真正的1px逻辑像素代表1px物理像素。 它会动态设置设计稿对应的html的font-size。拿淘宝来说的,他们用的设计稿是750的,所以html的font-size就是75px,那1rem为75px,当屏幕宽度变化,他的html的font-size也变化。(屏幕宽度/750px)*75

5.什么是BFC

BFC(Block formatting Context) 块级格式化上下文,布局规则如下: 1.内部的盒子会在垂直方向,一个一个地放置; 2.盒子垂直方向的距离由margin决定,属于同一个BFC的两个相邻Box的上下margin会发生重叠; 3.每个元素的左边,与包含的盒子的左边相接触,即使存在浮动也是如此; 4.BFC的区域不会与float重叠; 5.BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之也是如此; 6.计算BFC的高度时,浮动元素也参与计算。

BFC的作用 1、利用BFC避免margin重叠 2、自适应两栏布局 3、清除浮动

三.js

1.js数据类型

  1. 基本数据类型:Boolean、Undefined、Null、Number、String、symbol (es6 新增)
  2. 引用数据类型:Array, Boolean, Date, Error, Function, Math, Object,

基本数据类型存储在栈内存,引用数据类型存储在堆内存

2.NaN

NaN不是数字,和任何数都不相等。 typeof NaN == number。 //true 用isNaN()来判断

3.null和undefined区别

(1)null是一个表示”无”的对象,转为数值是为0,undefined是一个表示”无”的原始值,转为数值时为NaN。当声明的变量还未被初始化 时,变量的默认值为undefined

(2)Null用来表示尚未存在的对象,常用来表示函数企图返回一个不存在的对象;Undefined表示”缺少值”,就是此处应该有一个值,但是还没有定义

4.js获取变量类型的几种方法

​ 1.typeof:可以判断基本类型,但无法判断对象具体类型;且当判断基本包装类型创建的实例如 new String()时和array数组会判断成Object。 typeof原理:js 在底层存储变量的时候,会在变量的机器码的低位1-3位存储其类型信息。 ​ 2.instanceof:变量 instanceof 对象类型,返回Boolean值,且如new Array() instanceof Array或Object都返回true。当instanceof undefined和null时会报错。 ​ 3.Object.prototype.toString.call():可以判断具体对象类型,可以把new Array()、new Reg()分别判断成数组和正则表达式

5.js取消事件冒泡

取消事件冒泡 w3c标准: event.stopPropagation(); ie: event.cancelBubble=true;

取消默认: e.preventDefault(); ie: window.event.returnValue = false;

即取消默认行为也不冒泡,就直接return false

6.js闭包

闭包指有权限访问另一个函数的变量的函数。

深入理解的话: 当函数执行时,会创建一个称为执行期上下文。

函数还会获得它所在作用域的作用域链,是存储函数能够访问的所有变量对象的集合,即这个函数中能够访问到的东西都是沿着作用域链向上查找直到全局作用域。

函数每次执行时对应的执行环境都是唯一的,当函数执行完毕,函数都会失去对这个作用域链的引用,JS的垃圾回收机制就回收。

但是,当闭包存在时,即内部函数保留了对外部变量的引用时,这个作用域链就不会被销毁,此时内部函数依旧可以访问其所在的外部函数的变量,这就是闭包。

优点: 1:防止函数执行完后,变量被销毁,使其保存在内存中。 2:通过闭包和立即执行函数来封装函数, 全局变量可能会造成命名冲突,使用闭包不用担心这个问题,因为它是私有化,加强了封装性,这样保护变量的安全。

缺点: 由于它是驻留在内存中,会增大内存使用量,使用不当很容易造成内存泄露。

闭包的应用:

​ 1.采用函数引用方式的setTimeout调用

​ setTimeout的第一个参数一般是一个即将要执行的函数,第二个参数是一个延迟时间。

​ 如果一段代码想要通过setTimeout来调用,那么它需要传递一个函数对象的引用来作为第一个参数,但这个函数对象的引用无法为将 要被延迟执行的对象提供参数。此时可以调用另一个函数来返回一个内部函数的调用,将那个内部函数对象的引用传递给setTimeout 函数,内部函数执行时需要的参数,在调用外部函数时传递给它,setTimeout在执行内部函数时无需传递参数,因为内部函数仍然能 够防伪外部函数调用时提供的参数。

​ 2.小范围代替全局变量

//闭包,test2是局部变量,这是闭包的目的
//我们经常在小范围使用全局变量,这个时候就可以使用闭包来代替。
(function(){
    var test2=222;
    function outer(){
     alert(test2);
    }
    function test(){
     alert("测试闭包:"+test2);
    }
    outer();//222
    test();//测试闭包:222
}
)();
alert(test2);//未定义,这里就访问不到test2

7.跨域

  1. jsonp:动态创建script标签,利用script标签不受同源政策约束来跨域获取数据(只能用get请求);
  2. cors跨域:前端正常请求附带Origin字段附带网址,服务端接收到后,更据自己的跨域规则,如果允许访问,响应头设置Access-Control-Allow-Origin字段
  3. html5的postMessage方法跨域:不能和服务端交换数据,只能在两个窗口(iframe)之间交换数据。A页面有B页面的引用。A用postMessage方法发送消息,B页面通过message事件监听并接受消息:

看你的应用场景了:简单的跨域请求jsonp即可,复杂的cors,窗口之间JS跨域postMessage,开发环境下接口跨域用nginx反向代理或node中间件比较方便。

8.new()操作到底做了什么

  1. 创建一个新的空对象;
  2. 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象) ;
  3. 执行构造函数中的代码(为这个新对象添加属性) ;
  4. 返回新对象。

9.js继承

(1) 原型链继承:将父类实例作为子类的原型对象 缺点:所有子类的实例的原型都共享同一个父类实例的属性和方法。

(2)构造函数继承:在在函数中运行父级构造函数 缺点:父类函数没有共享,浪费内存。 无法继承原型链上的属性和方法

(3)组合继承:原型继承和构造函数继承的组合。 缺点:父级构造函数被调用两次,子类实例也有两份,浪费内存 实际上子类上会拥有超类的两份属性,只是子类的属性覆盖了原型对象上的属性

(4)原型式继承:传入参数obj,生成一个继承obj对象的对象 缺点:不是类式继承,而是原型式基础,缺少了类的概念

(5)寄生式继承(和原型式继承差不多 ):原型式继承套个壳子,增加你要传入的参数。 缺点:依旧没有类的概念

(5)寄生组合式继承:在子构造函数中执行父级函数,并创建Object.create(父级的原型对象)复值给obj,再修改obj的constructor的指 针指向子构造函数,最后obj作为子构造函数的原型对象。

10.this指向

在普通函数中,this总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。 在箭头函数中,并没有自己的this,所以箭头函数中的this是固定的,它指向定义该函数时所在的对象。

11.js严格模式

使用"use strict"即可。

  1. 不可使用未声明变量
  2. 不允许删除变量和函数
  3. 不允许八进制

作用: 1,消除代码不安全之处 2,提高编译效率

12.let const var区别

let: 1,不能重复声明一个变量 2,声明了他拥有块级作用域 3,let声明不提前

4,总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”

const: 1,要有默认值,声明后不能改变值

13.promise

1,promise原理: promise使用观察者模式,promise内部管理pending(等待),fulfilled(成功),rejected(失败)的状态改变,通过触发构造中的resolve()和reject()来改变状态,进而触发.then()中的回调函数,回调函数中返回promise对象,方便链式调用。

2,promise优点:异步编程,解决回调地狱。

3,promise缺点:1,promise一旦执行就不能停止如果想停止,不设置resolve()和reject()这样就一直是pending状态了 2,直接抛出错误catch接受

4,promise api

then

catch

Promise.reject

Promise.resolve

Promise.all

Promise.race

14.async函数和await

async function 声明用于定义一个返回 AsyncFunction 对象的异步函数。异步函数是指通过事件循环异步执行的函数,它会通过一个隐式的 Promise 返回其结果。返回一个promise对象。

async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待 Promise 的结果出来,然后恢复async函数的执行并返回解析值(resolved)。 注意:await只能在async中使用

15.call、apply、bind总结

相同点:都可以改变函数内部的this指向

区别点:1.call和apply会调用函数,并且改变函数内部this指向,而bind不会调用函数

​ 2.call和apply传递的参数不一样,call传递参数aru1,aru2..形式。apply 必须数组形式[arg]

主要应用场景:

1.call经常做继承

2.apply经常跟数组有关系,比如借助于数学对象实现数组最大值最小值

3.bind不调用函数,但是还能改变this指向,比如改变定时器内部的this指向

16.JS事件循环机制(Event Loop)

  1. 所有同步任务都在主线程上执行,形成一个执行栈。
  2. 只要异步任务有结果,就在事件队列中加入一个事件。
  3. 一旦执行栈中所有同步任务执行完后,系统就会去取事件队列中的事件执行。

主线程从“事件队列”中读取事件,这个过程是循环的,叫事件循环。

17.宏任务与微任务

宏任务(macrotask)微任务(microtask)
谁发起的宿主(Node、浏览器)JS引擎
具体事件1. script (可以理解为外层同步代码)
2. setTimeout/setInterval
3. UI rendering/UI事件
4. postMessage,MessageChannel
5. setImmediate,I/O(Node.js)
1. Promise
2. MutaionObserver
3. Object.observe(已废弃;Proxy 对象替代)
4. process.nextTick(Node.js)
谁先运行后运行先运行
会触发新一轮Tick吗不会

18.ES6新特性有哪些?

1.不一样的变量声明:const和let

2.模板字符串

3.箭头函数

4.解构赋值

5.类和继承

19.箭头函数与普通函数的区别

1.箭头函数是匿名函数,不能作为构造函数,不能使用new

2.箭头函数内没有arguments,可以用展开运算符…解决

3.箭头函数的this,始终指向父级上下文

4.箭头函数不能通过call() 、 apply() 、bind()方法直接修改它的this指向

5.箭头函数没有原型属性

20.深拷贝与浅拷贝

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

四.vue

1.vue双向绑定原理

​ vue.js采用数据劫持结合订阅者-发布者模式的方式,通过objec.defineproperty()来劫持各个属性的getter与setter。在数据变化时发布消息给订阅者,触发相应的监听回调。

2.vue优点

  1. 双向数据绑定
  2. 组件化开发 Vue.js通过组件,把一个单页应用中的各种模块拆分到一个一个单独的组件。最后拼装
  3. Virtual DOM 优化dom渲染操作
  4. 轻量高效 很小

3.vueRouter原理

vueRouter原理:更新视图而不重新请求页面。

hash模式: hash模式#后的字段对后端没有影响,不会加载。 原理:利用onhashchange事件来监听浏览器历史记录。

history模式:需要后台正确配置,如果url匹配不到资源,要返回一个index.html 原理:history模式利用了html5中的pushState()和replaceState() 方法。这两个方法应用于浏览器历史记录栈。 所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,需要把所有路由都重定向到根页面

4.vue的生命周期

1.beforeCreate: 组件实例刚刚被创建,无法访问方法和数据 2.created:组件实例创建完成,可访问数据和方法 3.beforeMount:挂载DOM之前,模板编译之前 4.mounted:模板编译之后,可获取dom,一般发起后端请求 5.beforeUpdate:组件更新之前 6.updated:组件更新之后 7.beforeDestory:组件被销毁前 8.destoryed:组件被销毁后

mounted和created的区别: created的时候,他的html的节点都没有渲染出来 mounted的时候,可以进行数据请求,进行数据绑定(此时HTML的结构已经出来了,可以将数据绑定到DOM结构上)

5.vue中的组件通信

父向子传: 使用props

子向父: 父亲=组件加 @getMessage=“getVal” 子组件 使用 $emit触发事件,父组件侦听事件

兄弟组件间传值: 先new Vue实例,在实例上用emit()触发事件,用emit()触发事件,用on()侦听事件,并执行相应的函数。

vuex传值

6. style scoped

在style中使用了scoped,则style标签里的css仅作用于当前页面。css对子组件不生效,可以在子组件的style标签里也使用scoped,这样可以使一个页面对应自己的css,避免影响到其他页面。

7.vue computed与watch区别

computed

  1. 支持缓存,只有依赖数据发生改变,才会重新进行计算
  2. 不支持异步,当computed内有异步操作时无效,无法监听数据的变化
  3. computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于data中声明过或者父组件传递的props中的数据通过计算得到的值
  4. 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed.
  5. .如果computed属性属性值是函数,那么默认会走get方法;函数的返回值就是属性的属性值;在computed中的,属性都有一个get和一个set方法,当数据变化时,调用set方法。

watch

  1. 不支持缓存,数据变,直接会触发相应的操作;

  2. watch支持异步;

  3. 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;

  4. 当一个属性发生变化时,需要执行对应的操作;一对多;

  5. 监听数据必须是data中声明过或者父组件传递过来的props中的数据,当数据变化时,触发其他操作,函数有两个参数,

    immediate:组件加载立即触发回调函数执行,

    deep: 深度监听,为了发现对象内部值的变化,复杂类型的数据时使用,例如数组中的对象内容的改变,注意监听数组的变动不需要这么做。注意:deep无法监听到数组的变动和对象的新增,参考vue数组变异,只有以响应式的方式触发才会被监听到。

五.计算机网络

1.3次握手与4次挥手

image-20210407132259208.png

3次握手

  • 第一次:客户端发送请求到服务器,服务器知道客户端发送,自己接收正常。SYN=1,seq=x
  • 第二次:服务器发给客户端,客户端知道自己发送、接收正常,服务器接收、发送正常。ACK=1,ack=x+1,SYN=1,seq=y
  • 第三次:客户端发给服务器:服务器知道客户端发送,接收正常,自己接收,发送也正常.seq=x+1,ACK=1,ack=y+1

image-20210407132255429.png

四次 挥手

  • 第一次:客户端请求断开FIN,seq=u
  • 第二次:服务器确认客户端的断开请求ACK,ack=u+1,seq=v
  • 第三次:服务器请求断开FIN,seq=w,ACK,ack=u+1
  • 第四次:客户端确认服务器的断开ACK,ack=w+1,seq=u+1

六.杂项

1.设计模式

  • 单例模式: 保证一个类仅有一个实例,并提供一个访问他的全局访问点。例如框架中的数据库连接

    单例模式分为饿汉式和懒汉式。

    饿汉模式: 优点:只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免了多线程同步的问题。 缺点:这个单例没有用到也会被创建,而且在类加载之后就被创建,内存就被浪费了。

    懒汉模式: 即用到时,再去创建实例,当多个线程时可能不安全。

  • js工厂模式:通过使用工程方法而不是new关键字。将创建实例封装在一个方法中,将所有实例化的代码集中在一个位置防止代码重复。

    js的工厂模式是用来创建对象的,通常一个函数传入参数,函数中new一个Object对象,并将参数加入到新对象中,最后返回这个对象。 简单工厂模式:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。 抽象类,具体实现类,工厂类,客户端。

    普通工厂模式: 首先定义工厂接口:之后实现工厂类。在客户端加多了代码。普通工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行,调用工厂类的create去创造产品。

  • 观察者模式: 一个对象通过添加一个方法使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。例如实现实现消息推送

2.Typescript和javascript

Typescript是微软推出的javascript的超集, TypeScript 中的数据要求带有明确的类型,JavaScript不要求。 TypeScript 通过类型注解提供编译时的静态类型检查。代码运行前识别某些类型的问题。

3.TypeScript 中 const 和 readonly 的区别?

  • 1、const只能在声明时初始化,而readonly既可以在声明中初始化,又可以在构造函数中初始化;
  • 2、const隐含static,不可以再写static const;readonly则不默认static,如需要可以写static readonly;
  • 3、const是编译期静态解析的常量(因此其表达式必须在编译时就可以求值);readonly则是运行期动态解析的常量;
  • 4、const既可用来修饰类中的成员,也可修饰函数体内的局部变量;readonly只可以用于修饰类中的成员。