2020前端面试题72道总结

1.说说你对盒子模型的理解?

当对一个文档进行布局的时候,浏览器的渲染引擎会根据标准之一的css基础框盒模型,将所有元素表示为一个个矩形的盒子

一个盒子由四部分组成:

content 内容区域

padding 内边距 清除内容周围的区域,内边距是透明的,取值不能为负,受到盒子的background 属性影响

margin 外边距 在元素外创建额外的空白,空白通常指不能放其他元素的区域

border 边框 由粗细样式颜色三部分组成

在css 中 盒子模型可以分成:

W3C标准盒子模型

IE怪异盒子模型

默认情况下是W3C标准盒子模型

标准盒子模型

盒子总宽度 = width+padding+border+margin

盒子总高度 = height+padding+border+margin

width height 只是内容高度,不包含padding和border值

怪异盒子模型

盒子总宽度 = width + margin

盒子总高度 = height + margin

width height 包含 padding和border值

2.css选择器有哪些?优先级?哪些属性可以继承?

选择器:

id选择器、类选择器、标签选择器、后代选择器、子选择器、相邻同胞选择器、群组选择器、

还有一些不常用的选择器:

伪类选择器、伪元素选择器,属性选择器

css3 中新增的选择器:

层次选择器,伪类选择器 (新增属性)、属性选择器(新增属性)

优先级:

!important > 内联>类选择器>标签选择器

继承属性:

字体系列属性:

font

font-family 规定元素的字体系列

font-weight 设置字体的粗细

font-size 设置字体的尺寸

font-style 定义字体的风格

font-variant 偏大或偏小的字体

文本系列属性

text-indent 文本缩进

text-align 文本水平对刘

line-height 行高

word-spacing 增加或减少单词见的空白

letter-spacing 增加或减少字符间的空白

text-transform 控制文本大小写

direction 规定文本的书写方向

color 文本颜色

元素可见性 visibility

表格布局属性

caption-side 定位表格标题位置

border-collapse 合并表格边框

border-spacing 设置相邻单元格的边框间的距离

empty-cells 单元格的边框的出现与消失

table-layout 表格的宽度由什么决定

列表属性

list-style-type 文字前面的小点点样式

list-style-position 小点点位置

list-style 以上的属性可以通过这个属性集合

引用 qoutes 设置嵌套引用的引号类型

光标属性 cursor 箭头可以变成需要的形状

继承中比较特殊的几点

a标签的字体颜色不能被继承

h1-h6的字体大小也是不能被继承的

无继承的属性

dispaly

文本属性 vertical-align、text-decoration

盒子模型属性 宽度 高度 内外边距 边框

背景属性 背景图片 颜色 位置

定位属性 浮动 清楚浮动 定位position 等

生成内容属性 content couter-reset counter-increment

轮廓样式属性 outline-style

页面样式属性 size page-break-before page-break-after

3.元素水平居中的的方法有哪些?如果元素不定宽高呢?

实现居中方法分为两大类

居中元素的宽高已知

居中元素宽高未知

实现方式

利用定位+margin:auto


父级设置为相对定位 子级绝对定位 并且4个定位属性的值都设置了0 
如果子级没有设置宽高则会被拉开到和父级一样的宽高

利用定位+margin:负值

利用定位+transfrom

table布局

flex布局

grid布局

根据元素标签的性质,可以分为

内联元素居中布局

块级元素居中布局

内联元素居中布局

水平居中

行内元素可设置 text-align center

flex 布局设置父元素 display:flex justify-content center

垂直居中

单行文本父元素确认高度 height === line-height

多行文本父元素确认高度 display:table-cell vertical-align:middle

块级元素居中布局

水平居中

定宽:margin:0 auto;

绝对定位+left:50%+margin:负自身的一半

垂直居中

position: absolute设置left、top、margin-left、margin-top(定高)

display: table-cell

transform: translate(x, y)

flex(不定高,不定宽)

grid(不定高,不定宽),兼容性相对比较差

4.什么是响应式设计?响应式设计的基本原理是什么?如何做?

响应式网站设计 是一种网络页面设计布局,页面的设计与开发应当根据用户行为以及设备环境 进行相应的响应和调整

响应式网站常见的特点:

同时适配pc+平板+手机等

标签导航在接近手持终端设备时改变为经典的抽屉式导航

网站的布局会根据视口来调整模块的大小和位置

实现方式

原理 通过媒体查询检测不同的设备屏幕尺寸做处理,为了处理移动端,页面头部必须有meta声明viewport

实现响应式布局方式

媒体查询

给不同分辨率的设备编写不同的样式来实现响应式布局,可以给不同分辨率的屏幕设置不同的样式

百分比

通过百分比单位来实现响应式的效果

可以让组件随着浏览器的宽度和高度变化而变化从而实现响应式布局,有些元素并不继承父元素的尺寸 所以并不建议使用百分比实现响应式

vw/vh

vw表示相对于视图窗口的宽度,vh表示相对于视图窗口的高度,任意层级元素,在使用vw单位的情况下。1vw都等于视图宽度的百分之一

rem

rem 相当于根元素html的font-size属性,默认浏览器字体大小为16px 所以 1rem = 16px

还可以利用ui框架 的栅格布局实现响应式

响应式设计通常会从以下几个方面思考:

弹性盒子 和媒体查询等技术

使用百分比布局创建流式布局的弹性ui同时使用媒体查询限制元素的尺寸和内容变更范围

使用相对单位使得内容自适应调节

选择断点针对不同断点实现不同的布局和内容展示

总结

响应式布局的优点

面对不同分辨率设备灵活性强

能够快捷解决多设备显示适应问题

缺点

仅适用布局、信息、框架、并不复杂的部门类型网站

兼容各种设备工作量大,效率低下

代码累赘,会出现隐藏无用的元素,加载时间加长

一种折中性的设计解决方案,多方面因素影响而达不到最佳效果,

一定程度上改变了网站原有的布局结构,会出现用户混淆的情况

5.怎么理解重绘和回流?什么场景下会触发?

html中每个元素都可以理解为一个盒子 在浏览器解析过程中会涉及到回流与重绘:

回流:

布局引擎根据各种样式计算每个盒子再页面上大小与位置

重绘:

当计算好盒子模型的位置、大小及其他属性后,浏览器根据每个盒子特性进行绘制

回流触发机制:

回流主要式计算节点的位置和几何信息,当页面布局和几何信息发生变化的时候就会触发回流

删除或者添加可见的dom元素

元素的位置发生变化、元素的尺寸发生变化

内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代

页面一开始渲染的时候(这避免不了)

浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

重绘触发机制:

触发回流一定会触发重绘

颜色的修改

文本方向的修改

阴影的修改

6.如果要做优化,css提高性能的方法有哪些?

实现方式有很多主要如下:

内联首屏关键CSS

通过内联css关键代码能够使浏览器在下载完html后就能立刻渲染

而如果外部引用css代码,在解析html结构过程中遇到外部css文件,才会开始下载css代码,再渲染

所以,CSS内联使用使渲染时间提前

异步加载CSS

在css文件请求 下载 解析完成之前,css会阻塞渲染,浏览器将不会渲染任何已处理的内容,前面加载内联代码后没必要阻塞浏览器渲染,这时候就可以使用异步加载

通过rel属性将link元素标记为alternate可选样式表

使用javascript将link标签插到head标签最后

资源压缩

使用webpack gulp/grunt rollup 模块化工具 将css代码进行压缩,使文件变小,大大降低了浏览器的加载时间

合理使用选择器

不要嵌套使用过多复杂选择器,最好不要三层以上,

使用id选择器就没必要在进行嵌套

通配符和属性选择器效率最低,避免复用

减少使用昂贵的属性

在页面发生重绘的时候,昂贵属性如box-shadow / border-radius / filter/透明度/:nth-child 等 会降低浏览器的渲染性能

不要使用@import

css样式文件有两种引入方式,一种是link元素 另一种是@import

@import 会影响浏览器的并行下载,使得页面在加载时增加额外的延迟,增添了额外的往返耗时,而且多个@import 可能会导致下载顺序紊乱,

其他:

减少重排操作,以及减少不必要的重绘

了解哪些属性可以继承而来,避免对这些属性重复编写

cssSprite 合成所有icon图片,用宽高加上background-position的背景图方式显现出我们要的icon图 ,减少了http请求

把小的icon图片转换成base64编码形式

css3动画或者过渡尽量使用transform和opacity 来实现动画,不要使用left和top属性

css实现性能方式可以从选择器嵌套 属性特性 减少http这三面考虑,同时还有注意css代码的加载顺序

7.对前端工程师这个职位是怎么理解的?它的前景会怎么样?

8.说说JavaScript中的数据类型?存储上的差别?

基本类型,复杂类型

两种类型的区别是:储存位置不同

基本类型:

Number,String,Boolean,Undefined,null,symbol

引用数据类型:

统称为Object ,

Object,Array,Function,Date,RegExp,Map,Set等

存储区别:

基本数据类型存储在栈中,

引用类型的对象储存于堆中,

当变量赋值时,解析器首先要确认的就是这个值是基本类型值还是引用类型值

声明变量时不同的内存地址分配:

简单类型的值存放在栈中,在栈中存放的时对应的值

引用类型对应的值存储在堆中,在栈中存放的是指向堆内存的地址

不同的类型数据导致赋值变量时的不同:

简单类型赋值,是生成相同的值,两个对象不同的地址

复杂类型赋值,是将保存对象的内存地址赋值给另一个变量,也就是两个变量指向堆内存中同一个对象

9.typeof 与 instanceof 区别

typeof 操作符返回一个字符串,表示未经计算的操作数的类型

虽然 typeof null 为 object 这是javaScript 一个悠久的Bug 不代表null 就是引用数据类型 并且null 本身也不是对象 所以 null在typeof之后返回的是有问题的结果

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

实现原理 顺着原型链去找,直到找到相同的原型对象,返回true , 否则为 false

typeof 与 instanceof 都是判断数据类型的方法 区别如下

