1.不一样的变量声明:const和let
let 表示声明一个变量,而const表示声明一个常量,两个都为块级作用域,const声明的变量都会被认为是常量,意思就是他的值被设置完成后就不可以修改了
如果const是一个对象的话,对象的值可以被修改,引用地址无法改变
2.字符串模板 ``
3.箭头函数
箭头函数就是一个函数的简单形式,他不需要function关键字来创建,也可以省略 return 关键字,并且他的this继承了上下文关键字
4.call, apply, bind区别
bind :他不会立即调用,而是返回一个绑定的新的函数
Function.prototype.myBind = function(context){
// 类型判断
if(typeof this !== 'function'){
throw new TypeError('must be a function')
}
let self = this // 这个this是调用bind的函数
let argsArr = [...arguments].slice(1)
return function(){
let bindFuncArg = [...arguments] // 合并两次传的参数
let totalArgs = argsArr.concat(bindFuncArg)
return self.apply(context, totalArgs)
} }
call:立即调用,返回函数执行的结果,this.指向第一个参数,后面可能有多个参数,并且这些参数都是函数的参数
Function.prototype.myCall = function(thisObj = window){
thisObj.fn = this //此处this是指调用myCall的function
// 处理arguments
let arg = [...arguments].slice(1)
let result = thisObj.fn(...arg) // 执行该函数
delete thisObj.fn
return result
}
apply:立即调用,返回函数的执行结果,第一个参数是this的执行,第二的参数是一个数组,数组里面的内容是函数的参数
Function.prototype.myApply = function (context = window, arr){
context.fn = this
let result
if(!arr){
result = context.fn()
} else {
result = context.fn(...arr)
}
delete context.fn
return result
}
5.一个完整的vue生命周期会经历以下钩子函数
beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。
created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。
beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。
mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。
beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。
updated发生在更新完成之后,当前阶段组件Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。
beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。
destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁
6.Vuex的5个核心属性是什么?
- state vuex使用单一状态树,用一个对象就包含来全部的应用层级状态
- getters相当于vue中的computed计算属性
- mutations更改vuex中state的状态的唯一方法就是提交mutation!
- actionsaction提交的是mutation,而不是直接变更状态,action可以包含任意异步操作
- modules模块化 。
7.怎么在组件中批量使用Vuex的state状态?
使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中
8.那你能讲一讲MVVM吗?
MVVM是Model-View-ViewModel缩写,也就是把MVC中的Controller演变成ViewModel。Model层代表数据模型,View代表UI组件,ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。
9.nextTick知道吗,实现原理是什么
我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
10.再说一下Computed和Watch
Computed本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。 适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。
Watch没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销哦。
11.说一下v-model的原理?如何实现v-model
v-model本质就是一个语法糖,可以看成是value + input方法的语法糖。 可以通过model属性的prop和event属性来进行自定义。原生的v-model,会根据标签的不同生成不同的事件和属性
12.组件通信有哪些方式
父->子props,子->父 $on、$emit
兄弟组件通信
Event Bus` 实现跨组件通信 `Vue.prototype.$bus = new Vue
跨级组件通信
Vuex
13.生命周期调用顺序
组件的调用顺序都是先父后子,渲染完成的顺序是先子后父。
组件的销毁操作是先父后子,销毁完成的顺序是先子后父。
加载渲染过程
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted
子组件更新过程
父beforeUpdate->子beforeUpdate->子updated->父updated
父组件更新过程
父 beforeUpdate -> 父 updated
销毁过程
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
14.keep-alive了解吗
keep-alive可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
15.再说一下虚拟Dom以及key属性的作用
由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟Dom的产生原因。
Vue2的Virtual DOM借鉴了开源库snabbdom的实现。
Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。(也就是源码中的VNode类,它定义在src/core/vdom/vnode.js中。)
VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段。
「key的作用是尽可能的复用 DOM 元素。」
新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。
需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识
16.Event Loop
Event Loop任务队列和事件循环,
执行顺序:执行当前栈->执行微任务->宏任务队列中的第一个推到栈中执行,(如果遇到微任务,则把微任务放到微任务队列中)-->执行微任务-->宏任务队列中的第二项推到栈中执行,安装此步骤依次循环
事件循环可以简单的描述为以下四个步骤:
- 函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,接着执行同步任务,直到Stack为空;
- 此期间WebAPIs完成这个事件,把回调函数放入队列中等待执行(微任务放到微任务队列,宏任务放到宏任务队列)
- 执行栈为空时,Event Loop把微任务队列执行清空;
- 微任务队列清空后,进入宏任务队列,取队列的第一项任务放入Stack(栈)中执行,回到第1步。
17.js模拟new操作符的实现
创建一个空的简单JavaScript对象(即{});
链接该对象(即设置该对象的构造函数)到另一个对象 ;
将步骤1新创建的对象作为this的上下文 ;
如果该函数没有返回对象,则返回this。
function objectFactory(){
var obj = {};
//取得该方法的第一个参数(并删除第一个参数),该参数是构造函数
var Constructor = [].shift.apply(arguments);
//将新对象的内部属性__proto__指向构造函数的原型,这样新对象就可以访问原型中的属性和方法
obj.__proto__ = Constructor.prototype;
//取得构造函数的返回值
var ret = Constructor.apply(obj, arguments);
//如果返回值是一个对象就返回该对象,否则返回构造函数的一个实例对象
return typeof ret === "object" ? ret : obj;
}
18.简单介绍一下 V8 引擎的垃圾回收机制
v8 的垃圾回收机制基于分代回收机制,这个机制又基于世代假说,这个假说有两个特点,一是新生的对象容易早死,另一个是不死的对象会活得更久。基于这个假说,v8 引擎将内存分为了新生代和老生代。
新创建的对象或者只经历过一次的垃圾回收的对象被称为新生代。经历过多次垃圾回收的对象被称为老生代。
新生代被分为 From 和 To 两个空间,To 一般是闲置的。当 From 空间满了的时候会执行 Scavenge 算法进行垃圾回收。当我们执行垃圾回收算法的时候应用逻辑将会停止,等垃圾回收结束后再继续执行。这个算法分为三步:
- 首先检查 From 空间的存活对象,如果对象存活则判断对象是否满足晋升到老生代的条件,如果满足条件则晋升到老生代。如果不满足条件则移动 To 空间。
- 如果对象不存活,则释放对象的空间。
- 最后将 From 空间和 To 空间角色进行交换。
新生代对象晋升到老生代有两个条件:
(1)第一个是判断是对象否已经经过一次 Scavenge 回收。若经历过,则将对象从 From 空间复制到老生代中;若没有经历,则复制到 To 空间。
(2)第二个是 To 空间的内存使用占比是否超过限制。当对象从 From 空间复制到 To 空间时,若 To 空间使用超过 25%,则对象直接晋升到老生代中。设置 25% 的原因主要是因为算法结束后,两个空间结束后会交换位置,如果 To 空间的内存太小,会影响后续的内存分配。
老生代采用了标记清除法和标记压缩法。标记清除法首先会对内存中存活的对象进行标记,标记结束后清除掉那些没有标记的对象。由于标记清除后会造成很多的内存碎片,不便于后面的内存分配。所以了解决内存碎片的问题引入了标记压缩法。
由于在进行垃圾回收的时候会暂停应用的逻辑,对于新生代方法由于内存小,每次停顿的时间不会太长,但对于老生代来说每次垃圾回收的时间长,停顿会造成很大的影响。 为了解决这个问题 V8 引入了增量标记的方法,将一次停顿进行的过程分为了多步,每次执行完一小步就让运行逻辑执行一会,就这样交替运行。
19.哪些操作会造成内存泄漏?
1.意外的全局变量
2.被遗忘的计时器或回调函数
3.脱离 DOM 的引用
4.闭包
第一种情况是我们由于使用未声明的变量,而意外的创建了一个全局变量,而使这个变量一直留在内存中无法被回收。
第二种情况是我们设置了setInterval定时器,而忘记取消它,如果循环函数有对外部变量的引用的话,那么这个变量会被一直留在内存中,而无法被回收。
第三种情况是我们获取一个DOM元素的引用,而后面这个元素被删除,由于我们一直保留了对这个元素的引用,所以它也无法被回收。
第四种情况是不合理的使用闭包,从而导致某些变量一直被留在内存当中。
20.React 组件的生命周期方法
componentWillMount**()** – 在渲染之前执行,在客户端和服务器端都会执行。
componentDidMount**()** – 仅在第一次渲染后在客户端执行。
componentWillReceiveProps**()** – 当从父类接收到 props 并且在调用另一个渲染器之前调用。
shouldComponentUpdate**()** – 根据特定条件返回 true 或 false。如果你希望更新组件,请返回true 否则返回 false。默认情况下,它返回 false。
componentWillUpdate**()** – 在 DOM 中进行渲染之前调用。
componentDidUpdate**()** – 在渲染发生后立即调用。
componentWillUnmount**()** – 从 DOM 卸载组件后调用。用于
21.防抖和节流方法
防抖:指的是触发时间n秒后才会执行函数,如果在n秒内再次触发这个事件,他会重新计算执行时间
/*
func:要进行防抖处理的函数
delay:要进行延时的时间
immediate:是否使用立即执行 true立即执行 false非立即执行
*/
function debounce(func,delay,immediate){
let timeout; //定时器
return function(arguments){
// 判断定时器是否存在,存在的话进行清除,重新进行定时器计数
if(timeout) clearTimeout(timeout);
// 判断是立即执行的防抖还是非立即执行的防抖
if(immediate){//立即执行
const flag = !timeout;//此处是取反操作
timeout = setTimeout(()=>{
timeout = null;
},delay);
// 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果。
if(flag) func.call(this,arguments);
}else{//非立即执行
timeout = setTimeout(()=>{
func.call(this,arguments);
},delay)
}
}
}
节流:指的是触发时间但是在n秒内只执行一次函数,再次期间不管你点击多少次他只执行一次事件
function throttle(func,delay){
let prev = 0;//上一次记录时间
return function(arguments){
let now = Date.now();//当前时间戳
if(now - prev > delay){//当前时间 - 上次时间 > 延时时间
func.call(this,arguments);//执行函数 发送请求
prev = now;//重置记录时间
}
}
}
22.原型
构造函数是一种特殊的方法,主要用来在创建对象时初始化对象。每个构造函数都有prototype(原型)(箭头函数以及Function.prototype.bind()没有)属性, 这个prototype(原型)属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的 属性和方法,即这个原型对象是用来给实例对象共享属性和方法的。每个实例对象的__proto__都指向这个 构造函数/类的prototype属性。
23.原型链
这是当我们实例化PersonB得到实例化对象,访问实例化对象的属性时会触发get方法,它会先在自身属性上查 找,如果没有这个属性,就会去__proto__中查找,一层层向上直到查找到顶层对象Object,这个查找的过程 就是原型链来。
24.陈述输入URL回车后的过程
- 读取缓存: 搜索自身的 DNS 缓存。(如果 DNS 缓存中找到IP 地址就跳过了接下来查找 IP 地址步骤,直接访问该 IP 地址。)
- DNS 解析:将域名解析成 IP 地址
- TCP 连接:TCP 三次握手,简易描述三次握手 客户端:服务端你在么? 服务端:客户端我在,你要连接我么? 客户端:是的服务端,我要链接。 连接打通,可以开始请求来
- 发送 HTTP 请求
- 服务器处理请求并返回 HTTP 报文
- 浏览器解析渲染页面
- 断开连接:TCP 四次挥手 关于第六步浏览器解析渲染页面又可以聊聊如果返回的是html页面 根据 HTML 解析出 DOM 树 根据 CSS 解析生成 CSS 规则树 结合 DOM 树和 CSS 规则树,生成渲染树 根据渲染树计算每一个节点的信息 根据计算好的信息绘制页面