- 掌握面试常见问题
学习内容: 1. 深拷贝和浅拷贝 2. substr和substring的区别 3. slice和splice的区别 4. js数据类型和数据类型检测 5. 生命周期,父子组件生命周期 6.v-model底层原理 7.观察者模式和订阅者模式 8.重绘与回流 9.router和route的区别 10.原型链 11. 如何实现三角形 12. promise的3种状态 13. let const var区别 14. 数组去重方法 15. 如何实现右边固定,左边自适应 16. watch和计算属性 17. new的过程 18. 事件循环—宏任务和微任务 19. flex:1的理解 20. link和@import有什么区别
学习产出: 1.深拷贝和浅拷贝
如果对象只有一层,就可以用浅拷贝`;
{…obj}或 Object.assign({},obj)
如果对象有多层,就是对象的属性又是一个对象,就需要用深拷贝
JSON.parse(JSON.stringify(obj))
- 如果对象身上有方法,需要用递归
- 如果存在循环引用就会堆栈溢出?
- 解决思路:把处理好的对象存起来,在处理新的对象的时候,会先在这个存的地方找一找有没有处理好,如果有就直接返回就行了
substr和substring的作用是用于字符串截取
substr第一个参数可以选取开始的位置,第二个参数选择从开始的长度
substring第一个参数可以选择开始的位置,第二个参数选取结束的位置
1.substr(a,b) //a:开始位置 b:长度 2.substring(a,b) //a:开始位置 b:结束位置
PS:只有一个参数时,表示只截取到字符串结尾
3.slice和splice的区别slice可以取出数组中的任意值,并且返回一个新的数组
splice可以实现数组的删除、替换、插入
数据类型分为简单数据类型和复杂数据类型,见下图:
其中Symbol表示独一无二的值,作用是避免了属性名的冲突
数据类型检测见下图:
typeof存在一个问题:当值是null 或者数组时打印出来也是 object
instanceof(只能检测复杂数据类型)
- 返回值只能是 true 或者 false
- 相关的构造函数只要在原型链上,就是 true,否则就是 false 可以用于检测是不是数组
- 首先要回答生命周期是什么,生命周期的阶段以及我们平时常用的钩子函数
-
什么是生命周期?
组件实例从创建到销毁的过程就是生命周期 -
vue有四个阶段 创建 挂载 更新 销毁
-
(1)创建 有二个钩子 beforeCreate created 在工作一般用的是created
拿ajax数据
(2) 挂载 有二个钩子 beforeMount mounted 在工作中可以在mounted
里面去操作DOM
(3) 更新阶段 beforeUpdate updated 在工作的时候可以在updated里面
拿到最新的DOM效果
(4) 销毁阶段 beforeDestroy destroyed 我们可以在beforeDestroy中
去清理当前组件中有的定时器和DOM监听的事件 -
如果面试官还需要问其他钩子函数就可以回答缓存组件的钩子函数
(1) keep-alive(缓存组件):用于缓存不活动的组件实例,防止重复渲染DOM,减少加载时间及性能消耗,提高用户体验性。
(2) keep-alive钩子函数
activated 激活
deactivated 失活
- 父子组件的生命周期执行顺序
-
组件的创建和挂载
- 调用顺序是先父后子 挂载完成是先子后父
- 调用顺序是先父后子 挂载完成是先子后父
-
更新阶段
- 调用顺序是先父后子 更新完成是先子后父
- 调用顺序是先父后子 更新完成是先子后父
-
销毁阶段
- 销毁调用先父后子 销毁完成先子后父
- 销毁调用先父后子 销毁完成先子后父
-
一句话总结:
永远是调用的时候先父后子 完成的时候先子后父
-
v-model可以实现双向数据绑定 ,所谓的双向数据就是数据变了,视图会跟着变,反过来也是,一般用于表单控件
数据(data)变化 -> 视图(view)变化同步
视图(view)变化 -> 数据(data)自动同步
v-modle属于一种语法糖,本质是 :value=“message” @input=“onInput”
7.观察者模式和订阅者模式两者区别:观察者模式有两个主体;订阅模式有是三个主体 ,订阅者和发布者是解耦的(不知道彼此存在)
-
观察者模式
两个主体:Dep 被观察者 ; Watcher 观察者
当对象间存在 一对多 关系时,则使用观察者模式(Observer Pattern)。
当一个对象或者数据被修改时,则会自动通知依赖它的对象,并自动更新。
比如,在一个微信群中,群主为Dep,群主一发消息,群里其他人(Watcher)都会关注
-
订阅者模式
三个主体:订阅者 ; 事件通道(调度中心); 发布者
订阅模式中,称为发布者的消息发送者不会将消息直接发送给订阅者,这意味着发布者和订阅者不知道彼此的存在。在发布者和订阅者之间存在第三个组件,称为调度中心或事件通道(event bus),它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。
比如,一个明星(发布者)在微博(调度中心)发了一条帖子,微博会自动通知订阅的粉丝(订阅者),其他人不会通知,明星和粉丝又没有存在直接的联系
-
回流(重排)
重排是指部分或整个渲染树需要重新分析,并且节点的尺⼨需要重新计算。
表现为重新⽣成布局,重新排列元素。
-
重绘
重绘是由于节点的⼏何属性发⽣改变,或由于样式发⽣改变(例如:改变元素背景⾊)。
表现为某些元素的外观被改变。或者重排后, 进行重新绘制!
-
两者的关系
重绘不⼀定会出现重排,重排必定会触发重绘。
每个页面至少需要一次回流+重绘。(初始化渲染)
重排和重绘的代价都很⾼昂,频繁重排重绘, 会破坏⽤户体验、让界面显示变迟缓。
我们需要尽可能避免频繁触发重排和重绘, 尤其是重排
-
$router 是VueRouter的实例对象,它包含所有的路由,用来实现路由之间跳转,以及钩子函数
- beforeEach
- afterEach
-
$route用于获取当前路由相关的信息
- params
- query
- 在 JS 中,每个对象中都有一个 __ proto__ 属性,这个属性指向了当前对象的构造函数的原型;
当访问一个对象的属性或者一个方法的时候,我们先会在对象的本身查找该属性或方法,没有的话会通过__
proto__往上面的person.prototype里面找,找不到的话再往上到Object.prototype里面找,直到找到null,这样就串联形成一个链式结构,也就是原型链。
- 先设置元素的宽高必须为0;
- 之后设置边框样式为实心;
- .三条边的颜色为transparent,即透明的,一条边框为你要的三角形的颜色。
12. promise的3种状态
- promise的三个状态:
- pending(默认)
- fulfilled(成功)
- rejected(失败)
resolve函数被执行时, 会将promise的状态从 pending 改成 fulfilled 成功
reject函数被执行时, 会将promise的状态从pending 改成 rejected 失败
reject函数执行后,promise的状态无法改变
new Promise((resolve, reject) => {
reject()
})
new Promise((resolve, reject) => {
resolve()
})
13. let const var区别
- var 存在变量提升 而 let 与 const 不存在变量提升
- var定义的变量可以声明多次,而let、const定义的变量只能声明一次
- var、let声明的变量可以再次赋值,而const声明的变量不能再次赋值
- var声明的变量没有自身的作用域,而ler、const声明的变量有自身的作用域
- 先用数组方法sort()排序,再用 splice() 去除重复相邻的元素
var arr1 = [1, 6, 4, 5, 7, 1, 4, 5, 6];
console.log(arr1)
arr1.sort(function (a, b) {
return a - b
})
console.log(arr1) //排序后
for (var i = 0; i < arr1.length - 1; i++) {
if (arr1[i] == arr1[i + 1]) {
arr1.splice(i, 1);
i--;
}
}
console.log( arr1) //去重后
- 遍历数组判断是否有相同元素
var arr2 = [1, 2, 3, 1, 9, 6, 4, 5, 7, 1, 4, 5, 6]
var arr22 = []
for (var i = 0; i < arr2.length; i++) {
if (arr22.indexOf(arr2[i]) == -1) {
arr22.push(arr2[i])
}
}
console.log("去重后: ", arr22)
- Set
var arr6 = [1, 2, 3, 1, 9, 6, 4, 5, 7, 1, 4, 5, 6]
var s = new Set(arr6) // 转为 Set 数据类型, 自然去重
console.log(s)
arr6.splice(0, arr6.length) // 将原数组变为空数组
// 方法1-还原成数组
arr6 = Array.from(s) // 将 Set 数据类型还原成数组
console.log("去重后-1: ", arr6)
// 方法2-还原成数组
arr6 = [...s]
console.log("去重后-2: ", arr6)
15. 如何实现右边固定,左边自适应
-
flax布局
首先设置display: flex,让2个盒子一行显示,设置右边的固定宽度px,然后设置左边flex:1,自动填满剩余空间 -
定位+overflow
首先设置右边盒子position:absolute(或者fixed),因为这种定位形式会脱离标准流,所以在设置左边盒子overflow:hidden,就可以实现需求
-
计算属性有缓存,当缓存发生变化时,重新计算并返回一个返回值
-
计算属性有getter和setter,默认为getter,watch(监听属性)不分set和get。
-
计算属性用来同步执行计算数据,watch(监听属性)可以有异步事件。
- 创建一个新对象
- 将构造函数的作用域赋给新对象(因此 this 就指向了这个新对象)
- 执行构造函数中的代码(为这个新对象添加属性)
- 返回新对象
-
事件循环:
事件循环由一个宏任务队列+多个微任务队列组成。
首先,执行第一个宏任务:全局Script脚本。产生的的宏任务和微任务进入各自的队列中。执行完Script后,把当前的微任务队列清空。完成一次事件循环。
接着再取出一个宏任务,同样把在此期间产生的回调入队。再把当前的微任务队列清空。以此往复。
宏任务队列只有一个,而每一个宏任务都有一个自己的微任务队列,每轮循环都是由一个宏任务+多个微任务组成。
① 常见宏任务
- 异步 Ajax 请求
- setTimeout、setInterval
- 文件操作
- 其它宏任务
② 常见微任务
- Promise.then、.catch 和 .finally
- process.nextTick
- 其它微任务
- flex-grow:1
flex-grow是用来增大盒子的,比如,当父盒子的宽度大于子盒子的宽度,父盒子的剩余空间可以利用flex-grow来设置子盒子增大的占比 - flex-shrink:1
flex-shrink用来设置子盒子超过父盒子的宽度后,进行缩小的比例取值 - flex-basis:0%
设置盒子的基准宽度,并且basis和width同时存在会把width干掉
- link属于XHTML标签,除了加载CSS外,还能用于定义RSS,定义rel连接属性等作用;而@import只能用于加载CSS
- 兼容性的区别,import是CSS2.1 提出的,只在IE5以上才能被识别,而link是XHTML标签,无兼容问题。
- 使用dom控制样式时的差别。当使用javascript控制dom去改变样式的时候,只能使用link标签,因为@import不是dom可以控制的。(link可以动态加载样式表)
- link是在页面加载的的同时一起加载的,@import是页面加载完毕后才加载的



