1、this指向问题
1.以函数的形式(包括普通函数、定时器函数、立即执行函数)调用时,this 的指向永远都是 window。比如fun();相当于window.fun();
2.以方法的形式调用时,this 指向调用方法的那个对象
3.以构造函数的形式调用时,this 指向实例对象
4.以事件绑定函数的形式调用时,this 指向绑定事件的对象
5.使用 call 和 apply 调用时,this 指向指定的那个对象
6.箭头函数没有自己的this,它沿作用域链逐层往外找
2、防抖和节流
一、防抖
不管用户如何频繁的进行操作,都只会去执行最后的一次操作
我们可以把防抖比喻成:一个公交车司机和乘客以及时间的关系
比如公交车司机在客运站等待乘客的到来,当第一个乘客来了之后会告诉乘客车10分钟后才发车,在此期间会继续等待其他乘客的到来,当第二个乘客到来之后,会重新计时,变成新的10分钟,以此类推,在下一次10分钟内如果还有乘客到来,继续重新计时为10分钟,否则就发车。
//简易防抖
//客车十分钟发一次车,十分钟内还有人上车就重新计时,直到下一个十分钟没有人上车就发车 发车可以看成执行业务代码
const inp = document.querySelector("input")
function fun(){
console.log(11111);
}
inp.oninput = debounce(fun,2000)
function debounce(fun,time){
let timer = null
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(()=>{
fun()
},time)}
}
二、节流
就是为了防止用户在某段时间内频繁的触发多次请求,而只会去执行第一次请求,例如滚动事件
我们可以把节流比喻成:一个公交车司机和乘客以及时间的关系
比如公交车司机在客运站等待乘客的到来,当第一个乘客来了之后会告诉乘客车10分钟后才发车,在此期间会继续等待其他乘客的到来,当10分钟到了之后,无论还有没有乘客,司机都会发动汽车离开,这就是节约资源(节流)。
//简易节流
//客车每十分钟发一次车,十分钟后不管有没有人都发车 发车可以看成执行业务代码
function fn(){
console.log(1111);
}
document.onmousemove = throttle(fn,3000)
function throttle(fn,time){
let firstTime = 0
return function(){
let nowTime = +new Date()
if(nowTime - firstTime < time)return
firstTime = nowTime
fn()
}
}
3、0.1+0.2为什么不等于0.3?如何实现等于0.3?
计算机中用二进制来存储小数,大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。
如上所述:0.1和0.2在转换成二进制后会无限循环,由于标准位数的限制后面多余的位数会被截掉,此时就已经出现了精度的损失,相加后因浮点数小数位的限制而截断的二进制数字在转换为十进制就会变成 0.30000000000000004。
对于这个问题,一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2^(-52),在ES6中,为我们提供了Number.EPSILON属性,而它的值就是2^(-52),只要我们判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3
4、登录实现流程
1、第一次登录的时候,前端调后端登录的接口发送用户名和密码
2、后端收到请求,校验用户名和密码,校验成功就给前端返回一个token
3、前端拿到token并保存在localStorage/cookie和vuex中,并跳转路由页面
4、前端每次跳转路由都要判断localStorage/cookie里有没有token,没有就跳转到登录页面,有就跳转相应路由页面
5、每次调用后端接口都要在请求头带token,后端判断请求头有无token,有token并校验成功就返回数据,校验失败或请求头没有token就返回401状态码
6、如果前端拿到状态码为401,就清除token信息并跳转登录页面
5、原生javascript获取路由参数
通过window.location的search属性获取浏览器路径?及后面的参数
function getParam(key) {
let search = window.location.search;
let value = undefined;
if(!search||search.indexOf("?")<0){
return value;
}
//生成参数数组
search = search.split("?");
search.map(item=>{
if(item.split("&")&&item.split("&").length>0){
item.split("&").map(param=>{
let paramKey = param.split("=");
if(paramKey[0]==key){
value = paramKey[1];
}
})
}
})
return value;
}
6、CSS3新特性、H5新标签、ES6新特性
CSS3新特性:
1、新增属性和伪类选择器
2、边框圆角、边框图像、盒子阴影
3、CSS3背景、css3渐变、css3过渡、css3变换、css3动画
4、媒体查询
H5新标签:
header、nav、aside、article、footer、audio、radio、svg、canvas
表单元素 input 的 type 属性扩充:
date(输入日期);
email(输入邮件);
url(输入url地址);
search(呈现搜索常规的文本域);
range(输入一定范围内的数值);
month(输入月份);
color(颜色);
number(输入数值);
以及表单元素 input 通过属性进行表单验证:required(必填项)、pattern(验证表单输入)。
ES6新特性:
1、新增基本数据类型symbol
2、let和const
3、新增数组和对象方法
4、对象的简写
5、promise和proxy
6、解构赋值
7、模块化
8、箭头函数
9、函数参数默认值
10、class类
7、常见http状态码
1xx:信息类,接受的请求正在处理,信息类状态码
2xx:成功,这一类型的状态码,代表请求已成功被服务器接收、理解、并接受;
3xx:重定向
301,永久移动,表示本网页已经永久性的移动到一个新的地址,在客户端自动将请求地址改为服务器返回的新地址,301永久重定向会被浏览器缓存
302,临时重定向,表示网页暂时性的转移到一的新的地址,客户端在以后可以继续向本地址发起请求。
303,表示必须临时重定向,并且必须使用GET方式请求。
304,重定向至浏览器本身,当浏览器多次发起同一请求,且内容未更改时,使用浏览器缓存,这样可以减少网络开销。
4xx:客户端错误
400(错误请求)服务器不理解请求的语法
401表示发送的请求需要有通过HTTP认证的认证信息
403(禁止)服务器拒绝请求
404(未找到)服务器找不到请求网页
5xx:服务器错误
500,(服务器内部错误)服务器遇到错误,无法完成请求
503,表示服务器处于停机维护或超负载,无法处理请求
8、微信小程序实现用户登录流程
1.调用 wx.login() 获取 临时登录凭证code ,并回传到服务器;
2.当后端拿到code之后,将code和AppID+AppSecret对接微信接口服务换取session_key和openid和后端生成的token通过code数据交互接口传回前端 ;
3.前端缓存自定义状态到storage,wx.request()携带自定义登录状态请求数据;
4.服务器通过自定义登录状态查询openid和session_key,前端拿到session_key、openid和token之后将session_key、openid和token存入缓存,在微信小程序后续的接口请求都带上token进行请求
9、深拷贝和浅拷贝
浅拷贝只拷贝第一层,相当于有个房间然后让你住进去,然后你就能用房间的所有东西,但是房间的东西改变会影响你的使用,深拷贝是拷贝对象每一层的基本数据类型,相当于把这个房间的所有东西拿走给自己用,当然自己拿走不会影响到原房间,只是打个比方,原房间的东西不管发生什么改变都不会影响你的使用。
/*浅拷贝*/
//浅拷贝只拷贝一层,属性值是基本数据类型直接拷贝,对象的复杂数据类型就是拷贝地址
let studentInfo = {
name:"张三",
age:18,
address:{
addressInfo1:"江西",
addressInfo2:"深圳"
}
}
//1.遍历原对象把每一个属性赋值给新对象
let personInfo = {}
for(let key in studentInfo){
personInfo[key] = studentInfo[key]
}
studentInfo.address.addressInfo1 = "广东"
studentInfo.name = "李四"
console.log(personInfo,studentInfo);
//2.扩展运算符
// let personInfo = {}
// personInfo = {...studentInfo}
// studentInfo.name = "李四"
// studentInfo.address.addressInfo1 = "重庆"
//3.Object.assign
// let personInfo = {}
// Object.assign(personInfo,studentInfo)
// studentInfo.address.addressInfo1 = "广东"
// studentInfo.name = "李四"
// console.log(personInfo,studentInfo);
/*深拷贝*/
//通过递归拷贝对象每层的基本数据类型
let studentInfo = {
name:"张三",
age:18,
gender:"男",
address:{
addressInfo1:"广东",
addressInfo2:"深圳"
}
}
function deepCopy(oldData,newData){
//遍历原对象,如果属性值是基本数据类型就直接赋给新对象,如果是对象就让新对象的那个属性创建一个空对 象,然后回调函数存储数据,如果是数组,则是创建
//一个空数组再回调函数
for(let key in oldData){
//如果原对象的属性值是对象
if(Object.prototype.toString.call(oldData[key]) == "object Object"){
//新对象的那个属性值创建一个空对象
newData[key] = {}
//再调自己这个函数
deepCopy(oldData[key],newData[key])
//如果属性值是数组
}else if(Object.prototype.toString.call(oldData[key]) == "object Array"){
//新对象的那个属性值创建一个空数组
newData[key] = []
//调用自己这个函数
deepCopy(oldData[key],newData[key])
}else{
//基本数据类型就直接赋值新对象
newData[key] = oldData[key]
}
}
}
let personInfo = {}
deepCopy(studentInfo,personInfo)
studentInfo.name = "李四"
studentInfo.address.addressInfo1 = "江西"
console.log(studentInfo,personInfo);
10、常用数组和字符串方法
数组:
增:push/unshift/splice/concat(不改变原数组)
删:pop/shift/splice/slice(不改变原数组)
改:splice
查:indexOf/includes/find
排序:sort/reverse
转换:join
迭代(不改变原数组):forEach/filter/map/some/every
字符串:
增:concat
删:slice/substr/substring
改:trim/trimLeft/trimRight/repeat/padStart/padEnd/toLowerCase/toUpperCase
查:charAt/indexOf/includes/startWith/endWith
转换:split
匹配:match/search/replace
11、Vue的指令有哪些
v-text/v-html(在标签内容插入指定文本;v-html能解析html标签,而v-text不能);
v-show/v-if/v-else-if/v-else(条件渲染;v-show通过display:none和display:block切换显示隐藏,v-if通过创建和删除节点显示隐藏);
v-bind(简写为:;给标签绑定动态属性);
v-model(在表单输入元素或组件上创建双向绑定);
v-on(简写为@;绑定事件符);
v-once(只会执行一次渲染,当数据发生改变时,不会再变化);
v-for(基于原始数据多次渲染元素或模板块);
v-slot(用于声明具名插槽或需要接收 props 的作用域插槽)
12、Vue2和Vue3的响应式原理
1.Vue2的响应式原理:
通过Object.defineProperty()对数据进行劫持,调用set和get方法操作数据后返回,当读取对象中属性时会调用get方法,修改调用set方法,缺点:无法观测到删除数据和新增数据的变化,解决方案:调用实例方法this.$set()或者this.$forceUpdate()强制更新dom
2.vue3的响应式原理:
通过Proxy代理拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
通过Reflect(反射): 对源对象的属性进行操作
Reflect其中某些方法与Object相同,如defineProperty()等,Reflect让Object操作都变成函数行为
13、Vue修改了数据但是dom没有更新
由于Vue 在更新 DOM 时是异步执行的,只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环中,Vue 刷新队列并执行实际 (已去重的) 工作。
解决办法:使用Vue.nextTick()在DOM更新完成后被调用
14、纯css实现tab栏选项卡切换
根据target选择器的特性锚点链接到对应的div,再根据z-index的属性,改变div的层级关系,从而实现tab的切换效果
15、微信开发中AppId、OpenId和UnionID
AppId:AppId用于标识小程序或者公众号的唯一性,一个AppId唯一对应一个小程序或者公众号。尽管一个AppId可以开发多个小程序,但是最终提交审核和上线的只能是一个小程序;
OpenId:OpenId 是用户唯一标识,微信用户在某个小程序或者公众号的唯一标识,用于获取用户信息。通过应用appid+用户微信号加密,产生的OpenId;
UnionId:UnionId是标识用户在一个开放平台中的唯一性的,同一个用户在不同的应用(小程序或者公众号)。UnionID是相同的,可以用来用户量去重。
16、git常用指令
加*为开发常用指令
*git init //初始化,生成.git文件(若该文件隐藏,则使用ls -ah)
git add test.txt //添加文件
*git add . //添加暂存区所有文件
*git commit -m "wrote a test file" //提交
*git log //查看提交历史记录,从最近到最远,可以看到3次
git log --pretty=oneline //加参,简洁查看
*git reflog //查看每一次修改历史
*git status //查看工作区中文件当前状态
*git reset --hard HEAD^(HEAD~100)(commit id) //回退版本
git checkout -- test.txt //丢弃工作区的修改,即撤销修改
git reset HEAD test.txt //丢弃暂存区的修改(若已提交,则回退)
*git remote add origin git@github.com:Daisy/AKgit.git //关联
git push -u origin master //将本地内容推送到远程仓库(第一次)
git push origin master //将本地内容推送到远程仓库(之后)
git remote -v //查看远程仓库信息
git remote rm origin //删除远程仓库(解绑)
*git clone git@github.com: Daisy/AKgit.git //克隆远程仓库
git remote //查看远程库的信息
git remote -v //查看远程库的详细信息
*git checkout -b dev //创建并切换到分支dev
//创建并切换到分支dev,同上
git branch dev //创建
*git checkout dev //切换分支
//新版本
git switch -c dev //创建并切换到分支dev
git switch master //直接切换分支
*git branch //查看当前分支
git merge dev (--no-ff)(-m)//合并,把dev分支的工作成果合并到master分支上
git branch -d dev //删除dev分支
git stash //将现场储藏起来
*git stash list //查看储存的工作现场
//恢复和删除
git stash apply //恢复被隐藏的文件
git stash drop //丢弃stash@{$num}存储,从列表中删除这个存储
*git stash pop //命令恢复之前缓存的工作目录,将缓存堆栈中的对应stash删除
git cherry-pick 4c805e2 //复制修改
*git push origin master(dev) //推送分支
git checkout -b dev origin/dev //创建远程origin的dev分支到本地
*git pull //抓取分支(解决冲突)
*git branch --set-upstream-to=origin/dev dev//指定本地与远程dev的链接
git rebase //把本地未push的分叉提交历史整理成直线
17、数组去重
let arr = [1, 2, 3, 1, 2, 3, 4, 3, 2, 4, 1, 4];
//方案一 利用filter过滤
// var arr1 = arr.filter(function(item,index,arr){
// //过滤用下标找到的第一个元素以外重复的元素
// return arr.indexOf(item,0) == index;
// })
// console.log(arr1);
//方案二 利用对象的key具有唯一性
// let obj ={};
// let arr1 =[]
// arr.forEach(function(item,index,arr){
// obj[item]= "";
// })
// for(var key in obj){
// arr1.push (+key)
// }
// console.log(arr1);
//方案三 遍历数组,将每一个元素与后面的元素比较,如果有相同的则去掉后一个的
// for(var i = 0;i < arr.length-1;i++){
// for(var j = i+1;j <arr.length; j++){
// if(arr[i] == arr[j]){
// arr.splice(j,1)
// //删除元素出现数组塌陷
// j--
// }
// }
// }
// console.log(arr);
//方案四 创建一个空数组,空数组没有的就添加,有的则不添加
// var arr1 = [];
// for(var i = 0;i <arr.length;i++){
// if(arr1.indexOf(arr[i]) == -1){
// arr1.push(arr[i])
// }
// }
// console.log(arr1);
//方案五 先将数组进行排序,然后从第一个开始和后面的比较,如果相同就去掉前一个
// var arr1 = arr.sort(function(a,b){return a - b})
// for(let i = 0;i < arr1.length;i++){
// if(arr1[i] == arr1[i+1]){
// arr1.splice(i,1)
// //删除元素造成数组塌陷
// i--
// }
// }
// console.log(arr1);
//方案六 利用es6的Set集合元素唯一性
// const s = new Set(arr)
// const newarr = [...s]
// console.log(newarr);
//方案七 利用es6的Map集合的key唯一性
let m = new Map()
let newarr = []
arr.filter(item => {
//如果map集合没有就设置添加
if (!m.has(item)) {
m.set(item, true)
newarr.push(item)
}
})
console.log(newarr);
18、如何理解MVVM?
1.解决了前端开发的三个痛点问题:
(1、 开发者在代码中大量调用相同的 DOM API,处理繁琐 ,操作冗余,使得代码难以维护。
(2、大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
(3、 当 Model 频繁发生变化,开发者需要主动更新到View ;当用户的操作导致 Model 发生变化,开发者同样需要将变化的数据同步到Model 中,这样的工作不仅繁琐,而且很难维护复杂多变的数据状态。
2.什么是MVVM:
MVVM 由 Model、View、ViewModel 三部分构成,Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象。
在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。
ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
3.vue.js的细节:
当执行 new Vue() 时,Vue 就进入了初始化阶段,一方面Vue 会遍历 data 选项中的属性,并用Object.defineProperty 将它们转为 getter/setter,实现数据变化监听功能;另一方面,Vue 的指令编译器Compile 对元素节点的指令进行扫描和解析,初始化视图,并订阅 Watcher 来更新视图, 此时Wather 会将自己添加到消息订阅器中(Dep),初始化完毕。
当数据发生变化时,Observer 中的 setter 方法被触发,setter 会立即调用Dep.notify(),Dep 开始遍历所有的订阅者,并调用订阅者的 update 方法,订阅者收到通知后对视图进行相应的更新。
19、为什么data是一个函数而不是对象?
1、vue根实例的data可以是函数也可以是对象,不会造成数据的污染;
2、组件实例的data必须写成函数返回一个对象,写成对象的话,组件之间会共用一个data,也就是组件之间会产生数据污染;采用函数返回对象的形式,initData时每次都会返回一个全新的data对象。
20、vue后台系统的权限管理?
用户点击登录后调用vuex定义的获取用户权限路由信息的异步方法,同时把之前创建的所有路由表信息和权限路由进行一一比对,把匹配到的路由调用router.addRoute()动态创建,然后跳转到内部页面,这时路由还没有创建,所以匹配不到路由,所以我们需要把调用的异步方法返回成一个promise,在动态路由创建完成后再做跳转,还有一个问题就是浏览器刷新以后动态路由会清空,所以我们根据vuex刷新后会重置数据的特性在vuex定义了一个判断用户刷新的标识,并且在路由前置守卫条件判断后调用创建动态路由的方法。
21、v-show和v-if有什么区别?
v-show是通过display:none和display:block来实现显示隐藏的,而v-if是通过创建和销毁节点实现的,如果需要频繁切换元素的显示隐藏使用v-show,v-if可以做更多的条件判断
22、什么是BFC?有什么作用?
BFC就是块级格式化上下文,它是独立于页面的一个渲染区域,里面的子元素不会对外部元素造成影响,形成BFC的原因包括但不限于:float:left/right,overflow:hidden/auto/scroll,position:fixed/absolute,display:table/inline-table/grid/inlie-grid/flex/inline-flex/inline-block
BFC的作用:
1、防止margin重叠造成塌陷
2、清除内部浮动
3、自适应多栏布局