typeof 会返回一个变量的基本类型,instanceof,返回的是一个布尔值

instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型

typeof 也存在弊端 虽然可以判断基础数据类型 null除外

但是引用数据类型中,除了function 类型以外,其他的也无法判断

如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式“[object Xxx]”的字符串

10.说你对闭包的理解?闭包使用场景?

闭包是什么?

闭包就是能够读取其他函数内部变量的函数

一个函数和对其周围状态的引用捆绑在一起这样的组合就是个闭包,

也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域

在js中 每当创建一个函数,闭包就会在函数创建的同时被创建出来,作为函数内容与外部来凝结起来的一座桥梁

使用场景

创建私有变量,

延长变量的生命周期

一般函数的词法环境在函数返回后就被销毁,但是闭包会保存对创建时所在词法环境的引用, 即便创建时所在的执行上下文被销毁,但是创建时所在的词法环境依然存在,以达到延长变量的生命周期的目的,

11.bind apply call 区别? 如何实现一个bind?

作用是改变函数执行时的上下文,简而言之就更是改变函数运行时的this指向,

区别

apply 接受两个参数 第一个参数时this的指向,第二个参数是函数接受的参数,以数组的形式传入,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次,当第一个参数为null,undefined 的时候 默认指向window浏览器中

call 方法的第一个参数也是this的指向,后面传入的是一个参数列表,跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次

bind 方法 和 call 很相似,第一个参数也是this的指向,后面传入的也是一个参数列表(但是这个参数列表可以分多次传入)

改变this指向后不会立即执行,而是返回一个永久改变this指向的函数

总结:

三者都可以改变函数的this对象指向

三者第一个参数都是this要指向的对象,如果没有这个参数或参数为undefined 或 null 则默认指向全局window

三者都可以传参,但是apply 是数组,而call、是参数列表,且apply和call是一次性传入参数,而bind可以分为多次传入

bind 是返回绑定this之后的函数,apply call 则是立即执行

实现步骤:

bind 修改this指向,动态传递参数,兼容new关键字

12.说说你对事件循环的理解

首先,JavaScript是一门单线程语言,意味着同一时间内只能做一件事,但是这并不意味着单线程就是阻塞,而实现单线程非阻塞的方法就是事件循环

在JavaScript中所有的任务都可以分为:

同步任务:

立即执行的任务,同步任务一般会直接进入到主线程中执行

异步任务:

异步执行的任务,ajax网络请求,setTimeout定时函数等

流程

任务进入执行栈中 判断是同步任务还是异步任务 同步任务进入主线程,即主执行栈,异步任务进入任务队列,主线程内的任务执行完毕为空,会去任务队列读取对应的任务,推入主线程执行,不断重复这个过程就是事件循环

异步任务的细分 宏任务和微任务

微任务 执行时机是在主函数执行结束之后,当前宏任务结束之前,常见的微任务有 promise.then , MutaionObserver, Object.observer (已经废弃,proxy代替) , process.nextTick (nodejs中)

宏任务 的事件粒度比较大,执行的时间间隔是不能精准控制,对一些高实时性的需求不太适合,常见的宏任务有,script(外层同步代码)

setTimeout/setInterval ,UI rendering/UI事件 , postMessage、messageChannel,setImmediate、I/O (node)

执行机制 执行一个宏任务,如果遇到微任务就将它放到微任务的事件队列中,当宏任务执行完成后会查看微任务的事件队列,然后将里面的所有微任务以此执行完

13.DOM常见的操作有哪些?

文档对象模型dom 是html和xml 文档的编程接口,它提供了对文档的结构化的表述,并定义了一种方式可以使从程序中对该结构进行访问,从而改变文档的结构、样式和内容,任何HTML或XML文档都可以用Dom表示为一个由节点构成的层级结构

常见的DOM操作主要分为:

创建节点

createElement 创建新元素

createTextNode 创建一个文本节点

createDocumentFragment 用来创建一个文档碎片

表示一种轻量级的文档,主要是用来存储临时节点,然后把文档碎片的内容一次性添加到DOM中

create Attribute 创建属性节点 可以是自定义属性

查询节点

querySelector

传入任何有效的css选择器,即可选中单个DOM元素

如果页面上没有指定元素时,返回null

querySelectorAll

返回一个包含节点子树内所有与之相匹配的Element节点列表,如果没有相匹配,则返回一个空节点列表 ,返回的是一个静态实例并不是实时查询

更新节点

innerHTML 不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树

innerText,textContent 自动对字符串进行HTML编码,保证无法设置任何HTML标签,区别在于读取属性时,innerText,不返回隐藏元素的文本,而textCOntent返回所有文本

style

dom节点的style属性对应所有的css,可以直接获取或者设置

添加节点

innerHTML

appendChild

把一个子节点添加到父节点最后一个子节点

删除节点

删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild 把自己删掉,删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置

14.说说你对BOM的理解?常见的BOM对象你了解多少?

BOM , 浏览器对象模型,提供了独立于内容与浏览器窗口进行交互的对象,其作用就是跟浏览器做一些交互效果,比如如何进行页面的后退,前进,刷新,浏览器的窗口发生变化,滚动条的滚动,以及获取客户的一些信息,浏览器品牌版本,屏幕分辨率,

浏览器的全部内容可以看成DOM,整个浏览器可以看成BOM 区别如下:

DOM

文档对象模型

DOM就是把文档当作一个对象来看待

DOM的顶级对象是document

DOM主要学习的是操作页面元素

DOM是W3C标准规范

BOM

浏览器对象模型

把浏览器当做一个对象来看待

BOM的顶级对象是window

BOM学习的是浏览器窗口交互的一些对象

BOM是浏览器厂商在各自浏览器上定义的,兼容性较差

二、window

BOM的核心对象是window,它表示浏览器的一个实例,在浏览器中,window对象有双重角色,即是浏览器窗口的一个接口,又是全局对象,因此所有在全局作用域中声明的变量、函数都会变成window对象的属性和方法,

三、location

属性名 例子 说明
hash "#contents" utl中#后面的字符,没有则返回空串
host www.wrox.com:80 服务器名称和端口号
hostname www.wrox.com 域名,不带端口号
href Homepage | Wiley 完整url
pathname "/WileyCDA/" 服务器下面的文件路径
port 80 url的端口号,没有则为空
protocol http: 使用的协议
search ?q=javascript url的查询字符串,通常为?后面的内容

四、navigator

navigator对象主要用来获取浏览器的属性,区分浏览器类型,属性较多,且兼容性比较复杂

五,screen

六、history

15.JavaScript本地存储的方式有哪些?区别及应用场景?

JavaScript 本地缓存的方法有四种

cookie、sessionStorage、localStorage、indexedDB

cookie

Cookie 类型为 小型文本文件,指某些网站为了辨别用户身份而储存在用户本地终端上的数据,是为了解决http无状态导致的问题,一般不超过4kb的小型文本数据,由一个name、一个值value和其它几个用于控制cookie有效期、安全性、使用范围的可选属性组成,但是cookie每次请求都会被发送,如果不使用https并对其加密,其保存的信息很容易被窃取,导致安全风险,

localStorage

新方法,IE8及以上浏览器都兼容

· 特点;

生命周期:持久化的本地储存,除非主动删除数据,否则数据是永远不会过期的

存储的信息在同一域中是共享的

当本页操作新增、删除、修改了localStorage的时候,本页面不会触发storage事件,但是别的页面会触发storage事件

大小:5M (跟浏览器厂商有关系)

localStorage本质上是对字符串的读取,如果存储的内容多会消耗内存空间会导致页面变卡,

受到同源策略的限制

localStorage两个缺点:

无法像cookie 一样设置过期时间

只能存入字符串,无法直接存对象

sessionStorage

sessionStorage 和 localStorage使用方法基本一致唯一不同的是声明周期,一旦页面关闭之后sessionStorage将会删除数据

拓展:

indexedDB是一种低级API,用于客户端储存大量结构化数据,该api使用索引来实现对数据的高性能搜索,

优点:

储存量理论上没有上限

所有操作都是异步的,相比localStorage同步操作性能更高,尤其是数据量较大时

原生支持储存JS的对象

是个正经的数据库,意味着数据库能干的事都能干

缺点:

操作非常繁琐

本身有一定门槛

使用步骤:

打开数据库并且开始一个事务

创建一个object store

9 构建一个请求来执行一些数据库操作,像增加或提取数据

通过监听正确类型的dom事件以等待操作完成,

在操作结果上进行一些操作(可以在request对象中找到)

cookie、sessionStorage、localStorage三者的区别主要如下

储存大小: cookie数据大小不能超过4k,sessionStorage和localStorage存储大小可以达到5M或者更大

有效时间:localSorage 存储持久数据,浏览器关闭后数据不丢失除非主动删除,sessionStorage数据在当前浏览器窗口关闭后自动删除;cookie设置的过期时间之前一直有效

数据与服务器之间的交互方式

cookie的数据会自动的传递到服务器,服务器端也可以写cookie到客户端,sessionStorage和localStorage不会自动把数据发给服务器,仅在本地保存

应用场景

标记用户和跟踪用户行为 推荐使用cookie

长期保存在本地的数据或者令牌 建议使用localStorage

敏感账号一次性登录建议使用sessionstorage

储存大量数据、在线文档、保存编辑历史情况推荐使用indexedDB

16.什么是防抖和节流?有什么区别?如何实现?

防抖和节流本质上是优化高频率执行代码的一种手段

可以采用防抖和节流来降低调用的频率

定义:

防抖:

n秒后在执行该事件,若在n秒内被重复触发则重新计时

节流:

n秒内只运行一次,若在n秒内重复触发只有一次生效

区别:

相同点

都可以通过使用setTimeout实现,

目的都是,降低回调执行频率,节省计算资源

不同点

函数防抖 ,在一段连续操作结束后,处理回调,利用clearTimeout和setTimeout实现,

函数节流在一段连续操作中,每一段事件只执行一次,频率较高的事件中使用来提高性能

函数防抖关注一定事件连续触发的事件,只在最后执行一次,而函数节流一段时间内只执行一次

