一、js基础
1、简述同步和异步的区别
同步是阻塞模式,异步是非阻塞模式。
同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
2、JS的执行机制
先执行执行栈中的同步任务
异步任务(回调函数)放入任务队列中
一旦执行栈中的所有同步任务执行完毕,系统会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行,执行完之后,主线程再查询任务队列有没有任务,有就取出来放到执行栈中执行
由于主线程不断重复的获得任务、执行任务、再获得任务、再执行,所以这种机制被称为事件循环(eventloop)
3、JS为什么是单线程的
最初设计JS是用来在浏览器验证表单操控DOM元素的是一门脚本语言,如果js是多线程的,那么两个线程同时对一个DOM元素进行了相互冲突的操作,那么浏览器的解析器是无法执行的。
4、防抖和节流的什么区别和使用场景
防抖:
高频率触发的事件,在指定的单位时间内,只响应最后一次,如果在指定的时间在触发,则重新计算时间(后面触发的事件执行,替代了前面的事件)
节流:
高频率触发的事件,在指定的单位时间内,只响应第一次(前面触发的执行前,忽略后面的事件)
防抖和节流的使用场景
防抖(debounce)
1.search搜索联想,用户在不断输入值时,用防抖来节约请求资源。
2.window触发resize的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
节流(throttle)
1.鼠标不断点击触发,mousedown(单位时间内只触发一次)
2.监听滚动事件,比如是否滑到底部自动加载更多,用throttle来判断
5、var、let、const之间的区别
var声明变量可以重复声明,而let不可以重复声明
var是不受限于块级的,而let是受限于块级
var会与window相映射(会挂一个属性),而let不与window相映射
var可以在声明的上面访问变量,而let有暂存死区,在声明的上面访问变量会报错
const声明之后必须赋值,否则会报错
const定义不可变的量,改变了就会报错
const和let一样不会与window相映射、支持块级作用域、在声明的上面访问变量会报错
6、JS的基本数据类型
Undefined、Null、Boolean、Number、String、Object 新增:Symbol
7、setimeout在1s后一定会执行吗
不一定。只有在同步任务执行完毕之后,任务对列中的异步任务才能进入主线程开始执行
8、策略模式 (很多if else怎么优化)
if/else可以说在项目中遇到频率是最高,通常可以这两种策略优化
(1)、排非策略
if (!user || !password) return throw('用户名和密码不能为空!')
// 逻辑处理
(2)、三元运算符
let str = true ? '是' : '否'
(3)、switch
(4)、key-value
let enums = {
'A': handleA,
'B': handleB,
'C': handleC,
'D': handleD,
'E': handleE
}
function action(val){
let handleType = enums[val]
handleType()
}
(5)、map
let enums = new Map([
['A', handleA],
['B', handleB],
['C', handleC],
['D', handleD],
['E', handleE]
])
function action(val){
let handleType = enums(val)
handleType()
}
9、数组的原生方法
push(), pop(), shift(), unshift(), splice(), sort(), reverse(), concat()和slice()
10、字符串的原生方法
//定位:
charAt() //返回在指定位置的字符;
charCodeAt() //返回在指定位置的字符的Unicode编码;
indexOf() //返回某个制动的字符值在字符串中首次出现的位置;
lastIndexOf() //从后向前搜索字符串;
//合并/截取:
concat() //合并字符串
slice() //提取字符串片段
substr() //此方法和slice()提取片段效果是一样的,参数也一样
substring() //此方法和slice()提取片段效果是一样的,参数也一样
//slice、substr、substring三者的用途是一样的,只是截取开闭区间的问题(eg: slice(2, 5))
match() //查找匹配的字符串;
replace() //查找到匹配的字符串,并替换掉;
//字符串和数组的相互转换方法:
split() //把字符串转换成数组;
join() //把数组转换成字符串;
11、axios前置操拦截器和后置拦截器
//1 使用axios前置拦截器,让所有的请求都携带token
axios.interceptors.request.use(config=>{
//携带token
let uToken = localStorage.getItem("token");
if(uToken){
//我就在请求头里面添加一个头信息叫做U-TOKEN ==》jsessionid(token) 后台通过token作为key值可以在redis中获得loginUser的信息
config.headers['U-TOKEN']=uToken;
}
return config;
},error => {
Promise.reject(error);
});
// 使用:在axios发送请求后,得到一个res对象,进行回调函数前的拦截器
//2 使用axios后置拦截器,处理没有登录请求
axios.interceptors.response.use(res=>{
let {success, result} = res.data;
//用户没有登录
if(!success && result==="noUser"){
//跳转登录界面
location.href = "/login.html";
return;
}
//后台redis用户已经过期了
if(!success && result==="expireUser"){
//移除localStorage
localStorage.removeItem("token");
localStorage.removeItem("loginUser");
//跳转登录界面
location.href = "/login.html";
return;
}
return res;
},error => {
Promise.reject(error);
})
12、axios取消请求
运用cancalToken
参考:https://www.jianshu.com/p/761114cbba68
13、浅拷贝与深拷贝
浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
var a1 = {b: {c: {}};
var a2 = shallowClone(a1); // 浅拷贝方法
a2.b.c === a1.b.c // true 新旧对象还是共享同一块内存
var a3 = deepClone(a3); // 深拷贝方法
a3.b.c === a1.b.c // false 新对象跟原对象不共享内存
浅拷贝的实现方式:
(1)Object.assign()
(2)函数库lodash的_.clone方法
(3)展开运算符...
(4)Array.prototype.concat()
(5)Array.prototype.slice()
深拷贝的实现方式:
(1)JSON.parse(JSON.stringify())
(2)函数库lodash的_.cloneDeep方法
(3).jQuery.extend()方法
(4)手写递归方法
14、箭头函数和普通函数有什么区别?
(1)箭头函数比普通函数更加简洁
如果没有参数,就直接写一个空括号即可
如果只有一个参数,可以省去参数括号
如果有多个参数,用逗号分割
如果函数体的返回值只有一句,可以省略大括号
如果函数体不需要返回值,且只有一句话,可以给这个语句前面加一个void关键字。最常用的就是调用一个函数:
let fn = () => void doesNotReturn()
(2) 箭头函数没有自己的this
箭头函数不会创建自己的this,所以它没有自己的this,它只会在自己作用域的上一层继承this。
所以箭头函数中的this的指向在它在定义时一家确定了,之后不会改变。
(3)箭头函数继承来的this指向永远不会改变
(4) call()、apply()、bind()等方法不能改变箭头函数中的this指向
(5) 箭头函数不能作为构造函数使用
(6) 箭头函数没有自己的arguments
(7) 箭头函数没有prototype
(8) 箭头函数不能用作Generator函数,不能使用yeild关键字
15、什么是闭包,闭包的作用是什么?
当一个内部函数被调用,就会形成闭包,闭包就是能够读取其他函数内部变量的函数。
闭包作用:
局部变量无法共享和长久的保存,而全局变量可能造成变量污染,所以我们希望有一种机制既可以长久的保存变量又不会造成全局污染。
16、Promise是什么?
Promise 是异步编程的一种解决方案:从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。promise有三种状态: pending(等待态),fulfiled(成功态),rejected(失败态) ;状态一旦改变,就不会再变。创造promise实例后,它会立即执行。
17、map和foreach有什么区别
foreach()方法会针对每一个元素执行提供得函数,该方法没有返回值,是否会改变原数组取决与数组元素的类型是基本类型还是引用类型
map()方法不会改变原数组的值,返回一个新数组,新数组中的值为原数组调用函数处理之后的值
二、http
- 浏览器缓存有哪些,通常缓存有哪几种
一、http缓存
二、websql
cookie
localstorage
sessionstorage
flash缓存
2、请描述一下cookies,sessionStorage和localStorage的区别
sessionStorage用于本地存储一个会话(session)中的数据,这些数据只有在同一个会话中的页面才能访问并且当会话结束后数据也随之销毁。因此sessionStorage不是一种持久化的本地存储,仅仅是会话级别的存储。而localStorage用于持久化的本地存储,除非主动删除数据,否则数据是永远不会过期的。
web storage和cookie的区别
Web Storage的概念和cookie相似,区别是它是为了更大容量存储设计的。Cookie的大小是受限的,并且每次你请求一个新的页面的时候Cookie都会被发送过去,这样无形中浪费了带宽,另外cookie还需要指定作用域,不可以跨域调用。
除此之外,Web Storage拥有setItem,getItem,removeItem,clear等方法,不像cookie需要前端开发者自己封装setCookie,getCookie。但是Cookie也是不可以或缺的:Cookie的作用是与服务器进行交互,作为HTTP规范的一部分而存在 ,而Web Storage仅仅是为了在本地“存储”数据而生。
3、如何解决跨域问题
跨域的概念:协议、域名、端口都相同才同域,否则都是跨域
解决跨域问题:
1、使用JSONP(json+padding)把数据内填充起来
2、CORS方式(跨域资源共享),在后端上配置可跨域
3、服务器代理,通过服务器的文件能访问第三方资源
三、css
1、行内元素和块级元素
行内元素(display: inline)
宽度和高度是由内容决定,与其他元素共占一行的元素,我们将其叫行内元素,例如:<span> 、 <i> 、 <a>等
块级元素(display: block)
默认宽度由父容器决定,默认高度由内容决定,独占一行并且可以设置宽高的元素,我们将其叫做块级元素,例如:<p> 、<div> 、<ul>等
2、绝对定位和相对定位的区别
position: absolute
绝对定位:是相对于元素最近的已定位的祖先元素
position: relative
相对定位:相对定位是相对于元素在文档中的初始位置
3、flex布局
Flex常见的属性 flex:1代表什么?
flex实际上是flex-grow、flex-shrink和flex-basis三个属性的缩写。
flex-grow: 1
flex-shrink: 1
flex-basis: 0%
4、媒体查询是什么
5、Rem你是怎么做适配的
四、vue
1、Vue生命周期总共有几个阶段?
它可以总共分为8个阶段:
创建前/后, 载入前/后,更新前/后,销毁前/销毁后
beforeCreate 组件实例被创建之初
created 组件实例已经完全创建
beforeMount 组件挂载之前
mounted 组件挂载到实例上去之后
beforeUpdate 组件数据发生变化,更新之前
updated 数据数据更新之后
beforeDestroy 组件实例销毁之前
destroyed 组件实例销毁之后
2、DOM渲染在哪个周期中就已经完成?
created 实例已经创建完成调用
mounted 实例已经挂在完成调用
在created都可以操作dom节点,
一般Ajax都放在created或者mounted中。
3、v-show与v-if区别
v-show是css切换,v-if是完整的销毁和重新创建
使用 频繁切换时用v-show,运行时较少改变时用v-if
v-if=‘false’ v-if是条件渲染,当false的时候不会渲染
4、vue开发中常用的指令有哪些
5、computed和watch有什么区别?
computed:
- computed是计算属性,也就是计算值,它更多用于计算值的场景
- computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
- computed适用于计算比较消耗性能的计算场景
复制代码watch: - 更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
- 无缓存性,页面重新渲染时值不变化也会执行
6、Vuex有5种属性: 分别是 state、getter、mutation、action、module;
state
Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据
mutations
mutations定义的方法动态修改Vuex 的 store 中的状态或数据
getters
类似vue的计算属性,主要用来过滤一些数据
action
actions可以理解为通过将mutations里面处里数据的方法变成可异步的处理数据的方法,简单的说就是异步操作数据。view 层通过 store.dispath 来分发 action
总结
vuex 一般用于中大型 web 单页应用中对应用的状态进行管理,对于一些组件间关系较为简单的小型应用,使用 vuex 的必要性不是很大,因为完全可以用组件 prop 属性或者事件来完成父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据
7、Vue 组件间通信有哪几种方式?
Vue 组件间通信是面试常考的知识点之一,这题有点类似于开放题,你回答出越多方法当然越加分,表明你对 Vue 掌握的越熟练。Vue 组件间通信只要指以下 3 类通信:父子组件通信、隔代组件通信、兄弟组件通信,下面我们分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信。
(1)props / parent /
parent /
emit /
attrs/
attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 ( class 和 style 除外 )。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 ( class 和 style 除外 ),并且可以通过 v-bind="
listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
(5)provide / inject 适用于 隔代组件通信
祖先组件中通过 provider 来提供变量,然后在子孙组件中通过 inject 来注入变量。 provide / inject API 主要解决了跨级组件间的通信问题,不过它的使用场景,主要是子组件获取上级组件的状态,跨级组件间建立了一种主动提供与依赖注入的关系。
(6)Vuex 适用于 父子、隔代、兄弟组件通信
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。
Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。
8、vue-router 有 3 种路由模式:
hash: 使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History Api 的浏览器;
history : 依赖 HTML5 History API 和服务器配置。具体可以查看 HTML5 History 模式;
abstract : 支持所有 JavaScript 运行环境,如 Node.js 服务器端。如果发现没有浏览器的 API,路由会自动强制进入这个模式.
9、history模式会造成的问题
path自定义
需要配置nginx代理刷新重定向 /index.html
location / {
try_files uri/ /index.html;
}
参考:https://v3.router.vuejs.org/zh/guide/essentials/history-mode.html#%E5%90%8E%E7%AB%AF%E9%85%8D%E7%BD%AE%E4%BE%8B%E5%AD%90
10、vue路由钩子函数
路由钩子函数有三种:
1:全局钩子: beforeEach、 afterEach、beforeResolve
2:单个路由里面的钩子: beforeEnter
3:组件路由:beforeRouteEnter、 beforeRouteUpdate、 beforeRouteLeave
11、路由拦截钩子
beforeEach
12、store持久化处理
1、引入vuex-persistedstate组件
安装 npm install vuex-persistedstate --save
引入及配置:在store下的index.js中
//先引入
import createPersistedState from "vuex-persistedstate"
const store = new Vuex.Store({
modules: {
app,
settings,
permission,
user
},
getters,
plugins: [createPersistedState({
storage: window.localStorage,
})]
})
13、使用keep-alive在页面被重新激活时哪个钩子函数不重新加载
created、mounted
14、事件修饰符
(1)、普通修饰符
.stop 阻止事件冒泡
.prevent 阻止默认行为
.capture 事件捕获
.self 自身触发
.once 只触发一次
//self 是阻止自身不执行冒泡触发,不会阻止冒泡继续向外触发;
//stop 是从自身开始阻止冒泡不向外触发。所以self 一般用在父元素上,stop 一般用在子元素上
(2)、按键修饰符
.enter
.esc
.tab
.space
.delete
.up
.left
.down
.right
(3)、鼠标修饰符
<!--点击鼠标左键触发事件:-->
<div @click.left="divEvent">点击鼠标左键触发事件</div>
<!--点击鼠标右键触发事件:并阻止默认右键菜单-->
<div @click.right="divEvent" oncontextmenu="return false">点击鼠标右键触发事件</div>
<!--点击鼠标右键触发事件:并阻止默认右键菜单-->
<div @contextmenu.prevent="divEvent">点击鼠标右键触发事件</div>
<!--点击鼠标滚轮触发事件:并阻止默认右键菜单-->
<div @click.middle="divEvent">点击鼠标滚轮</div>
14、vue父子组件生命周期执行顺序
父beforeCreate-> 父create -> 子beforeCreate-> 子created -> 子mounted -> 父mounted
15、Vuex有哪些基本属性?为什么 Vuex 的 mutation 中不能做异步操作?
有五种,分别是 State、 Getter、Mutation 、Action、 Module
1、state => 基本数据(数据源存放地)
2、getters => 从基本数据派生出来的数据
3、mutations => 提交更改数据的方法,同步
4、actions => 像一个装饰器,包裹mutations,使之可以异步。
5、modules => 模块化Vuex
1、Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样可以方便地跟踪每一个状态的变化,
从而能够实现一些工具帮助更好地了解我们的应用。
2、每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。
如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。
五、微信小程序
微信小程序生命周期
onLoad():页面加载时触发。
onShow():页面显示/切入前台时触发。
onReady():页面初次渲染完成时触发。
onHide():页面隐藏/切入后台时触发。
onUnload():页面卸载时触发。