面试题小总结

1.什么是单线程,和异步的关系?

  • 单线程 :只有一个线程,只能做一件事
  • 浏览器需要渲染 DOM
  • JS 可以修改 DOM 结构
  • JS 执行的时候,浏览器 DOM 渲染会暂停
  • 两段 JS 也不能同时执行(都修改 DOM 就冲突了)

解决方案 :异步


Vue是单向数据流,不是双向绑定
Vue的双向绑定不过是语法糖
Object.definePropert是用来做响应式更新的

2.js垃圾回收方式

当一个变量的生命周期结束之后,它所指向的内存就会被释放。js有两种变量,局部变量和全局变量,局部变量是在他当前的函数中产生作用,当该函数结束之后,该变量内存会被释放,全局变量的话会一直存在,直到浏览器关闭为止。

有两种方式: 标记清除、引用计数

标记清除:大部分浏览器使用这种垃圾回收,当变量进入执行环境(声明变量)的时候,垃圾回收器将该变量进行了标记,当该变量离开环境的时候,将其再度标记,随之进行删除。

引用计数:这种方式常常会引起内存的泄露,主要存在于低版本的浏览器。它的机制就是跟踪某一个值得引用次数,当声明一个变量并且将一个引用类型赋值给变量得时候引用次数加1,当这个变量指向其他一个时引用次数减1,当为0时出发回收机制进行回收。

3.逐进增强和优雅降级

  • 逐进增强针对低版本浏览器进行构建页面,保证最基本的功能,然后再针对高版本浏览器进行效果、交互等改进和追加功能达到更好的用户体验
  • 优雅降级一开始就构建完整的功能,然后再针对低版本浏览器进行兼容

4.for...in 迭代和 for...of 有什么区别

1、 推荐在循环对象属性的时候,使用 for...in,在遍历数组的时候的时候使用for...of。

2、 for in遍历的是数组的索引,而for of遍历的是数组元素值

3、for...of 不能循环普通的对象,需要通过和 Object.keys()搭配使用

4、for...in 遍历顺序以数字为先 无法遍历 symbol 属性 可以遍历到公有中可枚举的

5、从遍历对象的角度来说,for···in会遍历出来的为对象的key,但for···of会直接报错。

5.请说出vue.cli项目中src目录每个文件夹和文件的用法

  • components存放组件
  • app.vue主页面入口
  • index.js主文件入口
  • assets存放静态资源文件
  • api存放接口信息

6.渐进式框架的理解

  • 主张最少
  • 可以根据不同的需求选择不同的层级

7.谈一谈你对 nextTick 的理解?

当你修改了data的值然后马上获取这个dom元素的值,是不能获取到更新后的值,你需要使用$nextTick这个回调,让修改后的data值渲染更新到dom元素之后在获取,才能成功。

8.vue项目是打包了一个js文件,一个css文件,还是有多个文件?

根据vue-cli脚手架规范,一个js文件,一个CSS文件。

9.vue单向数据流

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子 组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

10.分别简述computed和watch的使用场景

  • 用官网的一句话来说,所有需要用到计算的都应该使用计算属性。多条数据影响一条数据时使用计算属性,使用场景购物车。
  • 如果是一条数据更改,影响多条数据时,使用watch,使用场景搜索框。

11.SPA单一首屏加载慢如何解决

安装动态懒加载所需插件;使用CDN资源。

12.如何解决数据层级结构太深的问题

在开发业务时,经常会岀现异步获取数据的情况,有时数据层次比较深,如以下代码: span 'v-text="a.b.c.d">, 可以使用vm.$set手动定义一层数据: vm.$set("demo",a.b.c.d)

13.Vuex 页面刷新数据丢失怎么解决?

需要做 vuex 数据持久化,一般使用本地储存的方案来保存数据,可以自己设计存储方案,也可以使用第三方插件(推荐使用 vuex-persist (脯肉赛斯特)插件,它是为 Vuex 持久化储存而生的一个插件。不需要你手动存取 storage,而是直接将状态保存至 cookie 或者 localStorage中)。