应用场景:

防抖

搜索框搜索输入,用户输入完再次发送请求

手机号、邮箱验证

窗口大小,窗口调整完成后在发送请求

节流

滚动加载,加载更多或者底部监听

搜索框,搜索联想功能

17.如何通过js判断一个数组?

通过原型链判断

__ proto __

实例的__ proto__属性指向其构造函数的原型对象.

const arr = [1,2,3]
console.log(arr.__proto__ === Array.prototype)  // true

constructor

实例的constructor属性指向构造函数本身

const arr = [1,2,3]
console.log(arr.constructor === Array)  // true

instanceof

instanceof可以判断Array是否是实例的构造函数(在原型链上即可)

isPrototypeOf()

判断Array的原型对象是否为在某个对象的原型链上

const arr = [1,2,3]
console.log(Array.prototype.isPrototypeOf(arr) )  // true

const arr = [1,2,3]
console.log(arr instanceof Array) // true

通过Object原型上的方法判断

Array.isArray()

es6中用于判断数组类型的方法

console.log(arr instanceof Array) // true

Object.prototype.toString

Object原型对象上的方法,被所有对象继承,返回'[Object type]'字符串

console.log(Object.prototype.toString.call(arr))  // [object Array]

18.作用域链的理解

作用域、即变量作用域又称上下文和函数生效的区域或集合

作用域分成 全局作用域、函数作用域、块级作用域

全局作用域

任何不再函数中或是大括号中声明的变量都是在全局作用域下,全局作用域下声明的变量可以在程序的任意位置访问

函数作用域

函数作用域也叫局部作用域,如果一个变量是在函数内部声明它就是在一个函数作用域下面,只能在函数内部访问,外部访问不到

块级作用域

es6中引入的let 和 const 关键字,和var不同在大括号中使用let和const声明的变量存在于块级作用域中,大括号外访问不到这些变量

词法作用域

又叫静态作用域,变量被创建时就确定好了,而非执行阶段确定的,也就是说写好代码时它的作用域就确定了,JavaScript遵循的就是词法作用域

作用域链:

JavaScript中使用一个变量的时候,首先JavaScript引擎会尝试在当前作用域下去寻找该变量,如果没找到,再到它的上层作用域寻找,以此类推直到该变量或是已经到了全局作用域

如果在全局作用域里仍然找不到该变量,他就会在全局范围内隐式声明该变量,或者直接报错

变量的引用会顺着作用域连进行查找,找不到则会往上一层找,一但到达顶层查找的过程都会停止

19.JavaScript原型,原型链?有什么特点?

JavaScript常被描述为一种基于原型的语言,每个对象拥有一个原型对象

当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索直到找到一个名字匹配的属性或达到原型链的顶层

准确的说,这些属性和方法定义在Object的构造器函数,(coustrtuctor functions) 之上的prototype属性上,而非实例对象本身

原型队形有一个自有属性constructor 这个属性指向该函数

二、原型链

原型对象也可能拥有原型,并从中继承方法和属性,一层一层一次类推,这种关系常被称为原型链

对象实例和它的构造器之间建立一个链接 proto属性,是从构造函数的prototype属性派生的,之后通过上溯原型链在构造器中找到这些属性和方法,

构造函数

总结

proto 作为不同对象之间的桥梁,用来指向创建它的构造函数的原型对象,每个对象的__proto__都是指向它的构造函数的原型对象prototype

一切对象都是继承Object对象,object对象直接继承根源对象null

一切的函数对象包括Object对象 都是继承Function对象

Object对象直接继承自Function对象

Function对象的proto 会指向自己的原型对象最总还是继承自Object对象

20.解释一下什么是事件代理?

事件代理通俗来讲就是把一个元素响应的事件 的函数委托到另一个元素

事件流都会经过三个阶段 : 捕获阶段->目标阶段->冒泡阶段 而事件委托就是在冒泡阶段完成

事件委托,会把一个或者一组元素的事件委托到它的父层或者更外层元素上,真正绑定事件的是外层元素,而不是目标元素,当事件响应到目标元素上时,会通过事件冒泡机制从而触发它的外层元素的绑定事件上,然后在外层元素上去执行函数

总结:

适合事件委托的事件有:click,mousedown,mouseup,keydown,keyup,keypress 从上面应用场景中,我们就可以看到使用事件委托存在两大优点:

减少整个页面所需的内存,提升整体性能

动态绑定,减少重复工作

但是使用事件委托也是存在局限性:

focus,blur这些事件没有事件冒泡机制,所以无法进行委托绑定事件

mousemove,mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,对性能消耗高,因此也是不合适于事件委托的

21.谈谈This对象的理解?

函数的this关键字在JavaScript中的表现略有不同,

this关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象

函数的调用方式决定了this的值(运行时绑定)

此外在严格模式和非严格模式之间也会有一些差别

绑定规则 ,根据不同的使用场合,this有不同的值,主要分为下面几种情况:

默认绑定,隐式绑定,new绑定,显示绑定

默认绑定

严格模式下,不能将全局对象用于默认绑定,this会绑定到undefined 只有函数运行在非严格模式下,默认绑定才能绑定到全局对象,

隐式绑定

函数还可以作为某个对象的方法调用,这时this就指这个上级对象,this永远指向的是最后调用它的对象

new绑定

通过构建函数new关键字生成一个实例对象,此时this指向这个实例对象

显示绑定

apply()、call()、bind()是函数的一个方法,作用是改变函数的调用对象,它的第一个参数就表示改变后的调用这个函数的对象

箭头函数

在ES6的语法中还提供了箭头函数语法,让我们在代码书写时就能确定this的指向

优先级

new绑定>显示绑定>隐式绑定>默认绑定

22.new操作符具体干了什么?

在JavaScript 中 new操作符用于创建一个给定构造函数的实例对象

流程:

创建一个新的对象obj

将对象与构建函数通过原型链连接起来

将构建函数中的this绑定到新建的对象obj上

根据构建函数返回类型作判断,如果是原始值则被忽略,如果是返回对象,需要正常处理

23.null和undefined的区别?

null 为一个字面量,而undefined则为一个全局变量

null 指的是一个变量未指向任何对象,而undefined通常代表一个变量处于值未定义(未赋值)的状态

概念方面

undefined:一般是简单数据类型,表示此处应该有个值,但是当前尚未赋值,

null 一般是复杂数据类型,表示不存在

用途方面:

undefined 返回执行之后无返回值,获取对象不存在的属性值,null原型链的最顶部的不存在对象

24.JavaScript代码中得 use strict 是什么意思?

use strict 是一个字符串字面量,用来指定代码运行于严格模式下,

25.同步和异步的区别?

同步时阻塞模式,异步是非阻塞模式

同步:

是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等下去,直到收到返回信息才继续执行下去

异步是指进程不需要一直等待下去,而是继续执行下面的操作不管其他进程的状态,当有信息返回时通知进程处理,提高执行效率

26.箭头函数和普通函数的区别?

this指向问题

箭头函数的this指向上层函数作用域的this对象,如果没有上层函数作用域,则指向顶部this(window) ,普通函数的this指向该函数的调用者,call,apply。bind 会改变篇普通函数的this,不能改变箭头函数的this

外形不同

箭头函数使用箭头定义,普通函数中没有

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

箭头函数不绑定arguments 在函数体中不存在,使用rest参数替代

箭头函数内 不能用yield且不能用作Generator函数,而普通函数可以

27.js数组和对象的遍历方式,以及几种方式的比较?

forEach

这里的forEach回调,中两个参数分别是value,index

forEach 无法遍历对象 IE不支持该方法 firefox 和 chrome支持

forEach 无法使用 break,continue 跳出循环,且使用return 是跳过本次循环

for in 循环 for 循环

这两种方法应该非常常见且使用很频繁,但实际上,两种方法都存在性能问题

for in 需要分析出array的每个属性,这个操作性能开销很大,用在key已知的数组上市非常不划算的,所以尽量不要使用for in 除非你不清楚要处理哪些属性

for 循环 循环每进行一次,就要检查一下数组的长度,读取属性要比读局部变量慢,尤其是当array里存放的都是dom元素,因为每次读取都会扫描一遍页面上的选择器相关元素,速度会大大降低

28.如何解决跨域问题?

跨域指的是浏览器不能执行其他网站的脚本,他是由浏览器的同源策略造成的,是浏览器加的安全限制

同源就是协议、域名和端口都相同。

跨域问题的四种解决方案

跨域资源共享(cors)

浏览器将cors请求分成两类:简单请求和预检请求

解决方式就是服务端在响应头中加入字段 Access-contro-allowl-origion:

ajax 的 jsonp

jsonp 是json 的一种使用模式 用于解决主流浏览器的跨域数据访问的问题

可以让所有人都能去访问 但是只是支持get请求

前端框架操作

修改配置文件 中的proxy 属性进行配置前端跨域

跨域问题只存在于浏览器,如果是服务器和服务器之间的通讯是不存在跨域问题的

可以再前端框架中设置代理服务器实现跨域请求

29.XML和JSON的区别

json 是一种轻量级的数据交换格式,它完全独立于语言。它基于JavaScript编程语言,易于理解和生成

xml 可扩展标记语言,用于传输数据并不是显示数据,这是W3C的推荐 ,它定义了一组规则,用于以人类可读和机器可读的格式编码文档,侧重于internet上的简单性通用性和可用性 是一种文本数据格式

json和xml之间的一些区别:

json是基于JavaScript语言 xml 源自于SGML

json是一种表示对象的方式,xml是一种标记语言,使用标记结构来表示数据项

json不提供对命名空间的任何支持,xml支持名称空间,

json支持数组,xml 不支持数组

xml文件相对难以阅读和解释,与xml相比,json的文件非常易于阅读

json不适用结束标记,xml有开始和结束标签

json的安全性较低,xml比json更安全

json 不支持注释,xml支持注释

json仅支持utf8编码 xml支持各种编码

30.谈谈你对webpack的看法

