如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?
第17天要刷的手写题如下:
-
实现图片懒加载
-
实现生成器的执行器函数
-
判断一个函数是不是生成器
下面是我的一些理解:
1. 实现图片懒加载
1.1 使用getBoundingClientRect实现
实现思路:
-
利用闭包保存三个变量imgs、len; 分别表示: 待加载的图片数组;多有图片的数目。
-
闭包返回一个函数,这个函数的作用为:遍历imgs,找到满足加载条件的图片进行加载然后从待加载的数组中移除出去。
-
如果某次lazyLoad执行完毕之后发现待加载的图片数组为空,表示所有的图片都已经加载完毕了,从此再也不需要执行lazyLoad函数了,这个时候就将lazyLoad的触发条件关闭
-
设置scroll事件触发的时候执行lazyLoad函数
const lazyLoad = (
function () {
let imgs = [...document.querySelectorAll("img")];
const len = imgs.length;
return function () {
imgs = imgs.filter(img=>{
const rect = img.getBoundingClientRect();
if(rect.top >= window.innerHeight) return true;
img.src = img.dataset.src;
return false;
});
if (len === imgs.length) document.removeEventListener('scroll', lazyLoad);
}
}
)();
document.addEventListener('scroll', lazyLoad);
1.2 使用IntersectionObserver实现
实现思路:
-
使用IntersectionObserver观察器对每一个img标签进行监控
-
观察器的回调中会通过判断img的intersectionRatio属性判断出此img是否需要加载
-
如果需要加载就在加载之后取消监控
const lazyLoad = function () {
const imgs = [...document.querySelectorAll("img")];
const observer = new IntersectionObserver(
entries => void entries.forEach(
entry => {
if (entry.intersectionRatio > 0) {
const _img = entry.target;
_img.src = _img.dataset.src;
observer.unobserve(_img);
}
}
)
);
imgs.forEach(img => observer.observe(img));
}
2. 实现生成器的执行器函数
这个稍微有些复杂,如果想要彻底掌握一定要在坐地铁的时候多想几遍,本质上并不难,但是平时使用异步函数的机会相对较少,所以有点绕。
写一个函数执行下面的这个生成器,对执行过程不断的进行优化,进而提取出执行生成器的样本函数co
2.0 等待被执行的生成器
function* gFunc () {
let _rst = 0;
_rst = yield Promise.resolve(_rst);
console.log('_rst1',_rst);
_rst = yield Promise.resolve(_rst+1);
console.log('_rst2',_rst);
_rst = yield Promise.resolve(_rst+1);
console.log('_rst3',_rst);
}
2.1 使用同步代码暴力执行
const lowCo = (gFunc) => {
let value;
let done;
const it = gFunc();
({ value, done } = it.next());
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
})
})
})
}
lowCo(gFunc);
* _rst1 0
* _rst2 1
* _rst3 2
*/
2.2 观察代码,发现大量的重复代码,对其进行分层
const lowCo2 = (gFunc) => {
let value;
let done;
const it = gFunc();
({ value, done } = it.next());
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
if(done) return value;
value.then(d=>{
({ value, done } = it.next(d));
})
})
})
}
2.3 使用递归的方式消除重复代码
const lowCo3 = (gFunc) => {
let value;
let done;
const it = gFunc();
const tick = _ => {
({ value, done } = _);
if(done) return value;
value.then(d=>{
tick(it.next(d))
});
}
return tick(it.next());
}
2.4 使用Promise.resolve增加容错
const lowCo4 = (gFunc) => {
let value;
let done;
const it = gFunc();
const tick = _ => {
({ value, done } = _);
if(done) return value;
Promise.resolve(value).then(d=>{
tick(it.next(d))
});
}
return tick(it.next());
}
2.5 将结果promisify
const lowCo5 = (gFunc) => {
let value;
let done;
const it = gFunc();
return new Promise((res,rej)=>{
const tick = _ => {
({ value, done } = _);
if(done) return res(value);
Promise.resolve(value).then(d=>{
tick(it.next(d))
}).catch(e=>rej(e));
}
tick(it.next());
})
}
2.6 感受自由
const co = gFunc => (it = gFunc(),new Promise((res,rej)=>(tick = _ => _.done ? res(_.value): Promise.resolve(_.value).then(d=>tick(it.next(d))).catch(e=>rej(e)),tick(it.next()))));
3. 判断一个函数是不是生成器
先判断是不是函数,再判断其构造函数的名称
const testGenerator = f => typeof f === "function" && f.constructor?.name === "GeneratorFunction";
最后
如果你现在正在找工作,可以私信“web”进群领取前端面试小册以及更多阿里、字节大厂面试真题合集,和p8大佬一起交流。