14.你都做过哪些 Vue 的性能优化?

  • 对象层级不要过深,否则性能就会差。
  • 不需要响应式的数据不要放在 data 中(可以使用 Object.freeze() 冻结数据)全局数据尽量不在每个页面重新定义获取 例如账号密码
  • 防止内部泄露,组件销毁后把全局变量和时间销毁
  • 适当采用 keep-alive 缓存组件
  • 防抖、节流的运用

15.如何实现浏览器响应式布局

  • 使用媒体查询(@media)
  • 使用flex弹性布局
  • 使用百分比单位
  • 使用rem单位
  • 使用VH、VW单位
  • 第三方插件(postcss-px-to-viewport会自动将px单位转换为vh vw)

16.cookie的弊端

每个特定的域名下最多生成的cookie的个数有限制IE和Opera会清理近期最少使用的cookie,firefox会随机清理cookiecookie最大为4096字节,为了兼容一般不超过4095字节安全性问题,如果cookie被人劫持,就可以获得所有的session信息

17.数据类型存储以及堆栈内存是什么(了解)

基本数据类型:直接存储在栈内存中,占据空间小,大小固定,属于被频繁使用的数据。指的是保存在栈内存中的简单数据段;number string 布尔

引用数据类型:同时存储在栈内存与堆内存中,占据空间大,大小不固定。

引用数据:类型将指针存在栈中,将值存在堆中。 当我们把对象值赋值给另外一个变量时,复制的是对象的指针,指向同一块内存地址,意思是,变量中保存的实际上只是一个指针,这个指针指向内存堆中实际的值,数组 对象

18.堆(heap)和栈(stack)有什么区别存储机制(了解)

栈: 是一种连续储存的数据结构,具有先进后出后进先出的性质。

通常的操作有入栈(压栈),出栈和栈顶元素。想要读取栈中的某个元素,就是将其之间的所有元素出栈才能完成。

堆: 是一种非连续的树形储存数据结构,具有队列优先,先进先出; 每个节点有一个值,整棵树是经过排序的。特点是根结点的值最小(或最大),且根结点的两个子树也是一个堆。常用来实现优先队列,存取随意。

19.宏任务和微任务(了解)

js中的一个机制,就是遇到宏任务,先将宏任务放入eventqueue,然后在执行微任务。

宏任务:setTimeout,setInterval,Ajax,DOM事件

微任务:Promise async/await

想明白这个机制 就要理解js单线程。因为JS是单线程语言,只能同时做一件事儿。js任务需要排队顺序执行,如果一个任务时间过长,后边的任务也会等着。假如,我们在请求一个网址时,图片加载很慢,网页总不能一直卡不出来,

这个时候就可以用异步来解决了,异步的特点不会阻塞代码的执行 ,解决了单线程等待的这个问题

在执行同步代码的时候,如果遇到了异步事件,js 引擎并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务

异步和单线程是相辅相成的,js是一门单线程语言,所以需要异步来辅助。

20.JS预解析(变量提升),它导致了什么问题?(了解)

JS代码在执行前,浏览器会对js代码进行扫描,默认的把所有带var和function声明的变量进行提前的声明或者定义,遵循先解析后使用的原则。 变量提升的表现是,在变量或函数声明之前访问变量或调用函数而不会报错。

原因 JavaScript引擎在代码执行前有一个解析的过程(预编译),创建执行上线文,初始化一些代码执行时需要用到的对象。 当访问一个变量时,会到当前执行上下文中的作用域链中去查找,而作用域链的首端指向的是当前执行上下文的变量对象,这个变量对象是执行上下文的一个属性, 它包含了函数的形参、所有的函数和变量声明,这个对象的是在代码解析的时候创建的。