webpack 是一个模块打包工具 使用webpack管理模块 并分析模块之间的依赖关系,最终编译输出模块为HTML JavaScript CSS以及各种静态文件 让开发过程更加高效,对于不同类型的资源,webpack 有模块加载器对模块包分析他们之间的依赖关系,最终优化合并为静态资源

31.webpack的打包原理

初始化参数,从配置文件和shell语句中读取与合并参数,得出最终的参数

开始编译 用上一步得到的参数初始化 Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译

确定入口 根据配置中的entry找出所有的入口文件

编译模块 从入口文件出发 调用所有配置的Loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理

完成模块编译 在经过第4步使用Loader翻译完所有模块后,得到了每个模块被编译后的最终内容以及它们之间的依赖关系

输出资源 根据入口和模块之间的依赖关系,组成一个个包含多个模块的Chunk 再把每个Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后步骤

输出完成 确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写到文件系统

32.如何优化webpack的打包速度

webpack中每个模块有唯一的id 是从0开始递增的,整个打包以后的bundle.js 是一个匿名函数自执行,参数则为一个数组,数组的每一项都为个function,function的内容则为每个模块的内容,并按照require的顺序排列

我们看到webpack_require 是模块加载函数,接收模块id

a依赖b 所以在a中调用webpack加载模块的函数

33.说说webpack中常见的loader? 解决了什么问题?

Loader 用于对模块的 源代码进行转换 ,在import 或 加载 模块时预处理文件,在遇到import 或者require 加载模块的时候,webpack只支持对js和json 文件打包 像css,sass,png,这些类型的 文件的时候

webpack则无能为力,这时候就需要配置对应的loader进行文件内容的解析,

关于配置loader的方式有三种

配置方式:在webpack.config.js 文件中指定Loader

内联方式: 在每个import 语句中显式指定loader

li方式 :在shell命令中指定它们

css-loader:分析css模块之间的关系,合并成一个css

style-loader:把css-loader生成的内容,用style 标签挂载到页面的head中

34.说说webpack中常见的plugin? 解决了什么问题?

Plugin 是一种计算机应用程序,它和主应用程序互相交互,以提供特定的功能,是一种遵循一定规范的应用程序接口编写出来的程序,只能运行在程序规定的系统下,因为其需要调用原纯净系统提供的函数库或者数据

htmlwebpackPlugin 在打包结束后 自动生成一个html文件,并打包生成js模块引入到该html中

clean-webpack-plugin 删除 构建目录

mini-css-extract-plugin:提取css到一个单独的文件中

defineplugin 允许在编译时创建配置的全局对象,是一个webpack内置的插件,不需要安装

copy-webpack-plugin 复制文件或目录到执行区域,如vue的打包过程中,如果我们将一些文件放到public的目录下,那么这个目录会被复制到dist文件夹中

35.说说你对promise的了解?

promise是异步编程的一种解决方案,比其他 传统的解决方法更合理更强大,ES6将其写进了语言标准,统一了用法 promise对象是一个构造函数,用来生成promise实例

promise 的实例有三个状态

pending(进行中)

Resolved (已完成)

Rejected(已拒绝)

把一件事情交给promise时 他的状态就是Pending,任务完成了状态就会变成Resolved 失败变成Rejected

promise 的实例有两个过程:

pending -> fulfilled : Resolved 已完成

pending-> rejected: Rejected 已拒绝

promise 的特点:

对象的状态不受外界影响,只有异步操作的结果,可以决定当前是那种状态

一旦状态改变(从pending变为fulfilled和从pending变为rejected)就不会再变,任何时候都可以得到这个结果

实例方法:

then() 实例状态发生改变时的回调函数 ,第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,then返回的是一个新的Promise实例

catch()

用于指定发生错误时的回调函数

finally()

用于指定不管promise对象最后状态如何都会执行的操作

构造函数的方法

all()、rece()、allSetted()、resolve()、reject()、try()

36.async函数是什么?有什么作用

async 函数是异步的一种方案,可以让异步的操作同步执行。async 函数就是 Generator 函数的语法糖

声明形式

在函数前加上关键字async表示该函数是一个async 函数 函数内部通常有await

async函数对 Generator 函数的改进,体现在以下四点

内置执行器

Generator函数的执行必须靠执行器,所以有了co模块,而async函数自带执行器,也就是说,async函数与普通函数一模一样

更好的语义

async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。

更广的适用性

co模块决定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Peomise对象和原始类型的值

返回值是Promise

async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

async函数完全可以看作多个异步操作,包装成的一个 Promise 对象,而await命令就是内部then命令的语法糖

基本用法:

async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句

37.有使用过vue吗? 说说你对vue的理解?

vue是什么?

Vue.js(/vjuː/,或简称为Vue)是一个用于创建用户界面的开源JavaScript框架,也是一个创建单页应用的Web应用框架

vue核心特性

数据驱动(MVVM)

组件化

什么是组件化

一句话来说就是把图形、非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue中每一个.vue文件都可以视为一个组件

组件化优势:

降低整个系统的耦合度,在保持接口不变的情况下,我们可以替换不同的组件快速完成需求,例如输入框,可以替换为日历、时间、范围等组件作具体的实现

调试方便,由于整个系统是通过组件组合起来的,在出现问题的时候,可以用排除法直接移除组件,或者根据报错的组件快速定位问题,之所以能够快速定位,是因为每个组件之间低耦合,职责单一,所以逻辑会比分析整个系统要简单

提高可维护性,由于每个组件的职责单一,并且组件在系统中是被复用的,所以对代码进行优化可获得系统的整体升级

指令系统

指令 是带有 v- 前缀的特殊属性作用:当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM/

常用的指令

条件渲染指令 v-if

列表渲染指令v-for

属性绑定指令v-bind

事件绑定指令v-on

双向数据绑定指令v-model

Vue跟传统开发的区别

Vue所有的界面事件,都是只去操作数据的,Jquery操作DOM

Vue所有界面的变动,都是根据数据自动绑定出来的,Jquery操作DOM

Vue和React对比

相同点:

都有组件化思想

都支持服务器端渲染

都有Virtual DOM(虚拟dom)

数据驱动视图

都有支持native的方案:VueweexReactReact native

都有自己的构建工具:Vuevue-cliReactCreate React App

区别:

数据流向的不同。react从诞生开始就推崇单向数据流,而Vue是双向数据流

数据变化的实现原理不同。react使用的是不可变数据,而Vue使用的是可变的数据

组件化通信的不同。react中我们通过使用回调函数来进行通信的,而Vue中子组件向父组件传递消息有两种方式:事件和回调函数

diff算法不同。react主要使用diff队列保存需要更新哪些DOM,得到patch树,再统一操作批量更新DOM。Vue 使用双向指针,边对比,边更新DOM

38.你对SPA单页面的理解,它的优缺点分别是什么?如何实现SPA应用呢?

SPA(single-page application),翻译过来就是单页应用SPA是一种网络应用程序或网站的模型,它通过动态重写当前页面来与用户交互,这种方法避免了页面之间切换打断用户体验在单页应用中,所有必要的代码(HTMLJavaScriptCSS)都通过单个页面的加载而检索,或者根据需要(通常是为响应用户操作)动态装载适当的资源并添加到页面,页面在任何时间点都不会重新加载,也不会将控制转移到其他页面

单页面(SPA)与多页面(MPA)用的区别

单页面

由一个主页面和多个页面片段组成

刷新方式 局部刷新

url模式 哈希模式

SEO搜索引擎优化 难实现,可使用SSR方式改善

数据传递 比较容易

页面切换 速度快,用户体验良好

维护成本相对容易

多页面

多个页面组成

刷新方式 整页刷新

url 模式 历史模式

SEO搜索引擎优化 比较容易实现

数据传递 通过url、cookie、localStorage等传递

页面切换 切换加载资源速度慢,用户体验差

维护成本 相对复杂

单页面优点:

具有桌面应用的即时性、网站的可移植性和可访问性

用户体验好、快,内容的改变不需要重新加载整个页面

良好的前后端分离,分工更明确

缺点:

不利于搜索引擎的抓取 首次渲染速度相对较慢

实现一个SPA

原理

监听地址栏中hash变化驱动界面变化

pushsate记录浏览器的历史,驱动界面发送变化

39.SPA首屏加载速度慢的怎么解决?

什么是首屏加载?

首屏时间,指的是浏览器从响应用户输入网址地址,到首屏内容渲染完成的时间,此时整个网页不一定要全部渲染完成,但需要展示当前视窗需要的内容。首屏加载可以说是用户体验中最重要的环节。

SPA首屏加载时间慢的原因?

网络延时问题

资源文件体积是否过大

资源是否重复发送请求去加载了

加载脚本的时候,渲染内容堵塞了

解决方案

减少入口文件体积

常用手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,等路由被请求的时候会单独打包路由,使入口文件变小,加载速度增加

静态资源本地缓存

后端返回资源问题

采用HTTP缓存 设置Cache-Control,Last-Modified,Etag响应头

采用Service Worker离线缓存

前端合理利用localStorage

UI框架按需加载

在日常使用UI框架,例如element-UI、或者antd,我们经常性直接引用整个UI库实际用到的组件只有几个,所以我们要按需在js文件中设置并引用

图片资源的压缩

图片资源虽然不在编码过程中,但它却是对页面性能影响最大的因素

对于所有的图片资源,我们可以进行适当的压缩

对页面上使用到的icon,可以使用在线字体图标,或者雪碧图,将众多小图标合并到同一张图上,用以减轻http请求压力

组件重复打包

开启Gzip压缩

拆完包之后,我们再用gzip做一下压缩 安装compression-webpack-plugin

在vue.config.js中引入并修改webpack配置

使用SSR

SSR(Server side ),也就是服务端渲染,组件或页面通过服务器生成html字符串,在发送到浏览器从头搭建一个服务端渲染是很复杂的,vue应用建议使用Nuxt.js 实现服务端渲染

40.vue 路由原理

vue-router 提供三种路由模式

hash模式

默认模式,通过路径中的hash值来控制路由跳转,不存在兼容问题

history模式

