js手写

77 阅读2分钟
//************************************************实现数组拍平
//es5递归
function flatten(arr) {
    var result = []
    for (var i = 0; i++; i < arr.length) {
        if (Array.isArray(arr[i])) {
            result = result.concat(flatten(arr[i]))
        } else {
            result.push(arr[i])
        }
    }
    return result;
}

//es6
function flatten(arr) {
    while (arr.some((item) => Array.isarray(item))) {
        arr = arr.concat(...arr)
    }
    return arr;
}

function flatten(arr) {

}

//*********************************************事件总线(发布订阅模式)
class EventEmitter {
    constructor() {
        this.cache = {}
    }

    on(name, fn) {
        if (this.cache[name]) {
            this.cache[name].push(fn)
        } else {
            this.cache[name] = [fn]
        }
    }

    off(name, fn) {
        let tasks = this.cache[name]
        if (tasks) {
            const index = tasks.findIndex(f => f === fn)
            index >= 0 && tasks.splice(index, 1)
        }
    }

    emit(name, once = false, ...args) {
        if (this.cache[name]) {
            // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
            let tasks = this.cache[name].slice()
            for (let fn of tasks) {
                debugger;
                fn(...args)
            }
            if (once) {
                delete this.cache[name]
            }
        }
    }
}

//死循环举例:
const emitter = new EventEmitter();

function callback() {
    console.log('Callback function');
    emitter.on('event', callback);
    //此时这里注册相同事件,在emit的for of循环里,如果循环的不是副本而是this.cache[name]
    //则注册完this.catch[name]长度为2(又注册了一个),会继续循环,造成死循环
}

emitter.on('event', callback);
emitter.emit('event');




//*****************************************解析 URL 参数为对象
function parseParam(url) {
    const paramStr = /.+\?(.+)/exec(url)[1]
    const paramArr = paramStr.split('&')
    let paramsObj = {}
    paramArr.forEach(item => {
        if (/=/.test(item)) {
            const [key, val] = item.split('=')
            val = decodeURIComponent(val);
            val = /^\d+$/.test(val) ? ParseFloat(val) : val;

            if (paramsObj.hasOwnPropoty(key)) {
                paramsObj = [].concat(paramsObj[key], val)
            } else {
                paramsObj[key] = val
            }
        } else {
            paramsObj[item] = true;
        }

    })
    return paramsObj;
}





//*********************************************字符串模板

function render(template, data) {
    const reg = /\{\{(\w+)\}\}/;
    if (reg.test(template)) {
        const name = reg.exec(template)[1]
        template = template.replace(reg, data[name])
        return render(template, data)
    }
    return template;
}

//测试:
let template = '我是{{name}},年龄{{age}},性别{{sex}}';
let person = {
    name: '布兰',
    age: 12
}
render(template, person); // 我是布兰,年龄12,性别undefined



//************************************************图片懒加载
//-----------法1:document.addEventListener('scroll', imageLazyLoad1)

let imgList = [...document.querySelectorAll('img')]
let length = imgList.length

const imgLazyLoad = (function () {
    let count = 0

    return function () {
        let deleteIndexList = []
        imgList.forEach((img, index) => {
            let rect = img.getBoundingClientRect()
            if (rect.top < window.innerHeight) {
                img.src = img.dataset.src
                deleteIndexList.push(index)
                count++
                if (count === length) {
                    document.removeEventListener('scroll', imgLazyLoad)
                }
            }
        })
        imgList = imgList.filter((img, index) => !deleteIndexList.includes(index))
    }
})()

document.addEventListener('scroll', imgLazyLoad)
//这里最好加防抖:
// 定义一个防抖函数
function debounce(func, delay) {
    let timer;
    return function () {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(this, arguments);
        }, delay);
    };
}
//或es6:箭头函数虽然没有arguments但可以用rest
const debounce = (func, delay) => {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => {
            func.apply(this, args);
        }, delay);
    };
};

//对应节流:
function throttle(func, delay) {
    let timer;
    return function () {
        if (!timer) {
            timer = setTimeout(() => {
                func.apply(this, arguments);
                timer = null;
            }, delay);
        }
    };
}




// 创建一个经过防抖处理的函数
const debouncedImgLazyLoad = debounce(imgLazyLoad, 200);

// 添加防抖处理后的事件监听
document.addEventListener('scroll', debouncedImgLazyLoad);



//--------------法2:images.forEach(item => { imageObserver.observe(item) })
//图片懒加载,dataset.src为真实地址

images.forEach(item => {
    item.dataset.src = item.src
    item.removeAttribute('src')
    imgObserver.observer(item)
})


const imgObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach(item => {
        if (!item.isIntersection) return
        const img = item.target
        const src = img.dataset.src
        if (!src) return
        img.setAttribute('src', src)
        observer.unobserver(img)
    })
})