首先要知道,JS在拿到一个变量或者一个函数的时候,会有两步操作,即解析和执行。

1.在解析阶段 JS会检查语法,并对函数进行预编译。解析的时候会先创建一个全局执行上下文环境,先把代码中即将执行的变量、函数声明都拿出来, 变量先赋值为undefined,函数先声明好可使用。在一个函数执行之前,也会创建一个函数执行上下文环境,跟全局执行上下文类似, 不过函数执行上下文会多出this、arguments和函数的参数。

全局上下文:变量定义,函数声明 函数上下文:变量定义,函数声明,this,arguments

2.在执行阶段,就是按照代码的顺序依次执行。

21.什么是服务端渲染(了解)

解释:服务端渲染的模式下,当用户第一次请求页面时,由服务器把需要的组件或页面渲染成 HTML 字符串,然后把它返回给客户端。客户端拿到手的,是可以直接渲染然后呈现给用户的 HTML 内容,不需要为了生成 DOM 内容自己再去跑一遍 JS 代码。使用服务端渲染的网站,可以说是“所见即所得”,页面上呈现的内容,我们在 html 源文件里也能找到。有了服务端渲染,当请求用户页面时,返回的body里已经有了首屏的html结构,之后结合css显示出来。

优点:

①首屏渲染快(关键性问题):相比于加载单页应用,我只需要加载当前页面的内容,而不需要像 React 或者 Vue 一样加载全部的 js 文件;

②SEO(搜索引擎)优化:不同爬虫工作原理类似,只会爬取源码,不会执行网站的任何脚本

③可以生成缓存片段、节能;

缺点:用户体验较差,不容易维护、通常前端改了部分html或者css,后端也需要改;

使用场景:vue全家桶或者react全家桶,都是推荐通过服务端渲染来实现路由的。

22.什么是函数式编程? 命令式编程?声明式编程?(了解)

声明式编程:专注于”做什么”而不是”如何去做”。在更高层面写代码,更关心的是目标,而不是底层算法实现的过程。

如:css, 正则表达式,sql 语句,html, xml…

命令式编程(过程式编程) : 专注于”如何去做”,这样不管”做什么”,都会按照你的命令去做。解决某一问题的具体算法实现。

如: for()

函数式编程:把运算过程尽量写成一系列嵌套的函数调用。

如 : forEach()

23.谈谈你对模块化开发的理解?(了解)

一个模块是实现一个特定功能的一组方法。在最开始的时候,js 只实现一些简单的功能,所以并没有模块的概念 ,但随着程序越来越复杂,代码的模块化开发变得越来越重要。

由于函数具有独立作用域的特点,最原始的写法是使用函数来作为模块,几个函数作为一个模块,但是这种方式容易造成全局变量的污染,并且模块间没有联系。

后面提出了对象写法,通过将函数作为一个对象的方法来实现,这样解决了直接使用函数作为模块的一些缺点,但是这种办法会暴露所 有的所有的模块成员,外部代码可以修改内部属性的值。

现在最常用的是立即执行函数的写法,通过利用闭包来实现模块私有作用域的建立,同时不会对全局作用域造成污染。

ES6 提出的方案,使用 import 和 export 的形式来导入导出模块。

vue充分运用

24.vue虚拟dom,diff算法(了解)

虚拟 DOM,其实就是用对象的方式取代真实的 DOM 操作,把真实的 DOM 操作放在内存当中,在内存中的对象里做模拟操作。当页面打开时浏览器会解析 HTML 元素,构建一颗 DOM 树,将状态全部保存起来,在内存当中模拟我们真实的 DOM 操作,操作完后又会生成一颗 dom 树,两颗 DOM 树进行比较,根据 diff 算法比较两颗 DOM 树不同的地方,只渲染一次不同的地方。