H5新增的history API 相对hash而言,不会显示#号,但是需要服务器端配置

abstract模式

支持JavaScript的所有运行环境,常指Node.js服务器环境

hash模式实现原理

在正常路径后跟一个 # 号,匹配 # 后边的路径为前端路由,通过window.onhashchange方法来操控路由改变的时候切换内容

onhashchange 方法的触发机制

直接更改浏览器地址,在最后面增加或改变#hash;

通过改变location.href或location.hash的值;

通过触发点击带锚点的链接;

浏览器前进后退可能导致hash的变化,前提是两个网页地址中的hash值不同。

history模式实现原理

window.history这个对象中,包含浏览器的历史,而在HTML5中,新增了 history.pushStatehistory.replaceState,通过这两个API可以改变url地址且不会发送请求,同时还有window.onpopstate事件,实现原理与hash相似,只不过因为没有了 # 号,所以刷新页面还是会向服务器发送请求,而后端没有对应的处理措施的话,会返回404

onpopstate事件的触发时机

仅仅调用pushState方法或replaceState方法 ,并不会触发该事件;

只有用户点击浏览器倒退按钮和前进按钮,或者使用JavaScript调用back、forward、go方法时才会触发。

另外,该事件只针对同一个文档,如果浏览历史的切换,导致加载不同的文档,该事件也不会触发。

abstract模式实现原理

abstract模式是不依赖于浏览器环境,所以没有使用hash或者history等浏览器才会提供的API,而是VueRouter内部使用数组进行模拟了路由管理,在node环境,或者原生App环境下,都会默认使用abstract模式,VueRouter内部会根据所处的环境自行判断,默认使用hash模式,如果检测到没有浏览器API的时候,就会使用abstract模式

41.vue中组件和插件有什么区别?

组件是什么?

组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念来实现开发的模式,在Vue中每一个.vue 文件都可以看成一个组件

插件是什么?

插件通常用来为Vue添加全局功能,插件的功能范围没有严格的限制

两者的区别:

编写形式

编写一个组件,可以有很多方式,我们最常见的就是vue单文件的这种格式,每一个.vue文件我们都可以看成是一个组件 vue插件的实现应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象

注册形式

vue组件注册主要分为全局注册与局部注册

全局注册通过Vue.component方法,第一个参数为组件的名称,第二个参数为传入的配置项

局部注册只需在用到的地方通过components属性注册一个组件

插件的注册通过Vue.use()的方式进行注册(安装),第一个参数为插件的名字,第二个参数是可选择的配置项

注意事项

注册插件的时候,需要在调用 new Vue() 启动应用之前完成

Vue.use会自动阻止多次注册相同插件,只会注册一次

使用场景

组件 (Component) 是用来构成你的 App 的业务模块,它的目标是 App.vue

插件 (Plugin) 是用来增强你的技术栈的功能模块,它的目标是 Vue 本身

简单来说,插件就是指对Vue的功能的增强或补充

42.Vue组件之间的通信方式都有哪些?

vuex

State:放状态的地方

Mutation:唯一修改状态的地方,不支持异步

· Action:通过调用Mutation中的方法来达到修改状态的目的,支持异步

Getter:可以理解为计算属性

Module:模块每个模块拥有自己的 state、mutation、action、getter

父子组件间通信

子组件通过 props 属性来接受父组件的数据,然后父组件在子组件上注册监听事件,子组件通过 emit 触发事件来向父组件发送数据

通过 ref 属性给子组件设置一个名字。父组件通过 $refs 组件名来获得子组件,子组件通过 $parent 获得父组件,这样也可以实现通信

vue3新增加的 使用 provide/inject,在父组件中通过 provide提供变量,在子组件中通过 inject 来将变量注入到组件中。不论子组件有多深,只要调用了 inject 那么就可以注入 provide中的数据

兄弟组件间通信

使用 eventBus 的方法,它的本质是通过创建一个空的 Vue 实例来作为消息传递的对象,通信的组件引入这个实例,通信的组件通过在这个实例上监听和触发事件,来实现消息的传递

通过 $parent / $refs 来获取到兄弟组件,也可以进行通信。

任意组件间通信

使用 eventBus ,其实就是创建一个事件中心,相当于中转站,可以用它来传递事件和接收事件

43. 你了解vue的diff算法吗?说说看

diff算法是什么?

diff算法就是进行虚拟节点对比,并返回一个patch对象,用来存储两个节点不同的地方,最后用patch记录的消息去局部更新Dom。

换句话说就是 diff的过程就是调用名为patch的函数,比较新旧节点,一边比较一边给真实的DOM打补丁

两个特点

比较只会在同层级进行, 不会跨层级比较

在diff比较的过程中,循环从两边向中间比较

diff算法的步骤

用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文 档当中

当状态变更的时候,重新构造一棵新的DOM树。然后用新的树和旧的树进行比较(diff),记录两棵树差异

把第二棵树所记录的差异应用到第一棵树所构建的真正的DOM树上(patch),视图就更新了

比较方式

diff整体策略为:深度优先,同层比较

比较只会在同层级进行, 不会跨层级比较

比较的过程中,循环从两边向中间收拢

原理分析

当数据发生改变时,set方法会调用Dep.notify通知所有订阅者Watcher,订阅者就会调用patch给真实的DOM打补丁,更新相应的视图

44. 为什么需要 Virtual Dom

创建真实DOM的代价高:真实的 DOM 节点 node 实现的属性很多,而 vnode 仅仅实现一些必要的属性,相比起来,创建一个 vnode 的成本比较低。

触发多次浏览器重绘及回流:使用 vnode ,相当于加了一个缓冲,让一次数据变动所带来的所有 node 变化,先在 vnode 中进行修改,然后 diff 之后对所有产生差异的节点集中一次对 DOM tree 进行修改,以减少浏览器的重绘及回流

虚拟dom由于本质是一个js对象,因此天生具备跨平台的能力,可以实现在不同平台的准确显示

虚拟DOM在性能上的收益并不是最主要的,更重要的是它使得 Vue 具备了现代框架应有的高级特性。

45.vue3 的设计目标是什么? 做了哪些优化?

设计目标

vue3之前或许会面临的问题

随着功能的增长复杂组件的代码变得越来越难以维护,

缺少一种比较干净的在多个组件之间提取和复用逻辑的机制

类型推断不够友好

bundle的时间太久了

vue3 做了哪些事情

更小

移除了一些不常用的API

引入tree-shaking,可以将无用模块舍去,只打包需要的 ,使打包的整体体积变小了

更快

主要体现在编译方面

diff算法优化

静态提升

事件监听缓存

SSR优化

更友好

vue3在兼顾vue2options API的同时还推出了composition API,大大增加了代码的逻辑组织和代码复用能力

TypeScript支持

API设计一致性

提高自身可维护性

开放更多底层功能

vue3从很多层面都做了优化,可以分成三个方面:

源码、性能、语法API

源码

源码管理

vue3整个源码是通过 monorepo的方式维护的,根据功能将不同的模块拆分到packages目录下面不同的子目录中

这样使得模块拆分更细化,职责划分更明确,模块之间的依赖关系也更加明确,开发人员也更容易阅读、理解和更改所有模块源码,提高代码的可维护性

另外一些 package(比如 reactivity 响应式库)是可以独立于 Vue 使用的,这样用户如果只想使用 Vue3的响应式能力,可以单独依赖这个响应式库而不用去依赖整个 Vue

TypeScript

Vue3是基于typeScript编写的,提供了更好的类型检查,能支持复杂的类型推导

性能

体积优化、编译优化、数据劫持优化

语法api

就是composition API,其两大显著的优化

优化逻辑组织

相同功能的代码编写在一块,而不像options API那样,各个功能的代码混成一块

优化逻辑复用

vue2中,我们是通过mixin实现功能混合,如果多个mixin混合,会存在两个非常明显的问题:命名冲突和数据来源不清晰

而通过composition这种形式,可以将一些复用的代码抽离出来作为一个函数,只要的使用的地方直接进行调用即可

46. Vue3.0 所采用的 Composition Api 与 Vue2.x 使用的 Options Api 有什么不同?

Options API

Options API,即大家常说的选项API,即以vue为后缀的文件,通过定义methodscomputedwatchdata等属性与方法,共同处理页面逻辑

Options代码编写方式,如果是组件状态,则写在data属性上,如果是方法,则写在methods属性上

用组件的选项 (datacomputedmethodswatch) 组织逻辑在大多数情况下都有效

然而,当组件变得复杂,导致对应属性的列表也会增长,这可能会导致组件难以阅读和理

Composition API

在 Vue3 Composition API 中,组件根据逻辑功能来组织的,一个功能所定义的所有 API 会放在一起(更加的高内聚,低耦合)

即使项目很大,功能很多,我们都能快速的定位到这个功能所用到的所有 API

对比

两个方面作比较

逻辑组织

options API

如果一个大型组件,内部有很多的处理逻辑关注点

使得代码碎片化 理解和维护复杂组件变的困难

选项的分离掩盖了潜在的逻辑问题。此外,在处理单个逻辑关注点时,我们必须不断地“跳转”相关代码的选项块

Compostion API

Compositon API正是解决上述问题,将某个逻辑关注点相关的代码全都放在一个函数里,这样当需要修改一个功能时,就不再需要在文件中跳来跳去

逻辑复用

在vue2中我们使用mixin 去使用复用相同逻辑

使用单个mixin问题不大的但是当一个组件混入大量不同的mixin的时候会存在两个非常明显的问题

命名冲突 和数据来源不清晰

使用CompositionAPI 可以很好的解决这两个问题

总结

在逻辑组织和逻辑复用方面,Composition API是优于Options API

因为Composition API几乎是函数,会有更好的类型推断Composition API中见不到this的使用,减少了this指向不明的情况

如果是小型组件,可以继续使用Options API,也是十分友好的

47.说一下vue数据响应式原理

Vue 的数据响应式原理是由 JS 标准内置对象方法 Object.defineProperty 来实现的

