1、倒计时如何实现的?如何解决不同地区、设备造成的时间误差,导致倒计时抢购出现问题?
1、记录登录时间,将登录时间储存在本地缓存中。
这样用户即便改变设备时间也对倒计时程序无影响
2、在数据事先存好需要倒计时的时间。
这样倒计时的时间一致,不会出现误差,
通过数据库的抢购时间,与用户登录的时间进行倒计时
2、登录注册功能的登录验证流程,token
1、安装并引用:jsonwebtoken、express-jwt
在登录路由中写
// 用于生成 token的模块
const jwt = require('jsonwebtoken')
// 用于解析 token的
const { expressjwt:expressJWT } = require('express-jwt')
2、加密密钥(登录路由)
// 自定义即可
// 加密密钥
const secretKey = 'zls991013@^_^'
3、在登录时,路由中生成一个token(登录路由)
// 生成 token
// username: 为登录时的用户名
// secretKey:自定义密钥
// { expiresIn: '12h' }:自定义token 过期时间
const tokenStr = jwt.sign({ username: username }, secretKey, { expiresIn: '12h' })
// 响应token给前端,带上Bearer头
res.send({
token:'Bearer '+tokenStr
})
4、登录成功,前端接收token,存储在本地缓存
localStorage.setItem('token',response.data.token)
5、在服务器中注册解析中间件(先引入express-jwt)
切记:放在路由中间件前面,静态资源前
// 注册中间件 expressJWT({ secret: secretKey})就是用来解析 token的中间件 .unless({ path: [/^\/login/] }) 用来指定哪些接口不需要访问权限
const secretKey = 'zls991013@^_^'
app.use(expressJWT({ secret: secretKey, algorithms: ['HS256'] }).unless({ path: [/^\/login/] }))
6、在服务器中设置错误中间件
切记:放在服务监听前,路由后
// 错误中间件
app.use((err, req, res, next)=> {
if (err.name === 'UnauthorizedError') {
return res.send({
status: 1,
message:'身份认证失败'
})
}
res.send({
status: 500,
message:'未知的错误'
})
})
7、设置拦截器,将拦截器文件放在每个页面的js文件前面
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pdoniCcD-1669815959114)(…/…/…/…/…/Users/zhonglongsheng/Library/Application%20Support/typora-user-images/image-20221010112301197.png)]
// 添加请求拦截器
axios.interceptors.request.use(function (config) {
config.headers.Authorization = window.localStorage.getItem('token')
return config
}, function (error) {
return Promise.reject(error)
})
// 添加响应拦截器
axios.interceptors.response.use(function (response) {
if (response.data.status === 1 && response.data.message === '身份认证失败') {
location.href = 'http://127.0.0.1:3000/html/login.html'
}
return response
}, function (error) {
return Promise.reject(error)
})
3、axios实现前后端数据交互
axios请求:
设置请求方式,请求头,请求路径 (常用get/post请求)
后端路由接收请求:
获取axios请求所传递的参数,
根据所需的数据对数据库进行查询数据,
将所查找的数据响应给axios接收
axios接收到数据,将数据渲染到页面中
4、get和post请求不同的传参方式,post请求头有哪几种类型?有什么区别?分别表示所传递数据的类型
post请求头常见的类型:
-
application/json(JSON数据格式)(常用)
用于告诉服务器数据主体是序列化后的json字符串
-
application/x-www-form-urlencoded (常用)
用于提交原生的form表单的数据,如果是文件的话只能上传文本格式的文件,form 的 enctyped 属性默认为这个值
-
multipart/form-data (常用)
也是用于提交原生的form表单的数据,不过必须让 form 的 enctyped 等于这个值;
这种方式可以将文件以二进制的形式上传,达到上传多种类型文件的功能
-
text/xml
它是一种使用 HTTP 作为传输协议,XML 作为编码方式的远程调用规范
get传参:
- 路由传参
5、ES6模块化
定义:
·每个js 文件都是一个独立的模块
·导入其他模块成员使用 import 关键字
·export 命令用于规定模块的对外接口
基本语法:
·默认导入和导出
·按需导入和导出
·直接导入并执行模块中的代码
①默认语法
默认导出格式: export default 默认导出的成员
注意: 每个模块中,只允许使用唯一的一次 export default,否则会报错。
要用到 export default 命令,为模块指定默认输出。
模块默认输出, 其他模块加载该模块时,import 命令可以为该匿名函数指定任意名字。
默认导入的语法:
import 任意名称 from ‘模块标识符’
②按需导入导出
按需导出: export 按需导出的成员
按需导入: import { … } from ‘模块标识符’
③直接执行
如果只想单纯的执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码,语法:import ‘模块’
6、less文件转css文件(脱离编译器)
·使用 antd-tools 中的 less 转换函数进行转换
·lessc 命令直接打包,node 安装 less 后,可以使用 lessc 命令进行转换:
npm install less --dev
npm lessc ./index.less ./index.css
需要注意的是,安装的包是 less,但是转换使用的命令是 lessc
7、轮播图的实现原理
①先让图片轮播起来,写一个函数里面加定时器,每次使图片的index对象加一,当index大于最大值时,设置index等于第一张图片
②图片下面的小圆点,就是使下面的按钮与上面的图片一一对应。然后点击下面的按钮显示对应的图片。
③左右的上一张和下一张按钮。点击左边的上一张按钮图片向前显示,实现原理就是使 index 对象减一。点击左边的下一张按钮图片向后显示,实现原理就是使 index 对象加一。
④对应上一张和下一张连续点击的问题就是给这两个按钮加上延时器。
⑤当鼠标放在轮播图区域时停止轮播,实现原理就是清除定时器,离开开始轮播就是加上定时器。
8、防抖节流
防抖指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖的应用场景:
用户在输入框连续输入一串字符时,可以通过防抖策略,只在输入完后,才执行查询的请求,这样可以有效减少请求次数,节约请求资源.
节流就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率
节流策略的应用场景:
鼠标不断触发某事件时,如点击,只在单位事件内触发一次.
懒加载时要监听计算滚动条的位置,但不必要每次滑动都触发,可以降低计算频率,而不必要浪费CPU资源.
9、Token和Session
Token的定义:Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。
使用Token的目的:Token的目的是为了减轻服务器的压力,减少频繁的查询数据库。
如何使用Token?
1、用设备号地址作为Token(推荐)
客户端:客户端在登录的时候获取设备的设备号,并将其作为参数传递到服务端。
服务端:服务端接收到该参数后,便用一个变量来接收同时将其作为Token保存在数据库,并将该Token设置到session中,客户端每次请求的时候都要统一拦截,并将客户端传递的token和服务器端session中的token进行对比,如果相同则放行,不同则拒绝。
2、用session值作为Token
客户端:客户端只需携带用户名和密码登陆即可。
服务端:服务端接收到用户名和密码后并判断,如果正确了就将本地获取sessionID作为Token返回给客户端,客户端以后只需带上请求数据即可。
token和session的区别?
\1) token和session其实都是为了身份验证,session一般翻译为会话,而token更多的时候是翻译为令牌;
\2) session在服务器端会保存一份,可能保存到缓存、文件或数据库;
\3) session和token都是有过期时间一说,都需要去管理过期时间;
\4) 其实token与session的问题是一种时间与空间的博弈问题,session是空间换时间,而token是时间换空间。两者的选择要看具体情况而定。
\5) 虽然确实都是“客户端记录,每次访问携带”,但token很容易设计为自包含的,也就是说,后端不需要记录什么东西,每次一个无状态请求,每次解密验证
10、map和foreach的区别
1、相同点
1) 都是循环遍历数组中的每一项。
2) 每次执行匿名函数都支持三个参数,参数分别为item(当前每一项),index(索引值),arr(原数组)。
3) 匿名函数中的this都是指向window。
4)只能遍历数组。
2、不同点
1) map()会分配内存空间存储新数组并返回,forEach()不会返回数据。
2) forEach()允许callback更改原始数组的元素,map()返回新的数组,map() 不会对空数组进行检测。
11、防抖和节流
防抖(deounce)
我们先说防抖吧,这里有个小笑话,看完你应该就秒懂了:
小明军训,教官发令:向左转!向右转!向后转!大家都照着做,唯有小明坐下来休息,教官火的一批,大声斥问他为啥不听指挥?小明说,我准备等你想好到底往哪个方向转,我再转。
虽然是个笑话,却很好地说明了防抖的定义:给一个固定时间,如果你开始触发动作,并且在这个固定时间内不再有任何动作,我就执行一次,否则我每次都会重新开始计时。我们可以用极端情况理解它:如果给定时间间隔足够大,并且期间一直有动作触发,那么回调就永远不会执行。放在笑话的语境里就是,只有教官最后一次发号后,小明才会转。
节流(throttle)
如果你理解了防抖,关于节流,就更好理解了
学生上自习课,班主任五分钟过来巡视一次,五分钟内随便你怎么皮,房顶掀了都没事,只要你别在五分钟的时间间隔点上被班主任逮到,逮不到就当没发生,逮到她就要弄你了。
在这里,班主任就是节流器;你搞事,就是用户触发的事件;你被班主任逮住弄,就是执行回调函数。
节流的定义:用户会反复触发一些操作,比如鼠标移动事件,此时只需要指定一个“巡视”的间隔时间,不管用户期间触发多少次,只会在间隔点上执行给定的回调函数。,我们同样可以用极端情况来理解:如果给定的间隔时间是 240毫秒,用户永不间断地在屏幕上疯狂移动鼠标,那么你的回调函数会分别在 240毫秒、 480毫秒、 720毫秒… 就这么一直执行下去
12、闭包
理解:主要是为了设计私有的方法和变量。
优点:可以避免全局变量造成污染。
缺点:闭包会常驻内存,增加内存使用量,使用不当会造成内存泄漏。
特征:(1)函数嵌套函数。(2)在函数内部可以引用外部的参数和变量。(3)参数和变量不会以垃圾回收机制回收。
13、src和href的区别
- href
href:是指向网络资源所在位置,建立和当前元素(锚点)或当前文档(链接)之间的链接,用于超链接。
标识超文本引用,用在link和a等元素上,href是引用和页面关联,是在当前元素和引用资源之间建立联系
****当浏览器解析到这一句的时候会识别该文档为css文件,会下载并且不会停止对当前文档的处理,这也是为什么建议使用link方式来加载css而不是使用@import。
- src
src:是指向外部资源的位置,指向的内部会迁入到文档中当前标签所在的位置;在请求src资源时会将其指向的资源下载并应用到当前文档中,例如js脚本,img图片和frame等元素。
表示引用资源,表示替换当前元素,用在img,script,iframe上,src是页面内容不可缺少的一部分。
当浏览器解析到这一句的时候会暂停其他资源的下载和处理,直至将该资源加载,编译,执行完毕,图片和框架等元素也是如此,类似于该元素所指向的资源嵌套如当前标签内,这也是为什么要把js饭再底部而不是头部。- src和href的区别:
- 当浏览器遇到href会并行下载资源并且不会停止对当前文档的处理。(同时也是为什么建议使用 link 方式加载 CSS,而不是使用 @import 方式)
- 当浏览器解析到src ,会暂停其他资源的下载和处理,直到将该资源加载或执行完毕。(这也是script标签为什么放在底部而不是头部的原因)
14、跨页面通信
1、路由传参
2、浏览器缓存
3、数据代理,proxy
4、bus总线程
5、vuex
16.new操作符具体干了什么
- 创建一个空对象
- 将对象的原型链挂载在函数的原型上
- 改变this指向
- 返回实例化对象
17、常见的请求方式
1、get:
请求指定页面信息,并返回实体主体。
2、head:
类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头。
3、post:
向指定资源提交数据进行处理请求(例如提交表单或上传文件),数据包含在请求体中。
post请求可能会导致新的资源的建立或已有资源的修改。
4、put:
从客户端向服务器传送的数据取代指定的文档的内容。
5、delete:
请求服务器删除指定的页面。
6、connect:
HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。
7、options:
允许客户端查看服务器的性能。
8、trace:
回显服务器收到的请求,主要用于测试或诊断。
18.JS有几种方法判断变量的类型?⭐⭐⭐
typeof
判断基本数据类型,对于引用数据类型除了function返回’function‘,其余全部返回’object’。
instanceof
区分引用数据类型,检测方法是检测的类型在当前实例的原型链上,用其检测出来的结果都是true,不太适合用于简单数据类型的检测,检测过程繁琐且对于简单数据类型中的undefined, null, symbol检测不出来。
constructor
检测引用数据类型,检测方法是获取实例的构造函数判断和某个类是否相同,如果相同就说明该数据是符合那个数据类型的,这种方法不会把原型链上的其他类也加入进来,避免了原型链的干扰。
Object.prototype.toString.call()
适用于所有类型的判断检测,检测方法是Object.prototype.toString.call(数据) 返回的是该数据类型的字符串。(举例:字符串返回的是[object String])
19、vue中的修饰符
1、表单修饰符
<1> .lazy
默认情况下,v-model同步输入框的值和数据。可以通过这个修饰符,转变为在change事件再同步。
2、事件修饰符
.stop 阻止事件继续传播
.prevent 阻止标签默认行为
.capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
.self 只当在 event.target 是当前元素自身时触发处理函数
.once 事件将只会触发一次
.passive 告诉浏览器你不想阻止事件的默认行为
使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用v-on:click.prevent.self会阻止所有的点击,而 v-on:click.self.prevent 只会阻止对元素自身的点击。
3、鼠标按键修饰符
<button @click.left="shout(1)">ok</button> //左键
<button @click.right="shout(1)">ok</button> //右键
<button @click.middle="shout(1)">ok</button> //中键
4、键值修饰符
put @keyup.enter=“submit”>
1
全部的按键别名:
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
修饰键:
.ctrl
.alt
.shift
.meta
5、v-bind修饰符
sync
实现子组件props的双向绑定
//父组件
//父亲组件
<comp :myMessage=“bar” @update:myMessage=“func”>
func(e){
this.bar = e;
}
//子组件js
func2(){
this.$emit(‘update:myMessage’,params);
}
使用sync的时候,子组件传递的事件名格式必须为update:value,其中value必须与子组件中props中声明的名称完全一致
注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用
将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的
props
设置自定义标签属性,避免暴露数据,防止污染HTML结构
20、Vuex的五个核心概念:
state、getters、mutations、actions、modules
1、state: vuex的基本数据,用来存储变量;
2、getters: 从基本数据(state)派生的数据,相当于state的计算属性;
3、mutations: 提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mutation 都有一个字符串的事件类型(type)和一个回调函数(handler)。
回调函数就是我们实际进行状态更改的地方,并且它会接受 state作为第一个参数,提交载荷作为第二个参数。
4、action: 和mutation的功能大致相同,不同之处在于 ①Action提交的是mutation,而不是直接变更状态,②Action可以包含任意异步操作。
5、modules: 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理。
21、V8引擎的垃圾回收采用标记清除法与分代回收法
1.引用计数算法:记录每个变量值被使用的次数,如果为0,就销毁,回收其所占用的内存空间
2.标记清除:标记清除,标记整理(解决内存碎片,合理分配内存),
3.分代回收:针对不同的对象(新生代---存活时间短和老生代)采用不同的算法
新生代:复制算法(Scavenge 算法)加标记整理算法:对象区域和空闲区域无限重复使用
JavaScript 引擎采用了对象晋升策略,也就是经过两次垃圾回收依然还存活的对象,会被移动到老生区中。
老生代:标记清除,标记整理(解决内存碎片,合理分配内存)
22、es6函数新增
-
新增参数默认值
function add(n1=0,n2=100){ return n1+n2; } console.log(add()) // 输出100
-
新增args参数
args参数可代替arguements,arguements是一个类数组对象,args是纯数组
function add(...args){ //将数组或类数组转成以逗号隔开的列表 console.log(args); let sum = 0; args.forEach(function(item){ sum += item; }) return sum; } console.log(add(1,2,3,4,5));
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4pRvcg0l-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128121650779.png)]
-
新增箭头函数
23、类数组
什么是类数组
长的像数组的对象,类数组又叫伪数组
类数组和数组的区别
1.都有length属性
2.都可以for循环遍历,有的类数组还可以通过for of遍历
3.类数组不具备数组的原型方法,不可以调用相关数组方法(如,push、slice、concat…)
类数组转换为真正的数组
-
遍历类数组,依次将元素放入一个空数组。
-
用扩展运算符或者Array.from()方法转换
es6新增了扩展运算符(…)以及Array.from()方法,可以直接将类数组转换为真正的数组。
关于扩展运算符以及Array.from()的详细用法,可参考阮一峰老师的《es6标准入门》一书。
-
利用apply展开
apply方法的第二个参数是数组,也可以是类数组,在调用的时候会将第二个参数依次展开。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GJjc3wDj-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128122602116.png)]
24、什么是严格模式
- 在严格的条件下允许 JS 代码。
- 消除了 JavaScript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
- 提高编译器效率,增加运行速度。
- 禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 JavaScript 做好铺垫。比如一些保留字,如:class、enum、export、extends、import、super不能做变量名。
25、map和forEach有什么区别
-
forEach()方法没有返回值,而map()方法有返回值。
-
forEach遍历通常都是直接引入当前遍历数组的内存地址,生成的数组的值发生变化,当前遍历的数组对应的值也会发生变化。
-
map遍历的后的数组通常都是生成一个新的数组,新的数组的值发生变化,当前遍历的数组值不会变化。
-
总的来说 map 的速度大于forEach。
26、如何给一个DOM元素绑定两个点击事件
通过事件监听进行绑定:addEventListener
27、一个冒泡一个捕获在同一个绑定对象先执行哪个
绑定在被点击元素的事件是按照代码顺序发生,其他元素通过冒泡或者捕获“感知”的事件,按照W3C的标准,先发生捕获事件,后发生冒泡事件。所有事件的顺序是:其他元素捕获阶段事件 -> 本元素代码顺序事件 -> 其他元素冒泡阶段事件 。
27、JQuery中的$(document).ready与window.onload有什么区别
-
执行时间
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYlUsHjx-1669815959115)(C:\Users\wzwde\AppData\Roaming\Typora\typora-user-images\image-20221128123524242.png)]
window.onload必须等到页面内包括图片的所有元素和资源加载完毕后才能执行,也就是上述图片的时间点2;
$(document).ready()是DOM加载完毕后就执行,不必等到整个网页资源加载完毕,也就是在上述图片的时间点1。
所以,使用document.ready()方法的执行速度比window.onload的方法要快。 -
编写个数不同
$(document).ready()可以同时编写多个,并且都可以得到执行
window.onload不能同时编写多个,如果有多个window.onload方法,只会其中执行一个 -
简化写法
window.onload没有简化写法
$ (document).ready(function(){ })可以简写成$(function(){方法体 });
又因为JQuery的默认参数是document,则还可以写成$().ready(function{ })
28、描述一下原型和原型链
一切对象都是继承来自Object对象,Object对象直接继承根源对象null
一切的函数对象(包括 Object对象)都是继承自Function对象
Object对象直接继承自Function对象
Function对象的__proto__会指向自己的原型对象,最终还是继承Object对象
29、ES6怎么实现继承
- 在定义类的时候使用extends继承父类把类方法和实例方法继承下来
- 在constructor中使用super调用父类调用父类
30、async/await和Promise有什么关系
await必须在async内使用,并装饰一个Promise对象,async返回的也是一个Promise对象。
async封装Promise,await相当then,try…catch相当于catch
31、bind、call、apply有什么区别
- 三者都可以改变函数的
this
对象指向 - 三者第一个参数都是
this
要指向的对象,如果如果没有这个参数或参数为undefined
或null
,则默认指向全局window
- 三者都可以传参,但是
apply
是数组,而call
是参数列表,且apply
和call
是一次性传入参数,而bind
可以分为多次传入 bind
是返回绑定this之后的函数,apply
、call
则是立即执行
call
、apply
、bind
作用是改变函数执行时的上下文,简而言之就是改变函数运行时的this
指向
32、computer和watch区别
计算属性computed :
-
支持缓存,只有依赖数据发生改变,才会重新进行计算
-
不支持异步,当computed内有异步操作时无效,无法监听数据的变化
3.computed 属性值会默认走缓存,计算属性是基于它们的响应 式依赖进行缓存的,也就是基于data中声明过的数据通过计算得 到的
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用computed
5.如果computed属性属性值是函数,那么默认会走get方法;函 数的返回值就是属性的属性值;在computed中的,属性都有一 个get和一个set方法,当数 据变化时,调用set方法。
监听属性watch:
- 不支持缓存,数据变,直接会触发相应的操作;
2.watch支持异步;
3.监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作;一对多
33、vuex5个方法以及如何同步异步
1.state:vuex的基本数据,用来存储变量
2. geeter:从基本数据(state)派生的数据,相当于state的计算属性,具有返回值的方法
3. mutation:提交更新数据的方法,必须是同步的
4. action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作
5. modules:模块化vuex,可以让每一个模块拥有自己的state、mutation、action、getters,使得结构非常清晰,方便管理
同步:组件中使用mapMutations方法或者this.$store.commit('JIAN',this.n)
异步:this.$store.dispatch('aaddper',count)
34、说说对$nextTick的理解
我们可以理解成,Vue 在更新 DOM 时是异步执行的。当数据发生变化,Vue将开启一个异步更新队列,视图需要等队列中所有数据变化完成之后,再统一进行更新
Vue生命周期的created()钩子函数进行的DOM操作一定要放在Vue.nextTick()的回调函数
35、说说对vue中keep-alive的理解
在平常开发中,有部分组件没有必要多次初始化,这时,我们需要将组件进行持久化,使组件的状态维持不变,在下一次展示时,也不会进行重新初始化组件。
也就是说,keepalive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染,也就是所谓的组件缓存。
是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM。
使用了keep-alive的组件以后,组件上就会自动加上了activated钩子和deactivated钩子。
activated 当组件被激活(使用)的时候触发 可以简单理解为进入这个页面的时候触发
deactivated 当组件不被使用(inactive状态)的时候触发 可以简单理解为离开这个页面的时候触发
36、v-model原理
v-model只不过是一个语法糖而已,真正的实现靠的还是
v-bind:绑定响应式数据
触发oninput 事件并传递数据
37、谈谈this的理解,指向对象有哪些
this 关键字是函数运行时自动生成的一个内部对象,只能在函数内部使用,总指向调用它的对象。
this 的情况:
1、以函数形式调用时,this 永远都是 window
2、以方法的形式调用时,this 是调用方法的对象
3、以构造函数的形式调用时,this 是新创建的那个对象
4、使用 call 和 apply 调用时,this 是指定的那个对象
5、箭头函数:箭头函数的 this 看外层是否有函数 如果有,外层函数的 this 就是内部箭头函数的 this 如果没有,就是 window
6、特殊情况:通常意义上 this 指针指向为最后调用它的对象。这里需要注意的一点就是 如果返回值是一个对象,那么 this 指向的就是那个返回的对象,如果返回值不是一个对象那么 this 还是指向函数的实例
38、for in 和for of的区别
for…of 遍历获取的是对象的键值,for…in 获取的是对象的键名;
for… in 会遍历对象的整个原型链,性能非常差不推荐使用,而 for … of 只遍历当前对象不会遍历原型链;
对于数组的遍历,for…in 会返回数组中所有可枚举的属性(包括原型链上可枚举的属性),for…of 只返回数组的下标对应的属性值;
总结: for…in 循环主要是为了遍历对象而生,不适用于遍历数组;for…of 循环可以用来遍历数组、类数组对象,字符串、Set、Map 以及 Generator 对象。
40、instanceof
instanceof运算符用于检测构造函数的prototype属性是否出现在某个实例对象的原型链上。
区别:
typeof与instanceof都是判断数据类型的方法
• typeof会返回一个变量的基本类型,instanceof返回的是一个布尔值
• instanceof 可以准确地判断复杂引用数据类型,但是不能正确判断基础数据类型
• 而 typeof 也存在弊端,它虽然可以判断基础数据类型(null 除外),但是引用数据类型中,除了 function 类型以外,其他的也无法判断
如果需要通用检测数据类型,可以采用Object.prototype.toString,调用该方法,统一返回格式“[object Xxx]” 的字符串。
41、堆和栈的区别
1.申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;
2.申请大小的不同。栈获得的空间较小,而堆获得的空间较大;
3.申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;
4.存储内容的不同。栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;
5.底层不同。栈是连续的空间,而堆是不连续的空间。
概述
栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间。基本类型:String,Number,Boolean,Null,Undefined。
堆(heap):动态分配的内存,大小不定也不会自动释放,存放引用类型,指那些可能由多个值构成的对象,保存在堆内存中,包含引用类型的变量,实际上保存的不是变量本身,而是指向该对象的指针。引用类型:Function,Array,Object
42、forEach中return有效果吗?如何中断forEach循环?
在forEach中用return不会返回,函数会继续执行。
中断方法:
• 使用try监视代码块,在需要中断的地方抛出异常。
• 官方推荐方法(替换方法):用every和some替代forEach函数。
o every在碰到return false的时候,中止循环。
o some在碰到return true的时候,中止循环。
43、什么是内存泄漏,什么原因会导致
①.程序中动态分配的堆内存由于某种原因未释放或无法释放
什么原因:全局变量过多、事件监听未移除、缓存
44、Ajax原理
通过xmlhttprequest对象来向服务器发送异步请求,从服务器获得数据,然后用js来操作dom而更新页面
1、ajax步骤:
1、创建XMLHttpRequest异步对象
2、设置回调函数
3、使用open方法与服务器建立连接
4、向服务器发送数据
5、在回调函数中针对不同的响应状态进行处理
2、ajax 的5个状态值
- 0:(未初始化) 还没有调用open()方法。
- 1:(启动) 已经调用open()方法,但还没有调用send()方法。
- 2:(发送) 已经调用send()方法,但还没有接收到响应。
- 3:(接收) 已经接收到部分响应数据。
- 4:(完成) 已经接收到全部的响应数据,且可以在客户端使用了。
3、ajax的状态码
5大类:
100+:表示连接继续
200+:操作成功收到,分析、接受
300+:完成此请求必须进一步处理,重定向
400+:表示各种客户端错误
500+:表示各种服务器错误
45、vue的组件通信
子传父:
1.
e
m
i
t
自
定
义
事
件
,
第
二
个
值
为
数
据
,
父
组
件
绑
定
监
听
器
获
取
参
数
的
数
据
2.
r
e
f
父
组
件
在
子
组
件
时
设
置
r
e
f
通
过
t
h
i
s
.
emit自定义事件,第二个值为数据,父组件绑定监听器获取参数的数据 2.ref 父组件在子组件时设置ref 通过this.
emit自定义事件,第二个值为数据,父组件绑定监听器获取参数的数据2.ref父组件在子组件时设置ref通过this.refs.xxx获取数据
父传子:
1.子组件设置props属性,定义接收父组件传递来的参数 父组件通过字面量传递值
2.$attrs$listeners 可传不被prop识别的特性绑定 可通过v-bind传入内部组件
非父子:
1.EventBus总线程
2.
p
a
r
e
n
t
、
parent、
parent、root 需要共同祖辈组件
3.provide和inject 祖先组件定义provide属性返回传递的值 后代组件通过inject接收
46、 r o u t e 和 route和 route和router区别
1、
r
o
u
t
e
r
是
用
来
操
作
路
由
,
router 是用来操作路由,
router是用来操作路由,route是用来获取路由信息
2、
r
o
u
t
e
r
是
V
u
e
R
o
u
t
e
r
的
一
个
实
例
,
他
包
含
了
所
有
的
路
由
,
包
括
路
由
的
跳
转
方
法
,
钩
子
函
数
等
,
也
包
含
一
些
子
对
象
(
例
如
h
i
s
t
o
r
y
)
3
、
router 是VueRouter的一个实例,他包含了所有的路由,包括路由的跳转方法,钩子函数等,也包含一些子对象(例如history) 3、
router是VueRouter的一个实例,他包含了所有的路由,包括路由的跳转方法,钩子函数等,也包含一些子对象(例如history)3、route 是一个跳转的路由对象(路由信息对象),每一个路由都会有一个
r
o
u
t
e
对
象
,
是
一
个
局
部
的
对
象
。
4
、
route对象,是一个局部的对象。 4、
route对象,是一个局部的对象。4、router是访问路由器而$route是访问当前路由
47、vue中hash和history区别
兼容性hash(ie8)比较与history(ie10)更好
外观history比较好看
history每次刷新会重新像后端请求整个网址,也就是重新请求服务器,如果后端没有及时响应,就会报错404
history的好处是可以进行修改历史记录,并且不会立刻像后端发起请求
48、arguments转化为数组的方法
1、Array.prototype.slice.apply(arguments)
2、使用ES6的新语法 Array.from() 来转换
3、使用 for 循环挨个将 arguments 对象中的内容复制给新数组中
49、同步和异步操作
同步:代码按照顺序一行一行执行。
**异步:**将用户请求放入消息队列,等同步执行完成以后再执行异步。
异步的操作有:axios,定时器,promise,事件监听,回调函数
50、e.target和e.currentTarget的区别
**e.target:****触发事件的元素
**e.currentTarget:**绑定事件的元素
51、闭包的垃圾回收机制?
垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大并且GC时停止响应其他操作,所以垃圾回收器会按照固定的时间间隔周期性的执行
53、cookie、sessionStorage、localStorage的区别⭐⭐⭐
保存方式
cookie存放在客户的浏览器上。
session都在客户端中保存,不参与服务器通讯。
生命周期
cookie可设置失效时间
localStorage除非手动清除否则永久保存
sessionStorage关闭当前页面或浏览器后失效
存储的大小
cookie 4kb左右
session 5M
易用性
cookie需自己封装
session可以接受原生接口
因为cookie每次请求都会携带在http请求中,所以它的主要用来识别用户登录,localStorage可以用来跨页面传参,sessionStorage可以用来保留一些临时数据。
54、数组的方法?
Push()、pop()、shift()、unshift()、slice()、splice()、jion()、concat、
IndexOf()、includes()、sort()reverse()、map()、filter()lastIndexO()
55、解决跨域问题
什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
常见的:
1、JSONP跨域
原理:利用script标签可以跨域请求资源,将回调函数作为参数拼接在url中。后端收到请求,调用该回调函数,并将数据作为参数返回去,注意设置响应头返回文档类型,应该设置成javascript。
2、跨域资源共享(CORS)
目前最常用的一种解决办法,通过设置后端允许跨域实现。
3、nginx反向代理
跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制。
4、WebSocket协议跨域
5、proxy
前端配置一个代理服务器(proxy)代替浏览器去发送请求:因为服务器与服务器之间是可以通信的不受同源策略的影响。
详细可看九种常见的前端跨域解决办法
56、下面代码的输出是什么?
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('timer')
resolve('success')
}, 1000)
})
const start = Date.now();
promise.then(res => {
console.log(res, Date.now() - start)
})
promise.then(res => {
console.log(res, Date.now() - start)
})
这里 Promise 构造函数只执行一次
结果:'timer ';‘success’ 1001 ; ‘success’ 1002
57、什么是Polyfill
polyfill(polyfiller),指的是一个代码块。这个代码块向开发者提供了一种技术, 这种技术可以让浏览器提供原生支持,抹平不同浏览器对API兼容性的差异。
(Polly 指的是用于实现浏览器并不支持的原生 API 的代码。)
58、Promise中的值穿透
解释:.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。
.then :当then中传入的不是函数,则这个then返回的promise的data,将会保存上一个的promise.data。这就是发生值穿透的原因。而且每一个无效的then所返回的promise的状态都为resolved
.catch :当使用.catch时,会默认为没有指定失败状态回调函数的.then添加一个失败回调函数(上文中有具体函数代码)。 .catch所谓的异常穿透并不是一次失败状态就触发catch,而是一层一层的传递下来的。 异常穿透的前提条件是所有的.then都没有指定失败状态的回调函数。 如果.catch前的所有.then都指定了失败状态的回调函数,.catch就失去了意义。
59、vue2响应式和vue3响应式的区别
- 基于Object.defineProperty,不具备监听数组的能力,需要重新定义数组的原型来达到响应式。
- Object.defineProperty 无法检测到对象属性的添加和删除 。
- 由于Vue会在初始化实例时对属性执行getter/setter转化,所有属性必须在data对象上存在才能让Vue将它转换为响应式。
- 深度监听需要一次性递归,对性能影响比较大。
-
基于Proxy和Reflect,可以原生监听数组,可以监听对象属性的添加和删除。
-
不需要一次性遍历data的属性,可以显著提高性能。
-
因为Proxy是ES6新增的属性,有些浏览器还不支持,只能兼容到IE11 。
-
通过new Proxy()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
4.1发布者-订阅者模式是什么
JavaScript设计模式(5)——发布者-订阅模式_闲人王昱珩的博客-CSDN博客
[观察者模式和发布订阅模式的区别 - 简书 (jianshu.com)](
60、箭头函数与普通函数的区别
1、外形不同:箭头函数使用箭头定义,普通函数中没有。
2、 箭头函数全都是匿名函数:普通函数可以有匿名函数,也可以有具名函数
3、箭头函数不能用于构造函数:普通函数可以用于构造函数,以此创建对象实例。
4、箭头函数中 this 的指向不同:在普通函数中,this 总是指向调用它的对象,如果用作构造函数,它指向创建的对象实例。
5、箭头函数没有自己的this,会捕获其所在的上下文的this值,并且不能通过call()和apply()来改变其this
6、箭头函数不具有 arguments 对象:每一个普通函数调用后都具有一个arguments 对象,用来存储实际传递的参数。但是箭头函数并没有此对象。
7、其他区别:箭头函数不具有 prototype 原型对象。箭头函数不具有 super。
箭头函数不具有 new.target
61、jQuery和vue各自的优势
答:
jQuery:简化dom操作,对dom操作变得更简单方便
vue:减少dom操作、双向数据绑定,把操作dom转变为操作数据,通过数据渲染dom;组件化
62、对aysnc、await的理解
答:
async用来声明一个函数是异步的,而await是等待这个异步方法执行完毕
async函数会返回一个promise对象
async与await的优势在于处理多条promise链式调用
63、深拷贝和浅拷贝的区别,如何实现
浅拷贝:
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
1、Object.assign()
2、函数库lodash的 _.clone 方法
3、es6的展开运算符 …
4、Array.prototype.concat()
5、Array.prototype.slice()
深拷贝:
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
总而言之,浅拷贝改动拷贝的数组原数组也会变(慎用!项目中很多地方共用的数组都会变)。深拷贝修改新数组不会改到原数组。
实现方法
- JSON.parse(JSON.stringify())
- 函数库lodash的 _.cloneDeep 方法
- **jQuery.extend()**方法
- 手写递归方法(转)
64、$.ready和window.onload的区别
答:$.ready,在Dom解构加载完毕后就执行;而window.onload是在,页面加载完毕(包括图片)才会执行。
.
r
e
a
d
y
执
行
效
率
比
w
i
n
d
o
w
.
o
n
l
o
a
d
更
高
。
w
i
n
d
o
w
.
o
n
l
o
a
d
会
覆
盖
,
只
执
行
一
个
,
.ready执行效率比window.onload更高。 window.onload会覆盖,只执行一个,
.ready执行效率比window.onload更高。window.onload会覆盖,只执行一个,ready不会覆盖,会根据代码执行顺序依次执行。
65、Promise中断
把pending状态的promise给reject
网络请求设置超时时间,一旦超时就中断
66、图片懒加载
通过判断每张图片的offsetTop是否小于视口的高度和滚动条的scrollTop的和,判断是否进入可视区域,如果进入可视区域就进行显示
67、字符串的常用方法:
拼接 concat()
切割 slince() substr() substring() split()
slince() 起始下标和结束下标 可正可负
substr() 起始下标和截取长度
substring() 起始下标和结束下标
split() 按照指定分隔符,拆分成数组中的每一项
复制 repeat() 接收一个整数参数
替换 replace()
删除前后空格符 trim()
查找 charAt() indexOf()
68、什么是扩展运算符?
-
对象中的扩展运算符(…)用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
let bar = { a: 1, b: 2 }; let baz = { ...bar }; // { a: 1, b: 2 }
69、什么是箭头函数?
箭头函数不会创建自己的this
箭头函数继承而来的this指向永远不变
.call()/.apply()/.bind()无法改变箭头函数中this的指向
箭头函数不能作为构造函数使用
箭头函数没有自己的arguments
箭头函数没有原型prototype
70、4钟字符串的切割方法
1、slice(start, end)
2、substr(start, length)
3、substring(start, stop)
4、split(separator, length)
5、join(separator)
6、splice(start, length, …args)
71、怎么在setTimerout实现setIterval的功能
setInterval是指定时间间隔后重复执行指定回调,所以可以使用递归方式重复调用setTimeout
function intervalFn(fn, ms) {
function inner() {
fn();
setTimeout(inner, ms);
}
setTimeout(inner, ms); // 第二个setTimeout
}
intervalFn(fn, 1000);
function fn() {
console.log(666)
}
72、什么是原型链
主要是还是实现继承与扩展对象。
每个函数对象都有一个 prototype 属性,这个属性就是函数的原型对象。
原型链是JavaScript实现继承的重要方式,原型链的形成是真正是靠__proto__ 而非prototype。
所有的引用类型(包括数组,对象,函数)都有隐性原型属性(proto), 值也是一个普通的对象。
所有的引用类型的 proto 属性值都指向构造函数的 prototype 属性值。
构造函数 new 出来一个对象,而每个对象都有一个 constructor 属性,该属性指向创建该实例的构造函数。
实例对象通过__proto__或者 object.getPrototype 的方法获取原型。
原型链其实就是有限的实例对象和原型之间组成有限链,就是用来实现共享属性和继承的。
73、bus总线程与vuex的区别
74、async/await的原理
当调用一个 async 函数时,会返回一个 Promise 对象 (关键)
async 函数中可能会有 await 表达式,await表达式 会使 async 函数暂停执行,直到表达式中的Promise解析完成后继续执行 async 中 await 后面的代码并返回解决结果。
既然返回的是Promise 对象,所在最外层不能直接获取其返回值,那么肯定可以用原来的方式:then() 链来处理这个 Promise 对象
原理:
async/await 函数其实就是一种语法糖
async/await 是基于promise实现的,async 函数其实就是把 promise 做了一个包装
await 返回值是一个 Promise 对象,它只是把 await 后面的代码放到了 Promise.then()
promise
使用.then
链式调用,但也是基于回调函数async/await
更加优雅的异步编程的写法
1.它是消灭异步回调的终极武器
2.它是同步语法,也就是用同步的写法写异步的代码
75、插槽的理解
-
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于 父组件 ===> 子组件 。
-
分类:默认插槽、具名插槽、作用域插槽
76、判断一个对象是否是空对象?
方法一:将对象转换成字符串,再判断是否等于“{}”
let obj={};
console.log(JSON.stringify(obj)==="{}");
方法二:for in循环
let result=function(obj){
for(let key in obj){
return false;//若不为空,可遍历,返回false
}
return true;
}
console.log(result(obj));//返回true
方法三:Object.keys()方法,返回对象的属性名组成的一个数组,若长度为0,则为空对象(ES6的写法)
console.log(Object.keys(obj).length==0);//返回true
方法四:Object.getOwnPropertyNames方法获取对象的属性名,存到数组中,若长度为0,则为空对象
console.log(Object.getOwnPropertyNames(obj).length==0);//返回true
78、es5和es6中class的区别
一、class类必须new调用,不能直接执行。
二、class类不存在变量提升
三、class类无法遍历它实例原型链上的属性和方法
四、new.target属性
es6为new命令引入了一个new.target属性,它会返回new命令作用于的那个构造函数。如果不是通过new调用或Reflect.construct()调用的,new.target会返回undefined
五、class类有static静态方法
static静态方法只能通过类调用,不会出现在实例上;另外如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。static声明的静态属性和方法都可以被子类继承。
79、es6新增的数组方法
1、Array.from()
Array.from 方法用于将两类对象转为真正的数组:
- 类似数组的对象(array-like object)
- 可遍历(iterable)的对象(包括 ES6 新增的数据结构 Set 和 Map)
2、Array.of()
Array.of 方法用于将一组值,转换为数组。弥补数组构造函数 Array()的不足。因为参数个数的不同,会导致 Array()的行为有差异
3、数组实例的 find() 和 findIndex()
find()
返回第一个符合条件的数组成员,它的参数是一个回调函数,所有数组成员依次执行该函数,直到找出第一个满足条件的成员,然后返回该成员,如果没有符合条件的成员,则返回undefined
该方法的回调函数接收三个参数: 当前的值 , 当前位置, 原数组
findIndex()
写法用法基本与find()方法相同,只是返回第一个符合条件的数组成员的位置,如果没有则返回-1
4、数组实例的 fill()
- fill()方法使用给定值, 填充一个数组
- fill 方法还可以接受第二个和第三个参数,用于指定填充的起始位置和结束位置
5、数组实例的 entries(),keys() 和 values()
三个方法都用来遍历数组,都返回一个遍历器对象,可用for…of循环遍历
不同的是:
- keys()是对键名的遍历
- values()是对键值的遍历
- entries()是对键值对的遍历
6、includes()方法返回一个布尔值
该方法返回一个布尔值,表示某个数组中是否包含给定的值
[1, 2, 3].includes(2) // true[(1, 2, 3)].includes(4) // false
7、数组实例的 flat(),flatMap()
- flat()用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。传参数代表拉平几层默认是一层
- flatMap()只能展开一层数组。方法对原数组的每个成员执行一个函数(相当于执行 Array.prototype.map()),然后对返回值组成的数组执行 flat()方法。该方法返回一个新数组,不改变原数组
8、数组实例的copywithin()
在当前数组内部将指定位置的成员复制到其他位置,然后返回当前数组,会改变原数组
接收三个参数:
1、target(必需) 从该位置开始替换数据
2、start(可选) 从该位置开始读取数据,默认为0,如果为负数,则表示到数
3、end(可选) 到该位置前停止读取数据,默认等于数组长度。如果是负数,表示到数
三个参数都应该是数字,如果不是,会自动转为数值
80、性能优化
举例:
- 减少http请求次数
- 减少DNS查找
- 避免重定向
- 使用Ajax缓存
- 少用全局变量、减少DOM操作的使用
- 优化图片大小,通过CSS Sprites(精灵图)优化图片,
- 将css放在顶部,将js放在底部
81、首屏加载优化方案
1、vue-router 路由[懒加载]
2、使用CDN加速,将通用库从vendor进行抽离
3、Nginx开启Gzip功能
4、异步组件
5、服务端渲染SSR
6、按需加载UI库
7、webpack开启gzip压缩
8、图片懒加载减少占用带宽
9、页面使用骨架屏
10、js脚本使用异步和延迟
82、事件与事件流
JavaScript 中每个元素都可以产生触发JavaScript 函数的事件。我们可以认为事件可以被JavaScript侦测到的一种行为。
而接收事件的次序称为事件流。 事件流总共有两种形式:事件冒泡,事件捕获。
事件冒泡流:具体触发事件的元素先接收事件,然后向上传导
事件捕获流:与冒泡流相反,最外层先接收到事件
事件流有三个阶段:事件捕获阶段---->处于目标阶段---->事件冒泡阶段
83、事件模型
事件模型可以分为三种:
- 原始事件模型(DOM0级)
- 标准事件模型(DOM2级)
- IE事件模型(基本不用)
原始事件模型
- HTML代码中直接绑定
<input type="button" onclick="fun()">
- 通过
JS
代码绑定
var btn = document.getElementById('.btn'); btn.onclick = fun;
特性
- 绑定速度快
DOM0
级事件具有很好的跨浏览器优势,会以最快的速度绑定,但由于绑定速度太快,可能页面还未完全加载出来,以至于事件可能无法正常运行
- 只支持冒泡,不支持捕获
- 同一个类型的事件只能绑定一次
2、标准事件模型
在该事件模型中,一次事件共有三个过程:
- 事件捕获阶段:事件从
document
一直向下传播到目标元素, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行 - 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数
- 事件冒泡阶段:事件从目标元素冒泡到
document
, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
事件绑定监听函数的方式如下:
addEventListener(eventType, handler, useCapture)
事件移除监听函数的方式如下:
removeEventListener(eventType, handler, useCapture)
参数如下:
eventType
指定事件类型(不要加on)handler
是事件处理函数useCapture
是一个boolean
用于指定是否在捕获阶段进行处理,一般设置为false
与IE浏览器保持一致
举个例子:
var btn = document.getElementById('.btn');
btn.addEventListener(‘click’, showMessage, false);
btn.removeEventListener(‘click’, showMessage, false);
特性
- 可以在一个
DOM
元素上绑定多个事件处理器,各自并不会冲突
3、IE事件模型
IE事件模型共有两个过程:
- 事件处理阶段:事件到达目标元素, 触发目标元素的监听函数。
- 事件冒泡阶段:事件从目标元素冒泡到
document
, 依次检查经过的节点是否绑定了事件监听函数,如果有则执行
事件绑定监听函数的方式如下:
attachEvent(eventType, handler)
事件移除监听函数的方式如下:
detachEvent(eventType, handler)
84、尾递归
尾递归,即在函数尾位置调用自身(或是一个尾调用本身的其他函数等等)。尾递归也是递归的一种特殊情形。尾递归是一种特殊的尾调用,即在尾部直接调用自身的递归函数
尾递归在普通尾调用的基础上,多出了2个特征:
- 在尾部调用的是函数自身
- 可通过优化,使得计算仅占用常量栈空间
在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储,递归次数过多容易造成栈溢出
这时候,我们就可以使用尾递归,即一个函数中所有递归形式的调用都出现在函数的末尾,对于尾递归来说,由于只存在一个调用记录,所以永远不会发生"栈溢出"错误
实现一下阶乘,如果用普通的递归,如下:
function factorial(n) { if (n === 1) return 1; return n * factorial(n - 1); } factorial(5) // 120
如果n
等于5,这个方法要执行5次,才返回最终的计算表达式,这样每次都要保存这个方法,就容易造成栈溢出,复杂度为O(n)
如果我们使用尾递归,则如下:
function factorial(n, total) { if (n === 1) return total; return factorial(n - 1, n * total); } factorial(5) // 120
可以看到,每一次返回的就是一个新的函数,不带上一个函数的参数,也就不需要储存上一个函数了。尾递归只需要保存一个调用栈,复杂度 O(1)
85、js本地储存的方法
与服务器交互
cookie:是网站为了标识用户的身份而存储在用户本地终端上的数据(通常需要加密),通常会在 同源 的 HTTP请求中携带(即使不需要),在浏览器和服务器之间来回传递。cookie还有路径的概念,可以控制存储在某个路径下面。
localStorage & sessionStorage: 不会自动把数据发给服务器,仅在本地存储
存储大小
cookie:根据不同浏览器的限制,大小一般不能超过4k,因为每次 HTTP 请求 都会携带
localStorage& sessionStorage 虽然也有存储大小的限制,但比 cookie大得多,可以达到5M或更大
存储时间
cookie:在设置 cookie 的有效期之前,是一直生效的,与浏览器页面是否关闭无关
localStorage:储存持久数据,浏览器页面关闭数据也不会丢失,除非手动清除数据
sessionStorage:数据在浏览器关闭之后自动清空
作用域不同
cookie:在所有的同源窗口内,数据是共享的
localStorage:也是在所有的同源窗口内,数据是共享的
sessionStorage:不在不同的浏览器窗口中共享,即使是同一个页面
86、对promise的理解
含义:异步编程的一种解决方案,用来解决回调地狱。
三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败) (Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。)
resolved函数作用:将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved)。
reject函数的作用:将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected)。
Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。
then: Promise 实例添加状态改变时的回调函数。可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为rejected时调用。
缺点:无法取消Promise,一旦新建它就会立即执行,无法中途取消。如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
87、你是怎么理解ES6中Proxy的?
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理”某些操作,可以译为“代理器”
88、Vue3与Vue2的区别
1、响应式数据
2、vue2和vue3的响应式原理
3、计算属性Computed
4、监听函数Watch
vue3中watch函数对ref的响应式数据的监听
vue3中watch函数对reactive的响应式数据的监听
5、vue3的生命周期
6、hooks函数
7、toRef和toRefs
8、readonly与shallowReadonly
9、toRaw与MarkRaw
10、组合式API的优势
https://blog.csdn.net/qq_38329392/article/details/126278088移动端点击穿透问题
89、点击穿透
原因:
点击蒙层上的关闭按钮,蒙层消失后触发了按钮下面元素的click事件
如果按钮下面恰好是一个有href属性的a标签,那么页面就会发生跳转
这次没有蒙层,直接点击页内按钮跳转至新页,然后发现新页面中对应位置元素的click事件被触发了.
解决方法:
最优解决方法——只用touch
最简单的解决方案,完美解决点击穿透问题
把页面内所有click全部换成touch事件( touchstart、touchmove、touchend)
90、ssr是什么
Server-Side Rendering
我们称其为SSR
,意为服务端渲染
指由服务侧完成页面的 HTML
结构拼接的页面处理技术,发送到浏览器,然后为其绑定状态与事件,成为完全可交互页面的过程
91、对es6新增的reflect对象的了解
92、null与undefined的区别
1、首先是数据类型不一样
2、null和undefined两者相等,但是当两者做全等比较时,两者又不等。(因为他们的数据类型不一样)
3、转化成数字不同
4、null代表“空”,代表空指针;undefined是定义了没有赋值
93、this指向的理解
-
this总是指向函数的直接调用者(而非间接调用者)
-
如果有new关键字,this指向new出来的那个对象
-
在事件中,this指向目标元素,特殊的是IE的attachEvent中的this总是指向全局对象window
函数类型 | this的指向 |
---|---|
普通函数 | 指向window |
匿名函数 | 指向window |
立即执行函数 | window |
回调函数 | window |
箭头函数 | 函数定义位置的上下文this |
对象下的函数 | 谁调用,指向谁 |
dom回调 | 绑定事件的对象 |
94、什么是递归函数
一个函数在它的函数体内调用它自身称为递归调用,这种函数称为递归函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。
一句话总结递归:自我调用且有完成状态
95、什么是模板字符串
模板字符串只是在JS 中创建字符串的一种新方法。我们可以通过使用反引号使模板字符串化
96、在es5中怎么实现继承
1、原型链继承
2、借用构造函数继承
3、组合继承
4、寄生式组合继承
97、es5与es6的区别
1.es6与es5的类不同
es6引入class,写起来更符合面向对象的编程思维,实际上底层实现的逻辑是一样的。但会极大的简化代码,看起来更加清晰。es6的类有暂时性死区,不可变量提升,因为es6的类必须声明之后才能使用。而es5中不是如此
另外继承类也不同,es6中继承的写法更符合java等面向对象的语言,使用extends关键字继承。而es5中继承写起来比较复杂,不够简洁。
补充点:
es6的类中必须有构造函数,构造函数中可以给予实例属性,另外还有原型方法。在es5中需这样实现:
var Animal=function(属性1,属性2){}
Animal.prototype = {method1:function(){},method2:function(){}}//原型方法
Animal.method3 = function(){}//静态方法
在es6中这样实现
class Animal = {
constructor(){}
method1(){}//原型方法
method2(){}//原型方法
}
2.es6中有promise,以及同步异步async await,可将多层嵌套的回调函数 改为 链式执行,看起来清晰明白
3.增加let,const数据类型,在块级作用域中有效,即必须包含在大括号里,且不可变量提升,即必须声明之后再使用 (var声明的变量与函数声明会被提升到顶部,因此函数与var的变量可在声明之前使用)
什么是块级作用域?就是在大括号里的就是块级作用域,{},比如for循环里。let非常适合for循环,for循环中使用var声明变量,容易受for之外的影响,而影响到数据的正确性。而在for中声明的let变量,只在for循环中有效,且每次for循环的let的变量都是一个新的变量 ,所以可保证数据不受其他影响
作用域:js中原来只有两种作用域,函数作用域和全局作用域, ES6中增加了块级作用域,适用于let和const
4.箭头函数,es6中函数不用写function关键字,用()=>{}就行。
es5中函数写法为
function a(){}
或者
var a = function(){}
es6中可改为箭头写法
var a = (属性1,属性2)=>{代码块} //若代码块中只有一行代码,则大括号{}可以省去
5.es6中可一次性取出数组或object的多个值
var student= {age:18,sex:‘男’,name:‘name1’}
let {age,sex,name} = student
6.函数有默认参数的值,es5中无法设置默认值
function f(p1,p2,p3=false,p4=‘none’){}
7.模版字符串,用反引号标识、、,最主要可以加入变量或表达式,不用再一直用++++来连接字符串了
模板字符串相当于加强版的字符串,用反引号 ,除了作为普通字符串,还可以在字符串中加入变量和表达式。 let info =
my name is ${name}`
98、bus是什么以及如何实现通讯
bus是一种通过事件实现组件交互的通信模式,它借助一个额外的Vue实例作为事件管理中心。任何引入了该Vue实例的组件都处于同一个事件环路内,可以相互注册和触发事件12.什么是事件代理。
- 通过
$emit
触发自定义事件,$emit
第二个参数为传递的数值 - 另一个组件通过
$on
监听自定义事件
99、什么是严格模式
严格模式对正常的 JavaScript 语义做了一些更改:
1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。
2.消除代码运行的一些不安全之处,保证代码运行的安全。
3.提高编译器效率,增加运行速度。
4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名1.js中数组是如何在内存中存储的?
数组不是以一组连续的区域存储在内存中,而是一种哈希映射的形式。它可以通过多种数据结构来实现,其中一种是链表。
js分为基本类型和引用类型:
- 基本类型是保存在栈内存中的简单数据段,它们的值都有固定的大小,保存在栈空间,通过按值访问;
- 引用类型是保存在堆内存中的对象,值大小不固定,栈内存中存放的该对象的访问地址指向堆内存中的对象,JavaScript不允许直接访问堆内存中的位置,因此操作对象时,实际操作对象的引用
100、map和filter有什么区别?
相同点:filter 和 map 都是对数组的操作,均返回一个新的数组
不同点:filter是满足条件的留下,是对原数组的过滤;map则是对原数组的加工,映射成一对一映射的新数组
101、vue模板是如何编译的?
102、如何实现页面跨组件通信?
1、父组件向子组件传递数据 props
2、子组件向父组件传递数据
父: @自定义事件名=“父methods函数”
子: this.$emit(“自定义事件名”, 传值) - 执行父methods里函数代码
$emit绑定一个自定义事件(subprice), 当这个语句被执行时, 就会将参数(index与价格)传递给父组件,父组件通过v-on监听并接收数
3、provide/inject
4、ref/refs
**ref
:**如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例,可以通过实例直接调用组件的方法或访问数据
5、eventBus 事件中心管理组件间的通信
6、localStorage/sessinStorage
7、 a t t r s / attrs/ attrs/listeners
8、Vuex
10.bus是什么以及如何实现通讯
12.什么是事件代理
13.什么是严格模式
103、堆栈的区别
堆(heap)是不连续的内存区域,即数据可以任意存放, 主要存放的是对象等。
栈(stack)是一块连续的内存区域,每个区块按照一定次序存放(后进先出),栈中主要存放的是基本类型的变量的值以及指向堆中的
数组或者对象的地址。
104、js为什么存在数据精度丢失问题
由于计算机的底层是由二进制实现的,有些运算的数字无法全部显示出来。就像一些无理数不能完全显示出来一样,如圆周率 3.1415926…,0.3333… 等。JavaScript遵循IEEE754规范,采用双精度存储(double precision),占用64bit。
1位用来表示符号位,11位用来表示指数,52位表示尾数。
因为在计算机最底层,数值的运算和操作都是采用二进制实现的,所以计算机没有办法精确表示浮点数,而只能用二进制近似相等的去表示浮点数的小数部分
105、如何实现异步传值
106、三次握手,四次挥手
含义:从建立连接到断开连接一共七个步骤,就是三次招手四次挥手
- TCP 建立连接
- 浏览器发送请求命令
- 浏览器发送请求头
- 服务器应答
- 服务器回应信息
- 服务器发送数据
- 断开TCP连接
107、路由懒加载
我们在路由中通常会定义很多不同的页面。如果不应用懒加载的话,很多页面都会打包到同一个js文件中,文件将会异常的大。造成进入首页时,需要加载的内容过多,时间过长,在浏览器中可能会出现短暂的空白页,从而降低用户体验,而运用路由懒加载是将各个模块分开打包,用户查看的时候再加载对应的模块,减少加载用时。
vue实现路由懒加载的三种方式:
-
Vue异步组件
vue-router 配置路由,使用vue的异步组件技术,可以实现懒加载,但是这种情况是一个组件会生成一个js文件。
component:resolve => require([‘需要加载的组件地址’],resolve)
-
ES6的import()
const 组件名 = () => import(‘组件路径’)
-
webpack的require.ensure()
vue-router 配置路由,使用webpack的require.ensure技术,可以实现懒加载,这种情况下,多个路由指定相同的chunkName,会合并打包成一个js文件。
require.ensure可实现按需加载资源,包括js,css等,会给里面require的文件单独打包,不会合主文件打包在一起。
const list = r => require.ensure([],()=>r(require(‘组件地址’)),‘list’)
108、前端状态码
100+:表示连接继续
200+:操作成功收到,分析、接受
300+:完成此请求必须进一步处理,重定向
400+:表示各种客户端错误
500+:表示各种服务器错误es6新增加
109、promise.all和promise.race的理解
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值async/await的理解。
Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
1、all( )
将多个Promise对象 进行整合成一个全新的Promise对象
- 方法接受一个数组作为参数
- 接收的参数中 只有全部都为成功的状态,新Promise对象的状态才为成功
- (逻辑与)其中一个为失败,则新Promise对象的状态为失败,结果返回第一个抛出失败后的值
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result);
const p2 = new Promise((resolve, reject) => {
throw new Error('报错了');
})
.then(result => result);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
2、race( )
将多个Promise对象 进行整合成一个全新的Promise对象
- 接收的参数中 第一个发生状态改变后的值,则为当前新的Promise对象的结果
- 方法接受一个数组作为参数
const p1 = new Promise((res,rej)=>{setTimeout(()=>{
res(1)
})})
const p2 = new Promise((res,rej)=>{res(2)})
const p3 = new Promise((res,rej)=>{rej(3)})
const p = Promise.race([p1,p2,p3])
console.log(p) // 状态为 resolve
110、路由器的两种工作模式
-
对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
-
hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
-
hash模式:
- 地址中永远带着#号,不美观 。
- 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
- 兼容性较好。
-
history模式:
- 地址干净,美观 。
- 兼容性和hash模式相比略差。
- 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。
async怎么执行同步哇
你常用的vue指令有哪些
vue的生命周期
在哪个周期可以访问数据
在哪个周期可以开始调用方法
v-model的原理
vue与react的区别
vue与uniapp的区别
聊聊vue
ajax和axios,以及他们的区别
ajax实现步骤