手写
深拷贝
- 深拷贝 定义一个深拷贝函数
- 函数接收一个需要拷贝的对象
- 根据传入的值判断类型需要创建一个对象还是数组来储存拷贝的值
- 使用typeof判断传来的数据是否是function类型是的话直接返回 因为我们遇到函数一般都是直接使用不一个不会重新创建一个造成内存浪费
- 然后接着对传入的值进行判断首先值不为null并且typeof类型为object 因为null typeof 也是object
- 如果条件为真对传入的对象进行for in 循环 用新创建的对象或数组去接收再次调用深拷贝函数deepCopy的返回值
- 如果条件为false则说明此数据类型为基本数据类型直接返回
- 最后将我们新创建的对象或者数组返回出去
function deepCopy(originValue) {
let newObj = Array.isArray(originValue) ? [] : {}
if (typeof originValue == 'function') {
return originValue
}
if (originValue != null && typeof originValue == 'object') {
for (const key in originValue) {
newObj[key] = deepCopy(originValue[key])
}
} else {
return originValue
}
return newObj
}
let obj = {
name: 'yuyss',
age: 18,
info: {
num: 999
},
arr: [{ name: 'oyss' }],
foo: function () {
console.log('foo')
}
}
let obj1 = deepCopy(obj)
obj.name = 'ss'
obj.info.num = 188
console.log(obj)
console.log(obj1)
function deepClone(originValue, map = new WeakMap()) {
// 判断是否是一个Set类型
if (originValue instanceof Set) {
return new Set([...originValue])
}
// 判断是否是一个Map类型
if (originValue instanceof Map) {
return new Map([...originValue])
}
// 判断如果是Symbol的value, 那么创建一个新的Symbol
if (typeof originValue === "symbol") {
return Symbol(originValue.description)
}
// 判断如果是函数类型, 那么直接使用同一个函数
if (typeof originValue === "function") {
return originValue
}
// 判断传入的originValue是否是一个对象类型
if (!isObject(originValue)) {
return originValue
}
if (map.has(originValue)) {
return map.get(originValue)
}
// 判断传入的对象是数组, 还是对象
const newObject = Array.isArray(originValue) ? [] : {}
map.set(originValue, newObject)
for (const key in originValue) {
newObject[key] = deepClone(originValue[key], map)
}
// 对Symbol的key进行特殊的处理
const symbolKeys = Object.getOwnPropertySymbols(originValue)
for (const sKey of symbolKeys) {
// const newSKey = Symbol(sKey.description)
newObject[sKey] = deepClone(originValue[sKey], map)
}
return newObject
}
防抖节流
- 定义一个防抖的函数
- 函数接收2个参数 1.要被防抖的函数 2.防抖的时间
- 定义一个timer变量用于记录定时器执行
- 然后在执行被防抖的函数前我们需要对我们的timer变量进行一个判断 有值的话就把上一次的定时器事件清除
- 然后将我们新的定时器赋值给timer 定时器里面就是我们要执行的防抖函数事件 然后在执行完毕防抖函数后可以把timer恢复初始状态值
优化this指向和传递参数问题 因为我们自定义的节流函数返回的新函数最终会绑定到我们的事件上,所以我们返回函数的this是指向我们触发的事件上 但是我们需要被执行的节流函数的this因为传参等于重新赋值的问题导致this丢失 我们可以再执行需要被执行的节流函数的时候进行显示绑定让this重新正确的执行
//基本实现
function debounce(fn, delay) {
let timer = null
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, args)
timer = null
}, delay)
}
}
let input = document.querySelector('.input')
let cancel = document.querySelector('.cancel')
input.oninput = debounce(function (evens) {
console.log(this.value, evens)
}, 1000)
//完整版本
function debounce(fn, delay, leading = false) {
let timer = null
let isLeading = false
let _debounce = function (...args) {
return new Promise((resolve, reject) => {
try {
let res = undefined
if (leading && !isLeading) {
res = fn.apply(this, args)
resolve(res)
timer = null
isLeading = true
}
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
res = fn.apply(this, args)
resolve(res)
timer = null
isLeading = false
}, delay)
} catch (error) {
reject(err)
}
})
}
_debounce.cancel = function () {
if (timer) clearTimeout(timer)
timer = null
isLeading = false
}
return _debounce
}
const input = document.querySelector('.input');
const cancel = document.querySelector('.cancel');
const debounceFn = debounce(function (event, age) {
console.log(this.value, event, age)
return '返回值'
}, 1000, true)
debounceFn(null, 18).then(res => {
console.log(res)
})
input.oninput = debounceFn
cancel.onclick = function () {
debounceFn.cancel()
}
节流
- 定义一个节流函数
- 函数接收2个参数,1.需要被节流的函数 2.节流的时间
- 外层定义一个最后执行函数时间的变量lastTime初始值为0
- 返回一个被触发事件执行的新函数
- 定义一个变量记录当前最新时间nowTime=new Date().getTime()
- 然后进行判断 最新时间的变量nowTime减去最后执行函数事件的变量lastTime是否大于我们传递需要执行的时间interval
- 如果大于就执行需要被节流的函数 并且把最后执行函数的时间变量lastTime进行赋值为最新时间nowTime
//基本实现
function throttle(fn, interval) {
let lastTime = 0
return function (...args) {
let nowTime = new Date().getTime()
if (nowTime - lastTime > interval) {
fn.apply(this, args)
lastTime = nowTime
}
}
}
let input = document.querySelector('.input')
let cancel = document.querySelector('.cancel')
input.oninput = throttle(function (evens) {
console.log(this.value, evens)
}, 2000)
//全部功能
function throttle(fn, interval, leading = true, trailing = false) {
let lastTime = 0
let timer = null
const _throttle = function (...args) {
return new Promise((resolve, reject) => {
const nowTime = new Date().getTime()
//立即执行进行控制
if (!leading && lastTime === 0) {
lastTime = nowTime
}
const waitTime = interval - (nowTime - lastTime)
if (waitTime <= 0) {
if (timer) clearTimeout(timer)
const res = fn.apply(this, args)
console.log(1)
resolve(res)
lastTime = nowTime
timer = null
}
//尾部执行进行控制
if (trailing && !timer) {
timer = setTimeout(() => {
const res = fn.apply(this, args)
console.log(2)
resolve(res)
lastTime = new Date().getTime()
timer = null
}, waitTime)
}
})
_throttle.cancel = function () {
clearTimeout(timer);
timer = null;
}
}
return _throttle
}
let input = document.querySelector('.input')
let cancel = document.querySelector('.cancel')
let throttleFn = throttle(function (evens, name) {
console.log(evens, name)
return '返回值'
}, 2000, false, true)
input.oninput = throttleFn
cancel.onclick = function () {
throttleFn.cancel()
}
throttleFn(null, 'oyss').then(res => {
console.log(res)
})
bind、call、apply
call、apply、bind作用是改变函数执行时的上下文,简而言之就是显示改变函数运行时的this指向
apply 以数组的形式传入 改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
call 跟apply一样,改变this指向后原函数会立即执行,且此方法只是临时改变this指向一次
bind 改变this指向后不会立即执行,而是返回一个永久改变this指向的函数
- 给函数原型对象上添加一个共同的方法 myCall/myApply myCall/myApply函数接收2个参数 1.需要指向的object 2.需要传递的参数
- 对传入的类型进行判断转换确保传入的一定是对象类型 判断传入的值不是null或者undefined就有Object()方法对数据进行包装是的话就返回一个window
- 谁调用函数this就是拿到当前函数 给新传入的对象加一个fn的属性赋值this
- 调用新传入对象的fn属性并且把外部传递的参数接着传递过去
- 然后删除传入对象的fn属性
- 给函数原型对象上添加一个共同的方法 myBind函数接收2个参数 1.需要指向的object 2.需要传递的参数
- 对传入的类型进行判断转换确保传入的一定是对象类型 判断传入的值不是null或者undefined就有Object()方法对数据进行包装是的话就返回一个window
- 谁调用函数this就是拿到当前函数 给新传入的对象加一个fn的属性赋值this
- 返回一个新的函数新 函数接收返回后的新函数调用的参数newArgs
- 调用新传入对象的fn属性并且把外部传递的和返回的新函数传递的参数合并传递过去
//call
function foo(name, age) {
console.log(this, name, age)
}
Function.prototype.myCall = function (thisArg, ...args) {
thisArg = thisArg === null || thisArg === undefined ? window : Object(thisArg)
thisArg.fn = this
// Object.defineProperty(thisArg, 'fn', {
// enumerable: false,
// configurable: true,
// value: this
// })
thisArg.fn(...args)
delete thisArg.fn
}
foo.myCall({ name: 'oyss' }, 'yuyss', 18)
//apply
function foo(arr) {
console.log(this, arr)
}
Function.prototype.myApply = function (thisArg, ...args) {
thisArg = thisArg === null || thisArg === undefined ? window : Object(thisArg)
thisArg.fn = this
// Object.defineProperty(thisArg, 'fn', {
// enumerable: false,
// configurable: true,
// value: this
// })
thisArg.fn(...args)
delete thisArg.fn
}
foo.myApply({ name: 'oyss' }, ['yuyss', 18])
//bind
Function.prototype.myBind = function (thisArg, ...args) {
thisArg.fn = this
return (...newArgs) => {
thisArg.fn(...args, ...newArgs)
}
}
function foo(name, age, num, a) {
console.log(name, age, num, a, this)
}
let result = foo.myBind({ name: 'oyss' }, 'yuyss', 18)
result(188, '深圳市')
迭代器函数封装
let arr = ['abc', 'cba', 'nbc']
function createArrayIterator(arr) {
let index = 0
return {
next: function() {
if (index < arr.length) {
return { done: false, value: arr[index++] }
} else {
return { done: true, value: undefined }
}
}
}
}
const friendsIterator = createArrayIterator(friends)
console.log(friendsIterator.next())
console.log(friendsIterator.next())
console.log(friendsIterator.next())
console.log(friendsIterator.next())
利用闭包实现函数缓存
memoize
函数是一个高阶函数,它接受一个函数func
作为参数,并返回一个新的函数。- 在
memoize
函数内部,我们定义了一个cache
对象,用于存储函数的计算结果。这个cache
对象是在memoize
函数执行时创建的,并且在返回的新函数中可以访问到它。这就是闭包的体现。 - 返回的新函数会接受任意数量的参数
...args
。我们将这些参数转换为字符串作为cache
对象的键。 - 在每次调用新函数时,我们首先检查
cache
对象中是否已经存在该键。如果存在,则直接返回缓存的结果。如果不存在,则调用原始函数func
,并将计算结果存储在cache
对象中。
function memoize(func){
const cache = {}
return function (...args){
const key = JSON.stringify(args)
if(cache[key]==undefined){
cache[key]=func(...args)
}
}
}
function foo(a,b){
console.log(a+b)
}
const result = memoize(foo)
result(1,2)
result(1,2)
eventBus
- 创建中央事件总线:在 Vue 应用中通常会创建一个专门用于事件发布和订阅的中央事件总线实例,通常被命名为 EventBus 或 eventBus。
- 注册事件:组件可以在中央事件总线上注册自己感兴趣的事件,包括事件的名称和对应的回调函数。
- 发布事件:当某个组件触发了特定的事件时,它会通过中央事件总线发布该事件,同时传递需要传递的数据。
- 订阅事件:其他组件可以在中央事件总线上订阅这个事件,并在事件发生时执行对应的回调函数,以应该事件。
1.创建一个pubsub对象
2.pubsub对象里有一个保存函数的属性evens值为对象
3.创建一个on属性值为函数用于定义事件
4.on函数接收2个参数1.事件的名称,回调函数
- 判断一下evens对象里是否有传入的evenName属性没有的话让传入的evenName=[]
- 然后往evens对象的evenName推进push传入的回调函数
5.创建一个emit属性值为函数用于发布事件
6.emit接收2个参数1.事件的名称2.传递的参数data
- 判断一下event对象中是否有evenName有的话对evenName进行遍历调用每一项并传递参数data
7.创建一个off属性值为函数用于取消事件订阅
- 判断一下event对象中是否有evenName有的话对该数组进行过滤filter判断一下不需要取消的回调函数
const pubsub = {
evens:{},
//订阅事件
on:function (eventName,callback){
if(!this.evens[eventName]){
this.evens[eventName]=[]
}
this.evens[eventName].push(callback)
},
//发布事件
emit:function (eventName,data){
if(this.evens[eventName]){
this.evens[eventName].forEach(callback=> {
callback(data)
})
}
},
//取消订阅
off:function (eventName,callback) {
if(this.evens[eventName]){
this.evens[eventName]=this.evens[eventName].filter(cb => cb !==callback)
}
}
}
//使用
const fn = data =>{
console.log(data)
}
pubsub.on('foo',fn)
pubsub.emit('foo','你好event-bus')
pubsub.off('foo',fn)
递归求1-100总和
function foo(num1, num2) {
if (num2 > 100) {
return num1
} else {
return foo(num1 + num2, num2 + 1)
}
}
console.log(foo(1, 2))
function sumFrom1ToN(n) {
if (n === 1) {
return 1; // 基本情况:当n为1时,总和为1
} else {
return n + sumFrom1ToN(n - 1); // 递归调用,规模减小,继续求和
}
}
// 调用函数计算1到100的总和
const result = sumFrom1ToN(100);
console.log(result); // 输出5050
斐波那契
function fn(num){
if(num==0||num==1) return 1
return fn(num-1)+fn(num-2)
}
fn(10)
一维数组转多维
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9]//=> [[1, 2, 3],[4, 5, 6],[7, 8, 9]]
function fn(arr,num){
let newArr=[]
for(let i=0;i<arr.length;i+=num){
newArr.push(arr.slice(i,i+num)
}
return newArr
}
fn(arr,3)
计算器(Counter)类,它能使用加法、减法、乘法、除法的操作,并且有一个能获取结果(get)的方法,并通过可选链式调用而工作。
class Counter {
result = 0
addition(val) {
this.result += val
return this
}
subtraction(val) {
this.result -= val
return this
}
multiplication(val) {
this.result *= val
return this
}
division(val) {
this.result /= val
return this
}
get() {
return this.result
}
}
function counter() {
let result = 0;
function addition(val) {
result += val;
return this;
}
function subtraction(val) {
result -= val;
return this;
}
function multiplication(val) {
result *= val;
return this;
}
function division(val) {
result /= val;
return this;
}
function get() {
return result;
}
return { addition, subtraction, multiplication, division, get };
}
数字数组组合
// 请设计一个 combinations 函数,它接收一个数字数组,要求返回这些数字的所有可能组合情况。
function combinations(nums) {
const result = [[]]
for (let n of nums) {
const length = result.length
for (let i = 0; i < length; i++) {
result.push([...result[i], n])
}
}
return result
}
// [[], [1]];
// [[], [1], [1, 2], [2]];
// [[], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]];
console.log(combinations([1]))
console.log(combinations([1, 2]))
console.log(combinations([1, 2, 3]))
情景
[] == ![]结果是什么?
== 中,左右两边都需要转换为数字然后进行比较。 []转换为数字为0。 ![] 首先是转换为布尔值,由于[]作为一个引用类型转换为布尔值为true, 因此![]为false,进而在转换成数字,变为0。 0 == 0 , 结果为true
文案少的时候是居中的,文案多的时候是换行左对齐的
/*当文字为一行是,则P的宽度小于div的宽度,p标签居中显示在盒子内,文字也就居中了 ;当大于一行时,P的宽度和div的宽度是一致的 ,文字就居左对齐了*/
.content {
width: 200px;
border: 1px solid #ee2415;
text-align: center;
padding: 2px 5px
}
/*display: inline-block使P的宽度根据文字的宽度伸缩 */
.content p {
text-align: left;
display: inline-block
}
无感登录
- 在相应其中拦截,判断token返回过期后,调用刷新token的接口
- 后端返回过期时间,前端判断token的过期时间,去调用刷新token的接口
- 写定时器,定时刷新token接口
方法1流程
- 登录成功后保存token 和 refresh_token
- 在响应拦截器中对401状态码引入刷新token的api方法调用
- 替换保存本地新的token
- 把错误对象里token替换
- 再次发送未完成的请求
- 如果refresh_token过期了,判断是否过期,过期了就清楚所有token重新登录
发送多次请求只需要最后一次结果
fetch:使用AbortController
是一个原生的API,可以用来取消一个或多个Web请求。
AbortController 实例用于取消请求:通过将 AbortController 实例的 signal 属性传递给 fetch 方法的 signal 参数,可以在调用 AbortController 的 abort 方法时取消对应的 fetch 请求。
let currentController = new AbortController();
signal属性主要用于与当前控制器相关联的信号对象,这个信号对象可以用来中止或取消 fetch 请求。
const { signal } = currentController;
fetch(url, { signal }).then((res)=>{})
在 Fetch API 中,并没有内置的 abort() 方法用于取消请求,但我们可以利用 new AbortController 来实现取消 Fetch 请求的功能 currentController.abort()
currentController.abort();
在 fetch 请求的 .catch() 中,可以对取消的请求进行特殊处理,一般会抛出一个名为 'AbortError' 的异常,用于识别请求是否被取消
if (error.name === ‘AbortError’) {
console.log(‘请求被取消:’, url);
}
axios:使用 canceltoken 取消请求
CancelToken.source()
是 axios library 中用来创建取消令牌(CancelToken)的静态方法。
-
创建取消令牌:调用
CancelToken.source()
方法会返回一个包含token
和cancel
两个属性的对象。token
是一个取消令牌实例,而cancel
是一个函数,调用该函数将取消与该取消令牌相关联的请求。 -
取消请求:将
token
属性传递给 axios 请求的cancelToken
配置项中,当需要取消请求时,调用cancel
函数即可取消该请求。
下面我们首先使用 CancelToken.source()
创建了一个取消令牌,并从中获取了取消函数 cancel
和取消令牌实例 token
。然后将 token
传递给 axios 请求的配置中。最后,在某个条件满足时,调用 cancel()
方法来取消请求
可以使用 CancelToken.source 工厂方法创建 cancel token,像这样:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function(thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// 处理错误
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');
还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// executor 函数接收一个 cancel 函数作为参数
cancel = c;
})
});
// cancel the request
cancel();
注意: 可以使用同一个 cancel token 取消多个请求
单点登录
单点登录前端的完整流程包括多个系统的登录、同域名下的单点登录、不同域名下的单点登录等。在现代企业应用中,用户往往需要在多个系统间进行切换,单点登录(Single Sign-On,简称SSO)技术使得用户仅需登录一次,即可访问所有相互信任的应用系统。下面将详细探讨单点登录前端的完整流程:
-
多个系统的登录:
- 当用户首次访问需要登录的子系统时,如果未登录,则会被重定向到SSO认证中心。
- 用户在SSO认证中心输入用户名和密码后,认证中心验证信息无误,会生成一个认证凭证(如Token或Cookie)。
- 认证中心将用户重定向回最初的请求地址(子系统),并附带上认证凭证。
-
同域名下的单点登录:
- 在同域名下实现SSO相对简单,主要利用了Cookie可以跨子域共享的特性。用户在SSO认证中心完成登录后,认证中心将认证凭证存储在根域的Cookie中。
- 之后,当用户访问同一域名下的任一子系统时,子系统可以通过读取根域的Cookie来验证用户的登录状态,从而实现自动登录。
-
不同域名下的单点登录:
- 对于不同域名下的SSO,由于Cookie无法跨域共享,因此需要借助一些技术手段来实现。一种常见的方法是使用CAS(Central Authentication Service)协议。
- 在这种模式下,用户首次访问子系统时,由于没有登录,会被重定向到SSO认证中心。认证中心完成登录后,会生成一个Service Ticket(ST),并通过回调的方式将ST发送给子系统。
- 子系统收到ST后,将其发送给SSO认证中心进行验证。验证通过后,子系统信任该ST,并允许用户访问受保护的资源。
-
前端实现细节:
- 前端在实现SSO时,需要注意处理来自认证中心的回调。这通常涉及到解析回调参数,提取认证凭证,然后将其存储在本地(如LocalStorage或SessionStorage)以供后续请求使用。
- 前端还需要确保每次向后端发送请求时,都携带了必要的认证信息(如Token或Cookie),以便后端服务验证用户的身份。
- 对于不同域名下的SSO,前端可能需要使用一些跨域技术,如iframe+postMessage,来实现在不同域下共享认证信息。
总的来说,单点登录前端的完整流程涉及到多个系统的协同工作,包括认证中心的登录、认证凭证的生成与传递、以及前端对认证信息的存储与管理。通过这些流程的紧密配合,可以实现用户在多系统间无缝切换的便捷体验。
把一个 div
做成宽高比 4:3
的矩形,距离屏幕两边有 50px
的距离
padding
的尺寸可以根据元素的宽度进行计算,所以只需要保证 padding-top
和 width
的比例是 4:3
就行了。
width:100%;
margin:0 50px;
padding-top:75%
provide和inject实现页面刷新
1 . 用vue-router重新路由到当前页面,页面是不进行刷新的
2 . 采用window.reload(),或者router.go(0)刷新时,整个浏览器进行了重新加载,闪烁,体验不好
我们只在控制路由的组件中写一个函数 使用 v-if 控制 router-view 的显示隐藏 v-if重新加载组件走生命周期, 然后把这个函数传递给后代,然后在后代组件中调用这个方法即可刷新路由啦。
<router-view v-if="isShowRouter"/>
export default {
name: 'App',
provide(){
return{
reload:this.reload
}
},
data(){
return{
isShowRouter:true,
}
},
methods:{
reload(){
this.isShowRouter = false;
this.$nextTick(()=>{
this.isShowRouter = true;
})
}
}
}
//后代组件
export default {
inject:['reload'],
}
??上拉加载,下拉刷新
上拉加载及下拉刷新都依赖于用户交互 什么时机下触发交互动作 上拉加载的本质是页面触底,或者快要触底时的动作 下拉刷新的本质是页面本身置于顶部时,用户下拉时需要触发的动作
localStorage 或 sessionStorage变响应式
在 Vue 中,如果直接使用本地存储 localStorage 或 sessionStorage 存储数据,这些数据并不是响应式的,也就是说当本地存储的数据发生变化时,视图不会自动更新。为了将本地存储的数据变成响应式,可以结合 Vue 提供的响应式数据和生命周期钩子函数来实现。以下是一种可能的做法:
-
在 Vue 组件中,可以在
data
钩子中声明一个用于存储本地数据的变量,并在created
钩子中读取本地存储的数据将其赋值给该变量。 -
使用 Vue 提供的计算属性来实现对本地存储数据的访问,并在需要时更新视图。
-
在本地存储数据变化时,手动更新 Vue 组件中的响应式数据,以触发视图更新。
export default {
data() {
return {
localStorageData: ''
};
},
computed: {
localStorageValue() {
return localStorage.getItem('myData');
}
},
created() {
this.localStorageData = localStorage.getItem('myData');
},
methods: {
handleLocalStorageChange(value) {
this.localStorageData = value;
}
},
watch: {
localStorageValue(newVal) {
localStorage.setItem('myData', newVal);
}
}
};
首先在 data
钩子中声明了一个变量 localStorageData
用来存储本地数据。在 created
钩子中从本地存储读取数据并赋值给这个变量。利用计算属性 localStorageValue
来实现对本地存储数据的访问。在监听计算属性中的 localStorageValue
的变化时,通过 watch
实现数据变化时更新本地存储的操作
项目两小时未动未发送请求,退出登录
设置 请求的token在请求头中 请求后最新的token在返回头中
java后台服务 需要拦截器 每次请求查看前端heards里边有没有你设置的token如果有而且没有过期是正确的 那就重新生成最新的token 放在返回的heards里边
在vue的main.js中写入拦截器 拦截所有请求 查看返回的heards里边是否存在token 如果存在更新你本地的token
methods: {
testTime() {
this.currentTime = new Date().getTime(); //更新当前时间
if (this.currentTime - parseInt(localStorage.getItem('lastTime')) > this.timeOut) { //判断是否超时(超时自动退出登录)
this.cleanAllCache(); //清除所有缓存数据
//下面就可以按自己的方式跳转到登录页
window.location.href = `${process.env.VUE_APP_OAUTH_URL}${this.OAUTH}logout?redirect_uri=${window.location.origin}${this.FOLDER_SUFFIX}`;
}
},
setLastTime() {
localStorage.setItem('lastTime', new Date().getTime().toString());//更新操作时间
}
}
created() {
//保存上次进去的时间戳
this.setLastTime();
//用定时器监听是否长时间未操作
window.setInterval(this.testTime, 5 * 1000);
}
收集
在浏览器中一天只能弹出一个弹窗,如何实现,说一下你的思路?
要在浏览器中实现一天只能弹出一个弹窗的功能,可以使用本地存储(localStorage)来记录弹窗状态。下面是一种实现方案:
- 当页面加载时,检查本地存储中是否已存在弹窗状态的标记。
- 如果标记不存在或者标记表示上一次弹窗是在前一天,则显示弹窗并更新本地存储中的标记为当前日期。
- 如果标记存在且表示上一次弹窗是在当天,则不显示弹窗。
// 检查弹窗状态的函数
function checkPopupStatus() {
// 获取当前日期
const currentDate = new Date().toDateString();
// 从本地存储中获取弹窗状态标记
const popupStatus = localStorage.getItem('popupStatus');
// 如果标记不存在或者标记表示上一次弹窗是在前一天
if (!popupStatus || popupStatus !== currentDate) {
// 显示弹窗
displayPopup();
// 更新本地存储中的标记为当前日期
localStorage.setItem('popupStatus', currentDate);
}
}
// 显示弹窗的函数
function displayPopup() {
// 在这里编写显示弹窗的逻辑,可以是通过修改 DOM 元素显示弹窗,或者调用自定义的弹窗组件等
console.log('弹出弹窗');
}
// 在页面加载时调用检查弹窗状态的函数
checkPopupStatus();
在这个实现中,checkPopupStatus
函数会在页面加载时被调用。它首先获取当前日期,并从本地存储中获取弹窗状态的标记。如果标记不存在或者表示上一次弹窗是在前一天,就会调用 displayPopup
函数显示弹窗,并更新本地存储中的标记为当前日期。
通过这种方式,就可以确保在同一天只能弹出一个弹窗,而在后续的页面加载中不会重复弹窗。
立即执行函数打印
var a = 1;
(function a () {
a = 2;
console.log(a);
})();
立即调用的函数表达式(IIFE) 有一个 自己独立的 作用域,如果函数名称与内部变量名称冲突,就会永远执行函数本身;所以上面的结果输出是函数本身;
全局作用域局部作用域
(function () {
var a = (b = 5);
})();
console.log(b);//5
console.log(a);//err:a is not defined
在这段代码中,变量 a
使用了一个赋值表达式 (b = 5)
,这个表达式实际上是两个操作的组合:赋值操作和赋值表达式。
- 在
(b = 5)
中,赋值操作b = 5
将会将5
赋值给一个全局变量b
,因为在这里b
没有使用var
、let
或const
进行声明,所以它会成为全局变量。 - 而在赋值表达式
(b = 5)
中,整个表达式的值为5
,所以a
的值被赋为5
。
由于 a
是在一个函数内部使用 var
声明的局部变量,它的作用域仅限于这个函数内部。因此,当在函数外部尝试访问变量 a
时,会报错,因为 a
不在外部的作用域中可见。
另一方面,变量 b
是一个全局变量,因此在函数外部可以访问并打印它的值。所以 console.log(b)
将会输出 5
,而 console.log(a)
将会报错,因为 a
在外部是不可见的。
typeof
var foo = function bar(){ return 12; };
console.log(typeof bar());
答案
输出是抛出异常,bar is not defined。
解析
这种命名函数表达式函数只能在函数体内有效
var foo = function bar(){ // foo is visible here // bar is visible here console.log(typeof bar()); // Work here :) }; // foo is visible here // bar is undefined here
var x=1;
if(function f(){}){
x += typeof f;
}
console.log(x)//1 undefined
解析
条件判断为假的情况有:0,false,'',null,undefined,未定义对象。函数声明写在运算符中,其为true,但放在运算符中的函数声明在执行阶段是找不到的。另外,对未声明的变量执行typeOf不会报错,会返回undefined
instanceof
function f(){
return f;
}
console.log(new f() instanceof f);//false
函数 f
返回的是函数本身 f
,而不是一个新创建的对象。因此,new f()
返回的是构造函数 f
的实例,但实际上是指向函数本身的。由于函数和它的实例是不同的对象,所以 new f() instanceof f
的结果是 false
。