3.0 版本中使用了 proxy 代替了 Object.defineProperty ,其有13中拦截方式,不需要对对象和数组分别进行处理,也无需递归进行拦截,这也是其提升性能最大的地方

vue响应式原理

vue响应式也叫作双向数据绑定,

通过Object.defineProperty()方法把数据设置为getter和setter的访问形式,这样我们就可以在数据被修改时在setter方法设置监视修改页面信息

vue实现数据响应式,是通过数据劫持侦测数据变化,发布订阅模式进行依赖收集与视图更新,换句话说是Observe,Watcher以及Compile三者相互配合,

Observe实现数据劫持,递归给对象属性,绑定setter和getter函数,属性改变时,通知订阅者

Compile解析模板,把模板中变量换成数据,绑定更新函数,添加订阅者,收到通知就执行更新函数

Watcher作为Observe和Compile中间的桥梁,订阅Observe属性变化的消息,触发Compile更新函数

48.说说对React的理解?有哪些特性?

React是什么?

React,用于构建用户界面的 JavaScript 库,提供了 UI 层面的解决方案

遵循组件设计模式、声明式编程范式和函数式编程概念,以使前端应用程序更高效

使用虚拟DOM来有效地操作DOM,遵循从高阶组件到低阶组件的单向数据流

帮助我们将界面成了各个独立的小块,每一个块就是组件,这些组件之间可以组合、嵌套,构成整体页面

特性:

JSX语法、单向数据绑定、虚拟DOM、声明式编程、Component

JSX语法

声明式编程

声明式编程是一种编程范式,它关注的是你要做什么,而不是如何做

它表达逻辑而不显式地定义步骤。这意味着我们需要根据逻辑的计算来声明要显示的组件

声明式编程方式使得React组件很容易使用,最终的代码简单易于维护

Component

React 中,一切皆为组件。通常将应用程序的整个逻辑分解为小的单个部分。我们将每个单独的部分称为组件

组件可以是一个函数或者是一个类,接受数据输入,处理它并返回在UI中呈现的React元素

一个组件该有的特点如下:

  • 可组合:个组件易于和其它组件一起使用,或者嵌套在另一个组件内部

  • 可重用:每个组件都是具有独立功能的,它可以被使用在多个UI场景

  • 可维护:每个小的组件仅仅包含自身的逻辑,更容易被理解和维护

通过上面的初步了解,可以感受到React存在的优势:

  • 高效灵活

  • 声明式的设计,简单使用

  • 组件式开发,提高代码复用率

  • 单向响应的数据流会比双向绑定的更安全,速度更快

49.说说 realDOM(真实DOM) 和 VirtualDOM(虚拟DOM)的区别? 优缺点?

Real DOM,真实DOM, 意思为文档对象模型,是一个结构化文本的抽象,在页面渲染出的每一个结点

Virtual Dom,本质上是以 JavaScript 对象形式存在的对 DOM 的描述

创建虚拟DOM目的就是为了更好将虚拟的节点渲染到页面视图中,虚拟DOM对象的节点与真实DOM的属性一一照应

区别

虚拟DOM不会进行排版与重绘操作,而真实DOM会频繁重排与重绘

虚拟DOM的总损耗是“虚拟DOM增删改+真实DOM差异增删改+排版与重绘”,真实DOM的总损耗是“真实DOM完全增删改+排版与重绘”

优缺点

真实DOM的优势:

易用

缺点:

效率低,解析速度慢,内存占用量过高

性能差:频繁操作真实DOM,易于导致重绘与回流

使用虚拟DOM的优势:

简单方便:如果使用手动操作真实DOM来完成页面,繁琐又容易出错,在大规模应用下维护起来也很困难

性能方面:使用Virtual DOM,能够有效避免真实DOM数频繁更新,减少多次引起重绘与回流,提高性能

跨平台:React借助虚拟DOM, 带来了跨平台的能力,一套代码多端运行

缺点:

在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化

首次渲染大量DOM时,由于多了一层虚拟DOM的计算,速度比正常稍慢

50.说说React生命周期有哪些不同阶段?每个阶段对应的方法是?

react生命周期分为3个阶段

创建阶段

更新阶段

卸载阶段

创建阶段主要分成了以下几个生命周期方法:

constructor 构造函数

实例过程中自动调用的方法,在方法内部通过super关键字获取来自父组件的props

在该方法中,通常的操作为初始化state状态或者在this上挂载方法

getDerivedStateFromProps

render

类组件必须实现的方法,用于渲染DOM结构,可以访问组件state与prop属性

componentDidMount

组件挂载到真实DOM节点后执行,其在render方法之后执行

此方法多用于执行一些数据获取,事件监听等操作

更新阶段的函数主要为如下方法:

getDerivedStateFromProps

shouldComponentUpdate

render

getSnapshotBeforeUpdate

componentDidUpdate

卸载阶段

componentWillUnmount

此方法用于组件卸载前,清理一些注册监听事件,或者取消订阅的网络请求等

一旦一个组件实例被卸载,其不会被再次挂载,而只可能是被重新创建

51.说说React中setState的执行机制?

一个组件的显示形态可以由数据状态和外部参数所决定,而数据状态就是state, 当需要修改里面的值的状态需要通过 调用setState来改变,从而达到更新组件内部数据的作用 setState第一个参数可以是一个对象,或者是一个函数,而第二个参数是一个回调函数,用于可以实时的获取到更新之后的数据 在使用setState更新数据的时候,setState的更新类型分成:同步更新,异步更新 在组件生命周期或React合成事件中,setState是异步 在setTimeout或者原生dom事件中,setState是同步 对同一个值进行多次 setState, setState 的批量更新策略会对其进行覆盖,取最后一次的执行结果

52. 说说对React中类组件和函数组件的理解?有什么区别?

语法上的区别

函数组件是一个纯函数,它接收一个props对象返回一个react元素。而类组件需要去继承React.Component并且创建render函数返回react元素

状态管理

函数组件是无状态组件 如果需要使用状态可以使用useState这个hook进行创建状态,类组件是有状态组件

生命周期

在函数组件中,并不存在生命周期,这是因为这些生命周期钩子都来自于继承的React.Component

· 调用方式

如果是一个函数组件,调用则是执行函数即可,如果是一个类组件,则需要将组件进行实例化,然后调用实例对象的render方法

53. 说说对React Hooks的理解?解决了什么问题?

是什么?

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性

至于为什么引入hook,官方给出的动机是解决长时间使用和维护react过程中常遇到的问题,例如:

  • 难以重用和共享组件中的与状态相关的逻辑

  • 逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面

  • 类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题

  • 由于业务变动,函数组件不得不改为类组件等等

在以前,函数组件也被称为无状态的组件,只负责渲染的一些工作

因此,现在的函数组件也可以是有状态的组件,内部也可以维护自身的状态以及做一些逻辑方面的处理

能够更容易解决状态相关的重用的问题:

  • 每调用useHook一次都会生成一份独立的状态

  • 通过自定义hook能够更好的封装我们的功能

编写hooks为函数式编程,每个功能都包裹在函数中,整体风格更清爽,更优雅

hooks的出现,使函数组件的功能得到了扩充,拥有了类组件相似的功能

54. 说说你对Redux的理解?其工作原理?

redux是专门用于集中式管理状态的javascript库,他并不是react的插件库。

遵循三大基本原则:

单一数据源

state 是只读的

使用纯函数来执行修改

工作原理

React的组件需要获取或者修改页面的数据,通过dispatch方法调用actions进入到Reducer函数中修改state的数据内容,state更新后,通知组件更新页面

55. 说说 React 性能优化的手段有哪些

1、避免使用内联函数,每次render渲染时,都会创建一个新的函数实例,应该在组件内部创建一个函数,讲事件绑定到函数,这样每次调用render时,就不会创建单独的函数实例。

2、使用react fragement 避免额外标记。用户创建新组件时,每个组件应具有单个父标签,父级不能有两个标签。所以顶部要有一个公共标签,所以经常在组件顶部添加额外标签div,这个div标签充当父标签意外,没有其他作用,这个时候可以使用fragement,它不会向组件引入任何的额外标记,但是可以作为父级标签

3、使用immutable。在react中使用immutablr能够带来性能优化,主要体现在减少渲染的次数,为了避免重复渲染,会在shouldComponentUpdate()中做对比,当返回true,执行render方法。immutable通过is方法完成对比

4、懒加载组件。从工程方面考虑,webpack存在代码拆分的能力,可以为应用创建多个包,并在运行时动态加载,减少初始包的大小, 在react中使用Suspense,lazy组件

5、事件绑定方式,从性能考虑,在render方法中使用bind和箭头函数,都会生成新的方法实例,在constructer中欧给使用bind和箭头函数,性能提高

6、服务端渲染,可以使用户更快的看到显然成功的页面,服务端渲染可以起一个node服务,可以使用express。koa等,调用react的renderToString方法,将跟组件渲染成字符串,再输出到相应中

7、组件拆分,合理使用hooks

56. vue、react、angular 区别

1、vue.js更轻量,压缩后大小只有20K+, 但React压缩后大小为44k,Angular压缩后大小有56k,所以对于移动端来说,vue.js更适合;

2、vue.js更易上手,学习曲线平稳,而Angular入门较难,概念较多(比如依赖注入),它使用java写的,很多思想沿用了后台的技术;react需学习较多东西,附带react全家桶。

3、vue.js吸收两家之长,借用了angular的指令(比如v-show,v-hide,对应angular的ng-show,ng-hide)和react的组件化(将一个页面抽成一个组件,组件具有完整的生命周期)

57. 说说你对 TypeScript 的理解?与 JavaScript 的区别

是什么?

TypeScriptJavaScript 的类型的超集,支持ES6语法 支持面向对象编程的概念

其是一种静态类型检查的语言,提供了类型注解,在代码编译阶段就可以检查出数据类型的错误

同时扩展了JavaScript 的语法,所以任何现有的JavaScript 程序可以不加改变的在 TypeScript 下工作

为了保证兼容性,typescript在编译阶段需要编译器编译成纯Javascript来运行,是为大型应用之开发而设计的语言 ​ 与JavaScript的区别:

