每日前端手写题--day17

以下题目来自掘金等其它博客,但是问题的答案都是根据笔者自己的理解做出的。如果你最近想要换工作或者巩固一下自己的前端知识基础,不妨和我一起参与到每日刷题的过程中来,如何?

第17天要刷的手写题如下:

  1. 实现图片懒加载
  2. 实现生成器的执行器函数
  3. 判断一个函数是不是生成器

下面是我的一些理解:

1. 实现图片懒加载

1.1 使用getBoundingClientRect实现

实现思路:

  1. 利用闭包保存三个变量imgs、len; 分别表示: 待加载的图片数组;多有图片的数目。
  2. 闭包返回一个函数,这个函数的作用为:遍历imgs,找到满足加载条件的图片进行加载然后从待加载的数组中移除出去。
  3. 如果某次lazyLoad执行完毕之后发现待加载的图片数组为空,表示所有的图片都已经加载完毕了,从此再也不需要执行lazyLoad函数了,这个时候就将lazyLoad的触发条件关闭
  4. 设置scroll事件触发的时候执行lazyLoad函数
js 复制代码
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实现

实现思路:

  1. 使用IntersectionObserver观察器对每一个img标签进行监控
  2. 观察器的回调中会通过判断img的intersectionRatio属性判断出此img是否需要加载
  3. 如果需要加载就在加载之后取消监控
js 复制代码
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 等待被执行的生成器

js 复制代码
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 使用同步代码暴力执行

js 复制代码
// 写一份同步代码执行完即可
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 观察代码,发现大量的重复代码,对其进行分层

js 复制代码
const lowCo2 = (gFunc) => {
    let value;
    let done;
    const it = gFunc();

    // layer1
    ({ value, done } = it.next());
    if(done) return value;
    value.then(d=>{
        // layer2
        ({ value, done } = it.next(d));
        if(done) return value;
        value.then(d=>{
            // layer3
            ({ value, done } = it.next(d));
            if(done) return value;
            value.then(d=>{
                ({ value, done } = it.next(d));
            })
        })
    })
}

2.3 使用递归的方式消除重复代码

js 复制代码
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增加容错

js 复制代码
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

js 复制代码
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 感受自由

js 复制代码
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. 判断一个函数是不是生成器

先判断是不是函数,再判断其构造函数的名称

js 复制代码
const testGenerator = f => typeof f === "function" && f.constructor?.name === "GeneratorFunction";
相关推荐
一颗花生米。10 分钟前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐0114 分钟前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio199515 分钟前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&1 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈1 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水2 小时前
简洁之道 - React Hook Form
前端
正小安4 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay5 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch6 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光6 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js