(个人理解)虚拟dom他不并不是真实的 dom ,是根据模板生成一个js对象(使用createElement,方法),根据这个js对象再去生成真实的dom,对复杂的文档DOM结构,提供一种方便的工具,进行最小化的DOM操作 ,是可以快速的渲染和高效的更新元素,提高浏览器的性能,

例如,一个 ul 标签下很多个 li 标签,其中只有一个 li 有变化,这种情况下如果使用新的 ul 去替代旧的 ul,因为这些不必要的 DOM 操作而造成了性能上的浪费,但是如果直接使用虚拟节点覆盖旧节点的话,减少了很多的不必要的 DOM 操作。

我们在渲染页面的时候 会对新的虚拟dom和旧的虚拟dom进行对比 只渲染不同的地方,而不再是像之前只要发生变化,全部的真实dom都要重新渲染,所以提高了渲染的效率。

缺点:首次渲染大量DOM时,由于多了一层虚拟DOM的计算,会比innerHTML插入慢

diff算法

diff 算法是一种通过同层的树节点进行比较的高效算法,比较方式:diff整体策略为:深度优先,同层比较

diff算法 当data发生改变 会根据新的数据生成一个新的虚拟dom ,新的虚拟dom和旧的虚拟dom进行对比,这个对比的过程就是diff算法,会找到不同地方,只去渲染不同的地方,

总的来说就是减少DOM,重绘和回流。

为什么要用虚拟DOM来描述真实的DOM呢?

创建真实DOM成本比较高,如果用 js对象来描述一个dom节点,成本比较低,另外我们在频繁操作dom是一种比较大的开销。所以建议用虚拟dom来描述真实dom。

25.Promise解决了什么问题

1.回调地狱问题

所谓回调地狱就是指把函数作为参数层层嵌套请求,这样层层嵌套,人们称之为回调地狱

有时候前端为了能够拿到异步的数据,使用了大量的回调函数,来获取将来异步执行成功之后的数据。从一定程度上来说,回调地狱能解决问题,但是有缺点,或者说不优雅,阅读性非常差。而Promise就解决了这个问题

Promise对象是一个构造函数,用来生成Promise实例,针对所有的异步


2.信任问题

回调函数不能保证什么时候去调用回调,以及使用什么方式去调用回调;而Promise一旦被确认成功或失败,就不能再被更改。

Promise成功之后仅调用一次resolve(),不会产生回调多次执行的问题。除非Promise再次调用。所以Promise很好地解决了第三方工具导致的回调多次执行(控制反转)的问题,这个问题也称为信任问题。

26.数组扁平化

.flat是个方法 不会改变原数组

数组扁平化:将嵌套的数组进行降维处理。

可以使用数组拍平方法 Array.prototype.flat(),接受一个参数

不传参数时,默认“拉平”一层,可以传入一个整数,表示想要“拉平”的层数。

传入 <=0 的整数将返回原数组,不“拉平”

Infinity 关键字作为参数时,无论多少层嵌套,都会转为一维数组

如果原数组有空位,Array.prototype.flat() 会跳过空位。

27.跨域的处理

jsonp: 利用 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP 请求一 定需要对方的服务器做支持才可以。

JSONP 优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持 get 方法具 有局限性,不安全可能会遭受 XSS 攻击。