JavaScript

一种脚本语言,用于创建动态网页、作为一种解释型语言,只能在运行时发现错误、弱类型,没有静态类型选项、 可以直接在浏览器中使用、不支持模块、泛型或接口、不支持编译其他 ES3,ES4,ES5 或 ES6+ 功能、大量的社区支持以及大量文档和解决问题的支持

TypeScript

JavaScript 的超集用于解决大型项目的代码复杂性、可以在编译期间发现并纠正错误、强类型,支持静态和动态类型、最终被编译成 JavaScript 代码,使浏览器可以理解、支持模块、泛型和接口、支持 ES3,ES4,ES5 和 ES6+功能、社区的支持仍在增长,而且还不是很大

58. 说说你对 TypeScript 中泛型的理解?应用场景?

泛型是什么?

程序设计(generic programming)是程序设计语言的一种风格或范式

泛型允许我们在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型 在typescript中,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性

应用场景

函数、类、接口

定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性的时候,这种情况下就可以使用泛型

59. 说说你对微信小程序的理解?优缺点?

是什么?

小程序是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或者搜一下即可打开应用

也体现了“用完即走”的理念,用户不用关心是否安装太多应用的问题。应用将无处不在,随时可用,但又无需安装卸载

优缺点:

优点:

随搜随用,用完即走:使得小程序可以代替许多APP,或是做APP的整体嫁接,或是作为阉割版功能的承载体

流量大,易接受:小程序借助自身平台更加容易引入更多的流量

安全

开发门槛低

降低兼容性限制

缺点:

用户留存:及相关数据显示,小程序的平均次日留存在13%左右,但是双周留存骤降到仅有1%

体积限制:微信小程序只有2M的大小,这样导致无法开发大型一些的小程序

受控微信:比起APP,尤其是安卓版的高自由度,小程序要面对很多来自微信的限制,从功能接口,甚至到类别内容,都要接受微信的管控

60. 说说你对发布订阅、观察者模式的理解?区别?

观察者模式

观察者模式定义了对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知,并自动更新

观察者模式一般至少有一个可被观察的对象 Subject ,可以有多个观察者去观察这个对象。二者的关系是通过被观察者主动建立的,被观察者至少要有三个方法——添加观察者、移除观察者、通知观察者。

当被观察者将某个观察者添加到自己的观察者列表后,观察者与被观察者的关联就建立起来了。此后只要被观察者在某种时机触发通知观察者方法时,观察者即可接收到来自被观察者的消息

观察者模式属于行为型模式,行为型模式关注的是对象之间的通讯,观察者模式就是观察者和被观察者之间的通讯

发布订阅模式

发布-订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者)。而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话)可能存在

观察者模式相比,发布订阅核心基于一个中心来建立整个体系。其中发布者订阅者不直接进行通信,而是发布者将要发布的消息交由中心管理,订阅者也是根据自己的情况,按需订阅中心中的消息。

区别

观察者是经典软件设计模式中的一种,但发布订阅只是软件架构中的一种消息范式

实现二者所需的角色数量有着明显的区别。观察者模式本身只需要2个角色便可成型,即观察者被观察者,其中被观察者是重点。而发布订阅需要至少3个角色来组成,包括发布者订阅者发布订阅中心,其中发布订阅中心是重点。

观察者是知道Subject的,Subject一直保持对观察者进行记录。然而,在发布订阅模式中,发布者和订阅者不知道对方的存在。它们只有通过消息代理进行通信。

在发布订阅模式中,组件是松散耦合的,正好和观察者模式相反。

观察者模式大多数时候是同步的,比如当事件触发,Subject就会去调用观察者的方法。而发布-订阅模式大多数时候是异步的(使用消息队列)

61. 项目做过哪些性能优化

(1) 减少 HTTP 请求数 (2) 使⽤ CDN (3) 使⽤外部 JavaScript 和 CSS (4) 避免重定向 (5) 图⽚懒加载 (6) 使⽤ iconfont (7) 尽量减少 iframe 使⽤ (8) 减少 DOM 元素数量 (9) 减少 DOM 操作 (10) 压缩 JavaScript 、 CSS 、字体、图⽚等 (11) 减少 DNS 查询 (12) js放在页⾯底部 (13)去除重复的js (13) 避免图⽚src为空 优化 CSS Sprite 字体裁剪

多域名分发划分内容到不同域名

62. 描述浏览器的渲染过程,DOM树和渲染树的区别

HTML 经过解析生成 DOM树; CSS经过解析生成 Style Rules。 二者一结合生成了Render Tree。 通过layout计算出DOM要显示的宽高、位置、颜色。 最后渲染在界面上

浏览器的渲染过程:

解析 HTML 构建 DOM(DOM 树),并行请求 css/image/js

CSS 文件下载完成,开始构建 CSSOM(CSS 树)

CSSOM 构建结束后,和 DOM 一起生成 Render Tree(渲染树)

布局(Layout):计算出每个节点在屏幕中的位置

显示(Painting):通过显卡把页面画到屏幕上

DOM 树 和 渲染树 的区别:

DOM 树与 HTML 标签一一对应,包括 head 和隐藏元素

渲染树不包括 head 和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的 css 属性

63. 你认为什么样的前端代码是好的

高复用低耦合,这样文件小,好维护,而且好扩展。

具有可用性、健壮性、可靠性、宽容性等特点

遵循设计模式的六大原则

64. 从浏览器地址栏输入url到显示页面的步骤

1、在浏览器地址栏输入URL

2、浏览器查看缓存,如果请求资源在缓存中并且新鲜,跳转到转码步骤

如果资源未缓存,发起新请求

如果已缓存,检验是否足够新鲜,足够新鲜直接提供给客户端,否则与服务器进行验证。

检验新鲜通常有两个HTTP头进行控制Expires和Cache-Control:

A )HTTP1.0提供Expires,值为一个绝对时间表示缓存新鲜日期

B )HTTP1.1增加了Cache-Control: max-age=,值为以秒为单位的最大新鲜时间

3、浏览器解析URL获取协议,主机,端口,path

4、浏览器组装一个HTTP(GET)请求报文

5、浏览器获取主机ip地址

1)浏览器缓存

2)本机缓存

3)hosts文件

4)路由器缓存

5)ISP DNS缓存

6)DNS递归查询(可能存在负载均衡导致每次IP不一样)

6、打开一个socket与目标IP地址,端口建立TCP链接,三次握手

7、TCP链接建立后发送HTTP请求

8、服务器接受请求并解析,将请求转发到服务程序,如虚拟主机使用HTTP Host头部判断请求的服务程序

9、服务器检查HTTP请求头是否包含缓存验证信息如果验证缓存新鲜,返回304等对应状态码

10、处理程序读取完整请求并准备HTTP响应,可能需要查询数据库等操作

11、服务器将响应报文通过TCP连接发送回浏览器

12、浏览器接收HTTP响应,然后根据情况选择关闭TCP连接或者保留重用,关闭TCP连接的四次挥手

13、浏览器检查响应状态码

14、如果资源可缓存,进行缓存

15、对响应进行解码

16、根据资源类型决定如何处理

17、解析HTML文档,构件DOM树,下载资源,构造CSSOM树,执行js脚本

遇到图片,样式表,js文件 启动下载

【构建dom树 -> 构建render树 -> 布局render树 -> 绘制render树】

18、显示页面

65. http 请求报文响应报文的格式

HTTP请求报文主要由请求行、请求头部、请求正文3部分组成

1,请求行

3部分组成,分别为:请求方法、URL以及协议版本,之间由空格分隔

请求方法包括GET、HEAD、PUT、POST、TRACE、OPTIONS、DELETE以及扩展方法

协议版本的格式为:HTTP/主版本号.次版本号,常用的有HTTP/1.0和HTTP/1.1

2,请求头部

请求头部为请求报文添加了一些附加信息,由“名/值”对组成,每行一对,名和值之间使用冒号分隔

常见的请求头部

请求头 说明
Host 接受请求的服务器地址,可以是IP:端口号,也可以是域名
User-Agent 发送请求的应用程序名称
Connection 指定与连接相关的属性,如Connection:Keep-Alive
Accept-Charset 通知服务端可以发送的编码格式
Accept-Encoding 通知服务端可以发送的数据压缩格式
Accept-Language 通知服务端可以发送的语言

请求头部的最后会有一个空行,表示请求头部结束,接下来为请求正文,这一行非常重要,必不可少

请求正文 可选参数 需要向服务器交互传输的值

HTTP响应报文主要由状态行、响应头部、响应正文3部分组成

1,状态行

3部分组成,分别为:协议版本,状态码,状态码描述,之间由空格分隔

2,响应头部

与请求头部类似,为响应报文添加了一些附加信息

响应头 说明
Server 服务器应用程序软件的名称和版本
Content-Type 响应正文的类型(是图片还是二进制字符串)
Content-Length 响应正文长度
Content-Charset 响应正文使用的编码
Content-Encoding 响应正文使用的数据压缩格式
Content-Language 响应正文使用的语言

66. Token cookie session 区别

Cookie

cookie网站为了辨别用户身份、进行 session 跟踪而储存在用户本地终端上的数据(通常经过加密),发送请求时,cookie会随着请求头一起发给服务器, cookie储存于浏览器

作用:识别用户信息,储存用户数据

cookie具有时效性,存活时间取决于不同的网站,在cookie未失效时,若再次登录该网站,则会自动登录,不需要重新输入账号与密码

Session: session是服务器为了保存用户状态而创建的一个特殊的对象。session储存在服务器

当用户第一次登陆网站时,服务器会生成一个session,并将该sessionID发送给用户储存在cookie中,当第二次发送请求时, sessionID就随着cookie一起发送,浏览器就可以识别出是谁发送的请求

当用户离开网站时,储存的session会被销毁

Token: Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

作用机制:服务器并不保存token,而是通过数据签名的方法,对数据用算法(如SHA-256)与私钥进行签名后作为Token,当Token发送给服务器时,服务会通过相同的算法与密钥进行签名,如果和Token中的签名相同,服务器就知道用户已经登录过了,并且可以直接得到用户的userID

