JS面试题

2022-03-07  本文已影响0人  我家有个王胖胖

一.一个完整的URL 解析过程
DNS解析过程
1 用户输入 URL 地址。
2 如果输入的是域名,对 URL 地址进行 DNS 域名解获得IP地址。
3 建立 TCP 连接(三次握手)。
4 浏览器向 web 服务器发送一个 HTTP 请求报文。
5 服务器返回 HTTP 响应报文给客户端。
6 关闭 TCP 连接(四次挥手)。
7 浏览器解析文档资源并渲染页面。
解析过程:浏览器解析的资源(html,svg,Xhtml等),解析完成后都会生成一个完整的DOM Tree ,css资源则会解析成CSS Rule Tree,生成之后进行浏览器渲染,保证脚本执行前已完成DOM渲染会放在body标签结束之后。

二.EventLoop机制:Event Loop即事件循环,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
为什么JS是单线程?
JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?产生了冲突。
单线程执行机制:
1)所有同步任务都在主线程上执行,形成一个[执行栈]execution context stack
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
avaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
① 宏任务(macrotask)

② 微任务(microtask)

宏任务和微任务的执行顺序:

宏任务与微任务.png

注意:promise本身是同步的,promise的回调then是异步的。
三.MVC与MVVM的区别
3.1mvc


mvc
  1. View 传送指令到 Controller
  2. Controller 完成业务逻辑后,要求 Model 改变状态
  3. Model 将新的数据发送到 View,用户得到反馈

mvc中的所有通信都是单向的

3.2mvp
MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向。

mvp

1. 各部分之间的通信,都是双向的。

2. View 与 Model 不发生联系,都通过 Presenter 传递。

3. View 非常薄,不部署任何业务逻辑,称为"被动视图"(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。
3.3mvvm
MVVM 模式将 Presenter 改名为 ViewModel,基本上与 MVP 模式完全一致。

mvvm

唯一的区别是,它采用双向绑定(data-binding):View的变动,自动反映在 ViewModel,反之亦然。AngularEmber 都采用这种模式。

四:Vue数据双向绑定原理
①Vue2.X的双向绑定响应式原理
什么是响应式,也即是说,数据发生改变的时候,视图会重新渲染,匹配更新为最新的值。
Object.defineProperty 为对象中的每一个属性,设置 get 和 set 方法,每个声明的属性,都会有一个 专属的依赖收集器 subs,当页面使用到 某个属性时,触发 ObjectdefineProperty - get函数,页面的 watcher 就会被放到属性的依赖收集器 subs 中,在数据变化时,通知更新;
当数据改变的时候,会触发Object.defineProperty - set函数,数据会遍历自己的依赖收集器 subs,逐个通知 watcher,视图开始更新;

②Vue3.x双向绑定响应式数据原理
Vue3.x改用Proxy替代Object.defineProperty。
因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。
Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢?
判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。
监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

③Vue3 里为什么要用 Proxy API替代 defineProperty API?
defineProperty API 的局限性最大原因是它只能针对单例属性做监听。
Vue2.x中的响应式实现正是基于defineProperty中的descriptor,对 data 中的属性做了遍历 + 递归,为每个属性设置了 getter、setter。这也就是为什么 Vue 只能对 data 中预定义过的属性做出响应的原因。
Proxy API的监听是针对一个对象的,那么对这个对象的所有操作会进入监听操作, 这就完全可以代理所有属性,将会带来很大的性能提升和更优的代码。
Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。
响应式是惰性的。
在 Vue.js 2.x 中,对于一个深层属性嵌套的对象,要劫持它内部深层次的变化,就需要递归遍历这个对象,执行 Object.defineProperty 把每一层对象数据都变成响应式的,这无疑会有很大的性能消耗。
在 Vue.js 3.0 中,使用 Proxy API 并不能监听到对象内部深层次的属性变化,因此它的处理方式是在 getter 中去递归响应式,这样的好处是真正访问到的内部属性才会变成响应式,简单的可以说是按需实现响应式,减少性能消耗。
五.什么是虚拟DOM
virtual DOM 虚拟DOM,用普通js对象来描述DOM结构,因为不是真实DOM,所以称之为虚拟DOM。
目的是为了减少回流和重绘,提升性能。
虚拟DOM基本步骤

1.页面初始加载解析DOM树,将DOM解析为JS对象
[最终还需要将其映射成真实DOM,渲染到页面]
2.DOM结构要发生变化,生成一个新的JS对象
3.DIFF比较,比较同级对象
4.将不同之处写入到patch对象中
5.渲染到页面

那么为什么用虚拟dom呢?
Web界面由DOM树(树的意思是数据结构)来构建,当其中一部分发生变化时,其实就是对应某个DOM节点发生了变化,
虚拟DOM就是为了解决浏览器性能问题而被设计出来的。若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。

大白话:就是为了提高代码效率,渲染效果,就如往常的js jquery这些使用dom操作时都是一段复杂的过程,整个过程要遍历属性,标签啦,就是在你的代码上一遍遍寻找大半天还不知道有没有收获,很慢。而且做多件事情的时候只能一件件去做,浪费很多时间,而我们的虚拟dom呢,就是,把你想要做的事情都用小本子记起来(记在本地js对象上)在虚拟dom上更新完成后,再拿到真是的dom上去渲染,然后交给浏览器绘制页面呀。从头到末尾,人家知道每一步要做什么,而且可以同时做,还不会等到你的页面渲染完成后才出发,人家可是早早出发了,

真实DOM和虚拟DOM的区别
那么说回到真实DOM,虚拟DOM和真实DOM又有什么区别呢?
我想,应该会有一下几点:

1.虚拟DOM不会进行排版与重绘操作
2.真实DOM频繁排版与重绘的效率是相当低的
3.虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分,最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗
4.虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部
DIFF算法
vue diff算法

六.Diff算法步骤
用js对象结构(虚拟DOM)表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
把所记录的差异应用到所构建的真正的DOM树上,视图就更新了
Diff 算法: 仅在同级的vnode间做diff,递归地进行同级vnode的diff,不仅仅是同层级对比,在diff算法中也会进行同key值对比和同组件对比
Diff 过程整体策略:深度优先,同层比较

key的作用
在使用v-forj添加唯一的key的作用标识,Diff算法就可以正确的识别此节点,找到正确的位置区更新新的节点,高效的更新虚拟DOM。
八:Vue的生命周期:

上一篇 下一篇

猜你喜欢

热点阅读