声明一个回调函数,其函数名(如 show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获 取目标数据(服务器返回的 data)。 创建一个 <script> 标签,把那个跨域的 API 数据接口地址,赋值给 script 的 src,还要在这个地址中向服 务器传递该函数名(可以通过问号传参:?callback=show)。

服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符 串,例如:传递进去的函数名是 show,它准备好的数据是 show('我爱你') 。

最后服务器把准备的数据通过 HTTP 协议返回给客户端,客户端再调用执行之前声明的回调函数 (show),对返回的数据进行操作。

CORS:跨域资源共享(CORS)是一种机制;当一个资源访问到另外一个资源(这个资源放在 不同的域名或者不同的协议或者端口),资源就会发起一个跨域的 HTTP 请求需要浏览器和服务器同时支持;

1.整个 CORS 通信,都是浏览器自动完成。浏览器发现了 AJAX 请求跨源,就会自动添加一些附加的头 信息,有时还会多出一次附加的请求,但用户不会有感觉;

2.实现 CORS 的关键是服务器,只要服务器实现了 CORS 接口,就可以跨源通信

3.服务器对于不同的请求,处理方式不一样; 有简单请求和非简单请求

28.vue 实例是挂载到那个标签上的?

vue 实例最后会挂载在body 标签里面,所以我们在 vue 中是获取不了 body 标签的,如果要使用 body 标 签的话需要用原生的方式获取

29.图片懒加载是怎么实现的?

就是我们先设置图片的 data-set 属性(当然也可以是其他任意的,只要不会发送 http 请求就行了,作用 就是为了存取值)值为其图片路径,由于不是 src,所以不会发送 http 请求。

然后我们计算出页面 scrollTop 的高度和浏览器的高度之和, 如果图片距离页面顶端的坐标 Y(相对于整个页面,而不是浏览 器窗口)小于前两者之和,就说明图片就要显示出来了(合适的时机,当然也可以是其他情况),

这时 候我们再将 data-set 属性替换为 src 属性即可。

30.token 是什么?(加密)

  1. token 也可以称做令牌,一般由 uid+time+sign(签名)+[固定参数] 组成

uid: 用户唯一身份标识

time: 当前时间的时间戳

sign: 签名, 使用 hash/encrypt 压缩成定长的十六进制字符串,以防止第三方恶意拼接

固定参数(可选): 将一些常用的固定参数加入到 token 中是为了避免重复查库)

  1. token 在客户端一般存放于 localStorage,cookie,或 sessionStorage 中。在服务器一般存于数据 库中
  2. token 的认证流程

用户登录,成功后服务器返回Token给客户端。 客户端收到数据后保存在客户端 客户端再次访问服务器,将token放入 headers中 或者每次的请求 参数中 服务器端采用filter过滤器校验。校验成功则返回请求数据,校验失败则返回错误码

  1. token 可以抵抗 csrf,cookie+session 不行
  2. session 时有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session 就会 面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享 session
  3. 客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将 token 存放于 localStroage 等容器中。客户端每次访问都传递 token,服务端解密 token,就知道这 个用户是谁了。通过 cpu 加解密,服务端就不需要存储 session 占用存储空间,就很好的解决负载 均衡多服务器的问题了。这个方法叫做 JWT(Json Web Token)

31.安全问题 :CSRF 和 XSS 攻击?

CSRF ( Cross-site request forgery )跨站请求伪造

方法一、Token 验证:(用的最多)

  1. 服务器发送给客户端一个 token ;
  2. 客户端提交的表单中带着这个 token 。
  3. 如果这个 token 不合法,那么服务器拒绝这个请求。

方法二:隐藏令牌:

把 token 隐藏在 http 的 head 头中。

方法二和方法一有点像,本质上没有太大区别,只是使用方式上有区别。

方法三、Referer 验证:

Referer 指的是页面请求来源。意思是,只接受本站的请求,服务器才做响应; 如果不是,就拦截 XSS(Cross Site Scripting):跨域脚本攻击。

1.编码: 对用户输入的数据进行 HTML Entity 编码。 如上图所示,把字符转换成 转义字符。 Encode 的作用是将 $var`等一些字符进行转化,使得浏览器在最终输出结果上是一样的

若不进行任何处理,则浏览器会执行 alert 的 js 操作,实现 XSS 注入。进行编码处理之后,L 在浏览 器中的显示结果就是 <script>alert(1)</script> ,实现了将 `$var 作为纯文本进行输出,且 不引起 J avaScript 的执行。

