防抖
防抖的应用场景是用户点击一个按钮多次会触发多次操作,其实用户目的只是触发一次操作。这样控制每一次点击操作的时间间隔的函数叫防抖。防抖根据不同场景也可以细化为可以立即执行的防抖与延时执行的防抖。
简单的防抖
function debounce(func, delay = 200) {
let timer;
return function (...args) {
let th = this;
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(() => {
timer = null;
func.apply(th, args);
}, delay);
}
}
立即执行的防抖函数
function debounce(func, wait = 200, immediate = false) {
let timer;
return function (...args) {
let that = this;
if (timer) {
clearTimeout(timer);
} else if (immediate) {
func.apply(that, args);
}
timer = setTimeout(() => {
timer = null;
if (!immediate) {
func.apply(that, args);
}
}, wait);
}
}
节流
节流函数就是将多次操作合并为一次定时触发
function throttle(func, wait) {
let now = +new Date();
let last;
return function (...args) {
let th = this;
if (last && last - now < wait) {
clearTimeout(timer);
timer = setTimeout(() => {
last = +new Date();
func.apply(this, args);
}, wait);
} else {
timer = null;
func.apply(this, args);
}
}
页面穿透(300毫秒延时)
点击穿透
由于是移动浏览器默认支持双击放大页面的操作,当单击第一次时会有一个300ms的判定是不是双击事件。另外在移动端会有touch事件与click事件的混用也会造成点击穿透(因为触发顺序touchstart-》touchmove-》touchend-》click)
解决方案:
- 头部标签禁止用双击缩放
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scaleble = no" />
或
<meta name="viewport" content="width=device-width"/>
- click事件或touch事件统一
- 点击手350ms再切换页面
- 阻止事件继续冒泡
- 垫个遮罩层
- 改变css属性
pointer-events
e.preventDefault();//阻止事件默认行为
e.stopPropagation();//阻止事件冒泡
滚动穿透
暂时掠过
深拷贝与浅拷贝
对于引用类型(对象或数组)的数据可能会出现修改同一个对象的现象,复制的时候只把地址传递过去,而不是创建新的对象
浅拷贝
通过=赋值或object.assign
let a = {
a: 'a'
};
console.log(a['a']); => a
let b = a;
b['a'] = 'b';
console.log(a['a']); => b
console.log(b['a']); => b
或
let a = {
a: [1,2,3]
};
console.log(a['a']); => [ 1, 2, 3 ]
let b = {};
Object.assign(b,a);
b['a'][0] = 'b';
console.log(a['a']); => [ 'b', 2, 3 ]
console.log(b['a']); => [ 'b', 2, 3 ]
深拷贝
对于对象或者嵌套对象可以创建新的空间进行存储
- json方法实现
let a = {'a':{
b:{
c:'c'
}
}};
let c = JSON.parse(JSON.stringify(a));
console.log(c == a); => false
c['a']['b']['c'] = 'a';
console.log(a); => { a: { b: { c: 'c' } } }
console.log(c); => { a: { b: { c: 'a' } } }
- 自己实现深拷贝方法(广度优先与深度优先)
function deepClone(data) {
let obj;
if(Array.isArray(data)){
obj = [];
} else if(typeof data == "object"){
obj = {};
} else {
return data
}
if(Array.isArray(data))
{
for (let index = 0; index < data.length; index++) {
obj.push(deepClone(data[index]));
}
} else{
for (const key in data) {
obj[key] = deepClone(data[key]);
}
}
return obj;
}
let a = {'a':{
b:{
c:'c'
}
}};
console.log(deepClone(a));
- messageChanel
function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
var obj = {
a: 1,
b: {
c: obj
}
}
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
async function func() {
const clone = await structuralClone(obj);
console.log(clone);
}
func();