防抖和节流
防抖和节流是闭包的实际应用
防抖函数
当持续触发事件 一定时间内没有再触发事件 事件处理函数才会执行一次 如果设定的时间到来之前 又一次触发了事件 就重新开始延时
如: 键盘一直输入内容 键盘抬起了并且满足1秒内没有再次输入
内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>防抖函数</title>
</head>
<body>
<input type="text" id="input">
<script>
// document.querySelector('#input').addEventListener('keyup',function(e){ debounce(1000,e.target.value)})
// let timer = null
// function debounce(delay,value){
// clearTimeout(timer)
// timer =setTimeout(function(){
// console.log(value);
// },delay)
// }
// 一直在内存当中 内存泄漏 闭包
// 内存泄露是指你用不到(访问不到)的变量,依然占居着内存空间,不能被再次利用起来。
// 函数柯里化
//以上完成了基本需求但是我们定义了一个全局变量,基于尽可能少的使用全局变量
// 我们将变量放入debounce函数的内部
document.querySelector('#input').addEventListener('keyup',function(e){
debounceFuc(e.target.value)})
var debounceFuc = debounce(1000,fn)
// 前面为什么要用callback是因为可以封装 不然写死了 用一个防抖就得重复写 callback灵活
function debounce(delay,callback){
let timer = null
return function(value){
clearTimeout(timer)
timer = setTimeout(function(){
// console.log(value);
callback(value)
},delay)
}
}
function fn(value){
console.log(value);
}
// 这里是基于闭包实现的,按钮 element 监听了 click 事件,并执行 debounce 方法,debounce 方法又返回一个新函数
// ,这里的 timer 就会一直存在,不被销毁,因为函数被外部引用。
// 所以下次点击的时候 timer 还是存在,我们点击的时候首先会 clearTimeout,也就是取消上次的请求
</script>
</body>
</html>
实际应用场景
使用echarts时,改变浏览器宽度的时候,希望重新渲染
echarts的图像,可以使用此函数,提升性能(虽然echarts里有自带的resize函数)
典型案例 :输入搜索 输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时
解决搜索的bug
节流函数
当持续触发事件的时候 保证一段时间内 只调用一次事件处理函数
一段时间内 只做一件事
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>节流函数</title>
</head>
<body>
<button id="button">点击</button>
<script>
document.querySelector('#button').onclick = throttle(handle,2000)
let timeOut = null
function throttle(func,wait){
return function(){
if(!timeOut){
timeOut = setTimeout(function(){
func()
timeOut = null
},wait)
}
}
}
function handle(){
console.log(Math.random());
}
</script>
</body>
</html>
实际应用场景
典型案例:鼠标不断点击触发,规定在n秒内多次点击只有一次生效
防抖节流优化图片懒加载
防抖-优化不断触发的窗口变化
重新计算可见区域的高度
节流-实现性能较好的懒加载
滑动时图片的显示
登录验证
传统的登录验证方案
分布式下,同域单点登录的session同步解决方案
1.IPhash
最原始的方式是iphash方法。通过IP值计算hash值,第一次该客户的的请求由代理服务器nginx发送到了系统A,则以后每次只要是该IP的哈希值,均发给系统A。这样做虽然能解决问题,但是是伪负载均衡,如果系统A挂了,则会造成系统瘫痪,该用户无法正常使用,会产生单点故障。此方法早已被淘汰。
2.session复制
集群服务器的代码逻辑都是相同的。当用户在系统A登录后,其登录信息保存在session域中。集群服务器之间可以进行通讯,进行session同步。说明白点就是将自己的session复制给其他服务器,保证所有正常工作的集群的session域的数据一模一样,让大家都知道这个用户登陆了。
3.中间存储媒介
第三种方法是在第二种方法的基础上改进的。不用每个集群都保存一份数据,而是在每个服务器处理完之后,将数据保存到一个第三方的存储结构。所有的服务器都会从第三方存储结构中去拿数据。一般使用非关系型数据库Redis作为第三方存储。
如图所示,不管是分布式服务器还是集群服务器,在用户登录之后会生成一个可以唯一确定客户端的id,依次id为键,以该用户为值将客户端的登录信息存到Redis中,然后会以Token的形式返回key值给客户端。当该客户的在此发起请求时携带Token一起发送,无论哪个服务器处理此次请求,都会从Token中拿到uuid,然后去Redis中查找该用户的信息。如此便可实现单点登录。
4.总结
Session同步技术使得不管是集群服务器,还是分布式服务器,都可以共享用户登录信息。给单点登录的实现贡献了浓墨重彩的一笔。
基于token身份认证方案就是第三种 中间存储媒介解决方案
jwt验证方案
现在用的最多的还是jwt方案
预编译
作用域创建的阶段就是预编译的阶段
预编译的时候做了那些事
js的变量对象 AO对象 供js引擎自己去访问的
预编译的四个步骤
1 创建ao对象
2 找形参和变量的声明 作为ao对象的属性名 值是undefined
3 实参和形参相统一
4 找函数声明 (函数声明会覆盖变量声明)
this
在函数中直接使用
函数作为对象的方法被调用(谁调用我 我就指向谁)
上面的写法是call写法的语法糖
箭头函数
手写实现call apply
call apply作用 区别 理解 实际开发场景
改变this指向 参数区别call单向数据 apply数组
js继承 原型链继承 构造函数继承 call实现
js数据类型
es5 伪数组转化成数组
实战中使用call的场景
- 实现js继承
- 判断复杂数据类型
- es5伪数组转化为数组
console.log(Object.prototype.toString.call({
})==='[object Object]');
console.log(Object.prototype.toString.call([])==='[object Array]');
function get() {
console.log(arguments);
console.log([...arguments]); //es6
console.log(Array.prototype.slice.call(arguments)); //es5
}
get(1, 2, 3)
call的作用 改变this指向
var person = {
getName:function(){
return this.name
}
}
var person1 = {
name:'zhangyue'
}
console.log(person.getName.call(person1));
实现手写call
tips: JS—arguments对象
arguments 是一个类数组对象(伪数组)。代表传给一个function的参数列表。
var person = {
getName:function(){
return this.name
}
}
var person1 = {
name:'zhangyue'
}
// 参数接收的 改变this指向的参数
Function.prototype.myCall = function(context){
// 这里面的this是谁 是getname 因为谁调用this就指向谁
// 这里的this必须是一个function
if(typeof this !== 'function'){
throw new Error('error')
}
// context = context || window 传了就是context 不传就是window
// 考虑参数 拿到除了第一个参数之外的参数
var args = [...arguments].slice(1)
console.log(args);
context.fn = this
var result = context.fn(args)
return result
}
console.log(person.getName.myCall(person1,1,2,3));
事件的循环机制 even-loop
js语言特点
- 单线程
- 解释性语言
event-loop事件循环机制 由三部分组成
- 调用栈
- 微任务队列
- 消息队列
even-loop开始时会从全局一行一行的执行 遇到函数调用 会压入到调用栈当中 被压入的函数被称之为帧 当函数返回后会从调用栈中弹出
js中的异步操作 比如 fetch setTimeout setInterval 压入到调用栈中的时候里面的消息会进入到消息队列中去 消息队列中的会等到调用栈清空后再执行
promise async await的异步操作的时候会加入到微任务中去 会在调用栈清空时立即执行
调用栈中加入的微任务会立马执行
BFC
BFC的理解
块级格式化上下文,它是指一个独立的块级渲染区域,只有Block-level BOX(块级盒子)参与,该区域拥有一套渲染规则来约束块级盒子的布局,且与外部区域无关
从一个现象开始说起
- 一个盒子不设置height,当内容元素都浮动时,无法撑起自身
那么这个盒子没有bfc
如何创建BFC
- float的 不为 none
- position 不为 static或relative
- display 为 iinline-block,flex,inline-flex
- overflow 为 hidden
BFC的其他作用
- 取消盒子margin塌陷
- 阻止元素被浮动元素覆盖
margin塌陷
想要的是子盒子距离父盒子有个上边距,可是父盒子一起被带下来了
父元素身上加上overflow: hidden; 开启BFC 就解决了margin塌陷的问题
阻止元素被浮动元素覆盖
蓝色的浮动元素遮挡了红色盒子,给红色设置overflow:hidden;解决被覆盖的问题
深拷贝浅拷贝
前置知识
数据存储
- 一般数据类型
number string boolean null undefined Symbol - 引用数据类型
object array Set Map
一般数据类型都是存储在栈里面的 包括它的名字和值
而复杂类型 比如对象 它的名字是放在栈里面的但是它真正的内容是放在堆里面的
栈里面只是存放了它的引用
赋值和浅拷贝的区别
赋值 赋值的对象和原来的对象引用的是同一个,改变当前的原对象也会变
浅拷贝
- 创建的是一个新对象
- 拷贝值(如果属性是一般数据类型 拷贝的就是基本类型的值 ; 如果属性是引用数据类型 那么拷贝的就是内存的地址)
var num = 123
var person = {
name:'张三'}
var person1 = person
person1.name = '李四'
console.log(person.name); //李四
console.log(person1.name); //李四
// 上面的代码是浅拷贝吗?
// 不是浅拷贝 上面的操作是赋值
// 浅拷贝
// 创建一个新的对象
// 层次的理解
// 拷贝值 (如果属性是一般数据类型 拷贝的是基本数据类型
// 如果属性是引用数据类型 那么拷贝的就是内存的地址)
// 说的直白一点 浅拷贝如果碰到的是引用数据类型 那么拷贝前和拷贝后是相互影响的
// 深拷贝
// 如果属性是引用类型,那么就会开辟一个新的空间
var person3 = {
name1:'张三',name2:'张三222',name3:'张三333',hobby:['篮球','足球']}
function shallowClone(source){
var newObj = {
}
for (var i in source){
// console.log('i',i);
if(source.hasOwnProperty(i)){
newObj[i] = source[i]
}
}
return newObj
}
person4 = shallowClone(person3)
// person5 = deepClone(person3)
person4.name1 = '李四'
person4.hobby[1] = '冰球'
// person4.hobby = '冰球'
// console.log(person5);
console.log(person4);
console.log(person3);
// 这里使用的hasOwnProperty的原因是:
// for in 会遍历到原型链上的属性,所以要用这个判断,遍历到的属性是不是确实在source对象上面的
// hasOwnProperty(属性名)确认该属性是否是其对象的自有属性,若是则返回true
深浅拷贝的应用场景
vue增删改查中的修改用到了深浅拷贝
前后端交互跨域处理
代理必须弄清楚弄明白
代理后地址前面会多出/api 有两种解决方案
1 后端接口前加/api
2 前端加上rewrite
utils - storage.js 里面是对localstorge的封装
vuex是持久化存储吗 页面刷新后会丢失吗
优化条件语句大厂面试题
function printAnimalsDetails({
type,name,gender}={
}){
if(!type) return 'no animal type'
if (!name) return 'no animal name'
if(!gender) return 'no animal gender'
return `${
name} is ${
gender} - ${
type}`
}
console.log(printAnimalsDetails({
name:'zhangsan'}));
console.log(printAnimalsDetails({
name:'zhangsan',type:'gg',gender:'nan'}));
console.log(printAnimalsDetails());
tips: vscode如何设置ctrl+滚轮缩放