2.过滤: 移除用户输入的和事件相关的属性。如 onerror 可以自动触发攻击,还有 onclick 等。(总而言是,过滤掉一些不安全的内容) 移除用户输入的 Style 节点、 Script 节点、 Iframe 节点。(尤其是 Script 节点,它可是支持跨域的呀,一定要移除)。 3.校正: 避免直接对 HTML Entity 进行解码。 使用 DOM Parse 转换,校正不配对的 DOM 标签。

备注: 我们应该去了解一下 DOM Parse 这个概念,它的作用是把文本解析成 DOM 结构。 比较常用的做法是,通过第一步的编码转成文本,然后第三步转成 DOM 对象,然后经过第二步的过滤。

32.CSRF 和 XSS 的区别

区别一:

  • CSRF :需要用户先登录网站 A ,获取 cookie
  • XSS :不需要登录。

区别二:(原理的区别)

  • CSRF :是利用网站 A 本身的漏洞,去请求网站 A 的 api 。
  • XSS :是向网站 A 注入 JS 代码,然后执行 JS 里的代码,篡改网站 A 的内容。

33.后台管理系统中的权限管理是怎么实现的?

登录: 当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个 token,

拿到 token 之后(我会将这个 token 存贮到 cookie 中,保证刷新页面后能记住用户登录状态),前端会 根据 token 再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。

权限验证: 通过 token 获取用户对应的 权限,动态根据用户的 权限算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。

具体思路:

登录成功后,服务端会返回一个 token(该 token 的是一个能唯一标示用户身份的一个 key),之后我 们将 token 存储在本地 cookie 之中,这样下次打开页面或者刷新页面的时候能记住用户的登录状态,不 用再去登录页面重新登录了。

ps:为了保证安全性,我司现在后台所有 token 有效期(Expires/Max-Age)都是 Session,就是当浏览器关 闭了就丢失了。重新打开游览器都需要重新登录验证,后端也会在每周固定一个时间点重新刷新 token,让后台用户全部重新登录一次,确保后台用户不会因为电脑遗失或者其它原因被人随意使用账 号。

用户登录成功之后,我们会在全局钩子 router.beforeEach 中拦截路由,判断是否已获得 token,在 获得 token 之后我们就要去获取用户的基本信息了 页面会先从 cookie 中查看是否存有 token,没有,就走一遍上一部分的流程重新登录,如果有 token, 就会把这个 token 返给后端去拉取 user_info,保证用户信息是最新的。 当然如果是做了单点登录得功 能的话,用户信息存储在本地也是可以的。当你一台电脑登录时,另一台会被提下线,所以总会重新登 录获取最新的内容。

先说一说我权限控制的主体思路,前端会有一份路由表,它表示了每一个路由可访问的权限。当用户登 录之后,通过 token 获取用户的 role ,动态根据用户的 role 算出其对应有权限的路由,再通过 router.addRoutes 动态挂载路由。但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是 绝对安全的,后端的权限验证是逃不掉的。

我司现在就是前端来控制页面级的权限,不同权限的用户显示不同的侧边栏和限制其所能进入的页面(也 做了少许按钮级别的权限控制),后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每 一个后台的请求不管是 get 还是 post 都会让前端在请求 header 里面携带用户的 token,后端会根据 该 token 来验证用户是否有权限执行该操作。若没有权限则抛出一个对应的状态码,前端检测到该状 态码,做出相对应的操作。 使用 vuex 管理路由表,根据 vuex 中可访问的路由渲染侧边栏组件。

具体实现:

创建 vue 实例的时候将vue-router挂载,但这个时候 vue-router 挂载一些登录或者不用权限的公用的页 面。

当用户登录后,获取用role,将 role 和路由表每个页面的需要的权限作比较,生成最终用户可访问的路 由表。

调用router.addRoutes(store.getters.addRouters)添加用户可访问的路由。

使用vuex管理路由表,根据 vuex 中可访问的路由渲染侧边栏组件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值