–记录到进大厂为止
CSS
position
- static: 默认。就是自然顺序,从上到下,从左到右,爱着你排列;
- absolute:绝对。原点是:最近的非static的父(上级)元素。
- dom是一棵树。当前节点的父节点如果是absolute或relative,那么原点就是父节点,如果不是,那么就找父亲的父亲,而不是找父亲的兄弟,或自己的兄弟。
- fixed: 固定。相对于浏览器。
- relative:相对。相对于正常文档流中的自己。
- inherit:继承父元素
JS
手写防抖和节流
防抖
基本函数
// 初级防抖函数
function debounce(fn,delay){
let timer;
return function(...args){
clearTimeout(timer);
timer=setTimeout(()=>fn.apply(this,args),delay);
}
}
// 自定义
let d=debounce(()=>console.log("excute fu"),100);
// 抖动5次,只会执行最后一次
for(let i=0;i<5;i++){
d();
}
// 输出
// excute fu
防抖是什么?防止抖动。如果一直抖动,只执行最后一次。
闭包的应用
- 什么是闭包?函数+函数访问的变量。特点是可以访问函数外面的变量;其实debounce本质上返回的是一个函数定义,也就是下面的d函数,显然,d函数访问了d函数外的timer,那么d函数和timer构成了闭包。这也是实现防抖的关键。
- timer变量,只要有函数引用,就不会消失。五次抖动中(循环),每次循环,都返回了debounce的d函数,我们可以对应成d1,d2,d3,d4,d5;实际上di都访问了timer,然后,di+1会清除掉di创建的timer,并且将timer指向新的timer,其实也就是覆盖。由此,只有最后一次会生效。
function debounce(fn,delay){
let timer;
let d=function(...args){
clearTimeout(timer);
timer=setTimeout(()=>fn.apply(this,args),delay);
}
return d;
}
进阶函数
- 怎么实现立刻执行?上面的实现效果是,n次抖动中,只执行第n次,并且,是在第n次发生delay时间后执行fn函数。那么,如果希望n次抖动中,执行且仅执行第1次呢?也就是所谓的立马执行。
// 进阶
function debounce(fn,delay,immedate){
let timer;
let d=function(...args){
clearTimeout(timer);
if(immedate){
let callNow=!timer; // callNow只有第一次会为真
timer=setTimeout(()=>timer=null,delay);// delay有没有都无所谓,不影响,这里只是说最后一次抖动完之后,delay时间后,再彻底清除掉timer。
if(callNow){
fn.apply(this,args);
}
}else{
timer=setTimeout(()=>fn.apply(this,args),delay);
}
}
return d;
}
let d=debounce(()=>console.log("excute fu"),100,true);
for(let i=0;i<5;i++){
d();
}
- callNow控制第一次执行。上述逻辑,只有d1的时候,callNow是真,因为timer初始状态为null;d2,d3,d4,d5的时候timer都不会为null;因为
timer=setTimeout(()=>timer=null,delay);// delay有没有都无所谓,不影响,这里只是说最后一次抖动完之后,delay时间后,再彻底清除掉timer。
等到最后一次,也就是d5执行了,隔delay后,清除timer;
节流基本函数
节流定义
节流就是在一定时间内只执行一次。
防抖:执行最后一次 vs 节流:每隔一定时间内执行且只执行一次
// 基础
function throttle(fn,await){
let timer;
return function (...args) {
if(!timer){
timer=setTimeout(()=>{
fn.apply(this,args);
timer=null; // 不能用clearTimeout清除定时器;因为只是暂停了,打印timer还是会有值,那么if(!timer)等于没用了
},await);
}
}
}
- 有timer就略过,没有timer就指定新的定时器;唯一需要注意的可能就是timer=null,而不是clearTimeout(timer);当然,为了防止内存泄露,也是可以加上句话的。
进阶函数
- 节流一般问的可能就是,定时器不一定准确,那么你怎么控制?
- 首先,定时器为什么不准确。
- 浏览器本身因素,设备熄屏计时器可能会暂停啊什么的
- 事件循环机制
- setTimeout属于宏任务。计时器结束之后的函数会进入宏任务队列。宏任务队列之前可能有其他函数。
由此可见,首先是你定时器本来就不准,其次是就算准确了,进入队列,也有可能需要等其他任务执行完了,才能执行定时器的回调函数。那么,我们至少可以控制第一个因素,定时器本身就不准确——时间戳解决。
function throttle2(fn,awaitt) {
let timer;
let start=Date.now();
return function (...args) {
let now=Date.now();
let remain=awaitt-(now-start);
clearTimeout(timer); // 如果有程序一直调的话,需要清除上一次产生的定时器
if(remain<=0){ // 到时间了,直接执行;
fn.apply(this,args);
start=Date.now(); // 重置;
}
else{ // 如果函数只被触发了一次
timer=setTimeout(fn,remain); // 还剩remian时间到达指定的awaitt时间长度;
}
}
}