1.session和cookie区别:

                            数据存放位置不同:Session数据是存在服务器中的,cookie数据存放在浏览器当中。
​
                            安全程度不同:cookie放在服务器中不是很安全,session放在服务器中,相对安全。

性能使用程度不同:session放在服务器上,访问增多会占用服务器的性能;考虑到减轻服务器性能方面,应使用cookie。

数据存储大小不同:单个cookie保存的数据不能超过4K,session存储在服务端,根据服务器大小来定。

2.token和session区别:

token是开发定义的,session是http协议规定的;

token不一定存储,session存在服务器中;

token可以跨域,session不可以跨域,它是与域名绑定的。

67. CORS跨域的原理

CORS是W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。它允许浏览器向跨源服务器发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制

CORS 原理解析

CORS是跨源AJAX请求的根本解决方法。JSONP只能发GET请求,但是CORS允许任何类型的请求

整个CORS通信过程都是浏览器自动完成的,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

两种请求

简单请求

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段(详见下文),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200

预检请求

也就是说浏览器会发送两次http请求。第一次Request Method: OPTIONS,第二次再请求所需内容。

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUTDELETE,或者Content-Type字段的类型是application/json

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为预检请求 (preflight)

68. 什么是MVVM

MVVM,是Model-View-ViewModel的简写,是一个软件架构设计模式,由M-V-VM三部分组成。它本质上就是MVC 的改进版。MVVM 就是将其中的View 的状态和行为抽象化,其中ViewModel将视图 UI 和业务逻辑分开,它可以取出 Model 的数据同时帮忙处理 View 中由于需要展示内容而涉及的业务逻辑。

MVVM采用双向数据绑定,view中数据变化将自动反映到model上,反之,model中数据变化也将会自动展示在页面上。把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

MVVM核心思想,是关注model的变化,让MVVM框架利用自己的机制自动更新DOM,也就是所谓的数据-视图分离,数据不会影响视图。

优点

1.Controller清晰简洁: ViewModel分离了大部分Controller代码,更加清晰和容易维护。

2.方便测试:开发中大部分Bug来至于逻辑处理,由于ViewModel分离了许多逻辑,可以对ViewModel构造单元测试。

3.开发解耦(举两个例子):

a.一人负责逻辑实现、另一人负责UI实现;

b.敏捷开发时,会发经常发不是等后端做好了接口我们再去开发,不过在没有接口的情况下通常我们可以把Controller和View完成。

缺点

1.看起来代码会比MVC多点

2.需对每个Controller实现绑定,这是分离不可避免的工作量。

69. 说说你对版本管理的理解?常用的版本管理工具有哪些?

版本控制(Revision control)是一种在开发的过程中用于管理我们对文件、目录或工程等内容的修改历史,方便查看更改历史记录,备份以便恢复以前的版本的软件工程技术。简单来说就是用于管理多人协同开发项目的技术。

版本控制分类

本地版本控制系统、

优点:

简单,很多系统中都有内置

适合管理文本,如系统配置

缺点:

其不支持远程操作,因此并不适合多人版本开发

集中式版本控制系统、

优点:

适合多人团队协作开发

代码集中化管理

缺点:

单点故障

必须联网,无法单机工作

代表工具有SVNCVS

分布式版本控制系统

常见的版本控制工具

Git

SVN(Subversion) ​ CVS(Concurrent Versions System) ​ VSS(Micorosoft Visual SourceSafe) ​ TFS(Team Foundation Server) ​ Visual Studio Online

版本控制系统的优点如下

  • 记录文件所有历史变化,这是版本控制系统的基本能力

  • 随时恢复到任意时间点,历史记录功能使我们不怕改错代码了

  • 支持多功能并行开发,通常版本控制系统都支持分支,保证了并行开发的可行

  • 多人协作并行开发,对于多人协作项目,支持多人协作开发的版本管理将事半功倍

70. 说说你对Git的理解?、

git 是一个分布式管理版本的控制软件,其作用就是可以让你更好的管理你的程序,

工作原理

当我们通过git init创建或者git clone一个项目的时候,项目目录会隐藏一个.git子目录,其作用是用来跟踪管理版本库的

Git 中所有数据在存储前都计算校验和,然后以校验和来引用,所以在我们修改或者删除文件的时候,git能够知道

Git用以计算校验和的机制叫做 SHA-1 散列(hash,哈希), 这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来

当我们修改文件的时候,git就会修改文件的状态,可以通过git status进行查询,状态情况如下:

已修改(modified):表示修改了文件,但还没保存到数据库中。

已暂存(staged):表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。

已提交(committed):表示数据已经安全的保存在本地数据库中。

文件状态对应的,不同状态的文件在Git中处于不同的工作区域,主要分成了四部分:

工作区:相当于本地写代码的区域,如 git clone 一个项目到本地,相当于本地克隆了远程仓库项目的一个副本

暂存区:暂存区是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中

本地仓库:提交更新,找到暂存区域的文件,将快照永久性存储到 Git 本地仓库

远程仓库:远程的仓库,如 github

git日常简单的使用的6个命令:

add commit push pull clone checkout

71. 说说Git常用的命令有哪些

本地git基本命令

    1、将文件添加到仓库         git add Readme #指定将Readme文件添加到暂存区         git add . #将当前目录中的所有文件全部添加到暂存区         git status #查看更改了哪些,创建了哪些,哪些没有添加到仓库,哪些添加到了仓库         git status diff readme #查看readme文件具体修改了哪些         git commit -m "commit tag" # git commit告诉Git,把文件提交到仓库-m后面输入的是本次提交的说明(版本名字)

        说明:

执行git commit 命令时必须配置用户信息

          git config --global user.name "Tom Git"           git config --global user.email [email protected]

    2、回滚         git log #查看所有提交到仓库的版本记录: git log -2         git reflog #查看所有操作记录(状态的md5值和改变的值)         git reset --hard d9e0ed0 #回到指定版本(d9e0ed0是创建版本的MD5值得前6位或者7位)         git reset --hard HEAD^ #回到上一个版本         注:这样可以回到第一次提交到仓库的状态,但再使用git log看不到其他几次的md5值了

    3、撤销修改

        vim Readme #我们在Readme文件中写了一些错误的代码         git add . #然后又一不小心将文件从工作区提交到了 stage区         git reset HEAD Readme #将Readme中刚提交到 stage区 的代码撤回到工作区         git status #查看目前工作区状态         git checkout -- Readme #将Readme在工作区错误的代码丢弃

    4、**删除操作(两种方法)**

        方法1:这种方法需要执行git add .

          rm Readme           git add .           git commit -m "delete file by git rm"           git reset --hard HEAD^

        方法**2:**这种方法可以省去执行git add .

          git rm Readme           git commit -m "delete file by git rm"           git reset --hard HEAD^

        注: 在没有git commit前,使用 git checkout -- Readme 可以恢复删除的文件(Readme)

    5、强制使用master覆盖本地代码

        $ git fetch --all         $ git reset --hard origin/master         $ git pull

			 git config --global user.name "用户名"

git config --global user.email "邮箱地址" ———设置用户信息 git config --list ————查看配置信息 git init ————本地初始化Git仓库 git clone ————远程仓库地址

本地仓库操作:

  • git status ————查看文件状态

  • git add —————将文件的修改加入暂存区

  • git reset ————将暂存区的文件取消暂存(git reset 文件名)或者是切换到指定版本(git reset --hard 版本号)

  • git commit ————将暂存区的文件修改提交到版本库(git commit -m msg 文件名)

  • git log ————查看日志

远程仓库操作:

  • git remote ————查看远程仓库(-v参数查看远程仓库更加详细的信息)

  • git remote add ————添加远程仓库(git remote add 简称(origin) 远程仓库地址)

  • git clone ————从远程仓库克隆(git clone 远程仓库地址)

  • git pull ————从远程仓库拉取(git pull 远程仓库简称 分支名称)(本地仓库存在文件,pull下拉报错——>在git pull命令后加入参数--allow-unrelated-histories)

  • git push ————推送到远程仓库(git push 远程仓库简称 分支名称)

分支操作:

  • git branch ————查看分支(git branch 列出所有本地分支/git branch -r 列出所有远程分支/git branch -a 列出所有本地分支和远程分支)

  • git branch [name] ———— 创建分支(git branch 分支名称)

  • git checkout [name] ————切换分支(git checkout 分支名称)

  • git push [shortName] [name] ————推送至远程仓库分支(git push 远程仓库简称 分支命令)

  • git merge [name] ————合并分支(git merge 分支命令) 注:分支合并,手动修改,提交时,冲突,可以在命令后加 -i 注:特殊窗口:i——>输入备注 ESC——>结束备注 :——>输入保存退出命令 wq——>保存退出

标签操作:

  • git tag ————查看标签

  • git tag [name] ————创建标签

  • git push [shortName] [name] ————将标签推送至远程仓库(git push 远程仓库简称 标签名)

  • git checkout -b [branch] [name] ————检出标签(git checkout -b 分支名 标签名)

72. 说说 git 发生冲突的场景?如何解决?

一般情况下,出现冲突的场景有如下:

多个分支代码合并到一个分支时

多个分支向同一个远端分支推送

具体情况就是,多个分支修改了同一个文件(任何地方)或者多个分支修改了同一个文件的名称

冲突解决方法

情景一:在当前分支上,直接修改冲突代码--->add--->commit。

情景二:在本地当前分支上,修改冲突代码--->add--->commit--->push

现在要做的事情就是将冲突的内容进行更改,对每个文件使用 git add 命令来将其标记为冲突已解决。一旦暂存这些原本有冲突的文件,Git就会将它们标记为冲突已解决

Git无法自动合并分支时,就必须首先解决冲突,解决冲突后,再提交,合并完成

解决冲突就是把Git合并失败的文件手动编辑为我们期望的内容,再提交

你可能感兴趣的:(面试题总结,javascript,vue.js,webpack,前端,前端框架)