#前端重铸之路 Day6 🔥🔥🔥🔥🔥🔥

72 阅读5分钟

前置涩话

掘金的小伙伴们大家好,这里是家里蹲选手sin~ 今天是第六天了,马上就要坚持一周了。也是小小得进行了一波简历海投,不出所料,通通遁入虚空,听不到一丝回响 o(╥﹏╥)o 但是没关系,还有好多知识没有学到呢,先坚持完这一周把!加油ヾ(◍°∇°◍)ノ゙

✅ 常见的函数方法手写实现

call、apply及bind

区别: 三者的第一个参数都是this需要指向的对象,但在后续的参数上只有apply是接收一个数组,call和bind的参数用逗号分开。
call和apply直接调用,返回的是一个值,而bind不直接调用,返回的是一个函数形式,执行:foo.bind(obj)()

实现call

Function.prototype.myCall = function(ctx, ...args) {
    ctx = ctx === undefined || ctx === null ? globalThis : Object(ctx);  // ctx一定是对象
    const fn = this;  //  待执行的函数
    //  用symbol作为属性名
    const key = Symbol('temp');
    //  ctx[key] = fn
    Object.defineProperty(ctx, key, {
        enumerable: false,
        value: fn
    });
    const result = ctx[key](...args);
    delete ctx[key];
    return result;
};

第一个参数指定的this打印是包装类型如[string:'a'],所以使用Object()包装(参数归一化,把参数归一成对象一种情况)
ctx里没有fn(),所以不能ctx.fn()。让函数调用this指向ctx,所以用ctx调用,给ctx新加一个不可枚举的symbol属性,值为执行函数

实现bind

Function.prototype.myBind = function(ctx) {
    //var args = Array.prototype.slice.call(argument, 1);
    let args = [...arguments].slice(1);  // 将arguments变成数组并且去掉第一个参数
    let fn = this;
    return function A() {
        // 返回的函数调用时可能会有参数
        let restArgs = [...arguments];
        let allArgs = args.concat(restArgs);
        // 需要判断新函数是否用new调用,new调用实际是new了返回的函数,所以使用原型进行判断  
        if (Object.getPrototypeOf(this) === A.prototype) {
            return new fn(...allArgs);  // es6使用new调用
            
            //下面方法需要完善(手写new实现)
            //var obj = {};
            //Object.setPrototypeOf(obj, fn.prototype);
            //fn.apply(obj, allArgs);
            //return obj;
        } else {
            return fn.apply(ctx, allArgs);
        };
    };
};

高阶函数

使用函数作为另一个函数的参数
封装动画函数、函数防抖、并发队列、参数归一化、惰性函数、分时函数封装、函数柯里化
柯里化函数:固定某一个函数的参数,得到函数剩余参数的新函数,如果没有剩余参数则调用。

函数防抖(回城)

  • 频繁调用某一个函数
  • 造成效率问题
  • 结果以最后一次为准
function debounce(func, duration = 500) {
    let timerId;
    return function(...args) {
      clearTimeout(timerId);
      timerId = setTimeout(() => {
        func.apply(this, args);
      },duration);
    };
};

函数节流(技能CD)

  • 频繁调用某一个函数
  • 造成效率问题
  • 设定时间内只出一次结果
function throttle(callback, time, immediately = true) {
    if (immediately) {  //立即执行一次
        let t
        return function() {
            if (!t || Date.now() - t >= time) {
                callback.apply(null, arguments)
                t = Date.now
            }
        }
    } else {
        let timer
        return function(...args) {
            if (timer) return
            timer = setTimeout(() => {
                callback.apply(this, args)
                timer = null
            }, time)
        }
    }
}

并发队列

function paralleTask(tasks, paralleCount = 2) {
  return new Promise((resolve) => {
      if (tasks.length === 0) {
        resolve();
        return
      }
      let nextIndex = 0;
      let finishCount = 0;
      function _run() {
        //运行下一个任务
        const task = tasks[nextIndex];
        nextIndex++;
        task().then(() => {
            finishCount++;
            if (nextIndex <tasks.length) {
                _run();
            } else if (finishCount === tasks.length) {
                resolve();
            };
        });
      };
      for (let i = 0; i < paralleCount && i <= tasks.length; i++) {
          _run();
      };
  });
};

封装分时函数

const chunkSplitor = (task) => {
    setTimeout(() => {
        task((time) => time < 16);
    }, 30);
};

function performChunk(datas, consumer, chunkSplitor) {
    if (typeof datas === 'number') {
        datas = new Array(datas);
    }
    if (datas.length === 0) {
        return
    };
    if (!chunkSplitor && globalThis.requestIdleCallback) {
        chunkSplitor = (task) => {
            requestIdleCallback((idle) => {
                task(() => idle.timeRemaining());
            });
        };
    };
    let i = 0;  //目前应该取出的任务下标
    //执行一块任务
    function _run() {
        if (i === datas.length) {
            return
        };
        chunkSplitor((hasTime) => {
            const now = Date.now();
            while (hasTime(Date.now() - now) && i < datas.length) {
                //在这一帧还有空闲时间
                const item = datas[i];
                consumer(item, i);
                i++;
            };
            _run();
        });
    };
    _run();
};

封装动画函数

function animation(duration, from, to, onProgress) {
  const dis = to - from;
  const speed = dis / duration;
  const startTime = Date.now();
  let value = from;
  onProgress(value);

  function _run() {
    const now = Date.now();
    const time = now - startTime;
    if (time >= duration) {
        value = to;
        onProgress(value);
        return
    }
    const d = time * speed;
    value = from + d;
    onProgress(value);
    requestAnimationFrame(_run);
  };
  requestAnimationFrame(_run);
};

统计函数 array.filter(fn).length

function countBy(array, generateKey) {
    const result = {};
    for (const u of array) {
        const key = generateKey(u);
        if (result[key]) {
            result[key]++;
        } else {
            result[key] = 1;
        };
    };
    return result;
};

数据分组

function groupBy(arr, generateKey) {
    if (typeof generateKey === 'string') {
        const propName = generateKey;
        generateKey = (item) => item[propName];
  };
  const result = {};
  for (const item of arr) {
      const key = generateKey(item);
      if (!result[key]) {
          result[key] = [];
      };
      result[key].push(item);
  };
  return result;
};

深度克隆

function deepclone(value) {
    const cache = new WeakMap();
    function _deepclone(value) {
        if (value === null || typeof value !== 'object') {
            return value;
        };
        if (cache.has(value)) {
            return cache.get(value);
        };
        const result = Array.isArray(value) ? [] : {};
        Object.setPrototypeOf(result, Object.getPrototypeOf(value));
        cache.set(value, result);
        for (const key in value) {
            if (value.hasOwnProperty(key)) {
                result[key] = _deepclone(value[key]);
            };
        };
        return result;
    };
    return _deepclone(value);
};

柯里化函数

function curry(func) {
    //得到从下标1开始的参数
    let args = Array.prototype.slice.call(arguments, 1);
    let that = this;
    return function() {
        let curArgs = Array.from(arguments); //当前调用的参数
        let totalArgs = args.concat(curArgs);
        if (totalArgs.length >= func.length) {
            //参数数量够
            return func.apply(null, totalArgs);
        } else {
            //参数数量不够
            totalArgs.unshift(func);
            return that.curry.apply(that, totalArgs);
        };
    };
};

惰性函数

function createCopyText() {
  if (navigator.clipboard) {
    return (text) => {
      navigator.clipboard.writeText(text);
    };
  } else {
      return (text) => {
          const input = document.createElement('input');
          input.setAttribute('value', text);
          document.body.appendChild(input);
          input.select();
          document.execCommand('copy');
          document.body.removeChild(input);
      };
  };
};

const copyText = createCopyText();

实现Promise

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
    #state = PENDING
    #result = undefined
    #handlers = []

    constructor(executor) {
        const resolve = (data) => {
            this.#changeState(FULFILLED, data)
        }
        const reject = (reason) => {
            this.#changeState(REJECTED, reason)
        }
        try{
            executor(resolve, reject)
        }
        catch(err) {
            reject(err)
        }  //无法捕获异步错误
        
    }

    #changeState(state, result) {
        if(this.#state !== PENDING) return
        this.#state = state
        this.#result = result
        this.#run()
    }

    #isPromise(value) {
      return (
          value !== null &&
          (typeof value === 'object' || typeof value === 'function') &&
          typeof value.then === 'function'
      )
    }

    #runMicroTask(func) {
        // node环境
        // process.nextTick(func)
        if (typeof process !== 'underfined' && typeof process.nextTick === 'function') {
            return process.nextTick(func)
        } else if (typeof MutationObserver === 'function') {
            // 浏览器环境
            // MutationObserver
            const ob = new MutationObserver(func)
            const textNode = document.createTextNode()
            ob.observe(textNode, {
                characterData: true
            })
            textNode.data = '2'
        } else {
            setTimeout(func, 0)
        }
        
    }

    #runOne(callback, resolve, reject) {
        //this.#runMicroTask(() => {
            if (typeof callback !== 'function') {
                const settled = this.#state === FULFILLED ? resolve : reject
                settled(this.#result)
                return
            }
            try {
                const data = callback(this.#result)
                if (this.#isPromise(data)) {
                    data.then(resolve, reject)
                } else {
                    resolve(data)
                }
            } catch (err) {
                reject(err)
            }
        //})
    }

    #run() {
        if (this.#state === PENDING) return
        while (this.#handles.length) {
            const { onFulfilled, onRejected, resolve, reject } = this.#handlers.shift()
            if (this.#state === FULFILLED) {
                this.#runOne(onFulfilled, resolve, reject)
            } else {
                this.#runOne(onRejected, resolve, reject)
            }
        }
    }

    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            this.#handlers.push({
             onFulfilled,
             onRejected,
             resolve,
             reject
            })
            this.#run()
        })
    }
}

Promise.myAll = function(proms){
    let res, rej
    const p = new Promise((resolve, reject)=>{
        res = resolve
        rej = reject
    })
    let i = 0
    let fulfilled = 0
    const result = []
    for (let prom of proms) {
        const index = i
        i++
        Promise.resolve(prom).then((data)=>{
            //1.把完成的数据汇总到最终结果
            result[index] = data
            //2.判定是否全部完成
            fulfilled++
            if (fulfilled === i) {
                res(result)
            }
        }, rej)
    }
    if (i === 0) {
        res([])
    }
    return p;
}

片尾涩话

<{=....(嘎嘎~) 今天这部分可烧脑了,你们学会了吗 反正我是学废了︿( ̄︶ ̄)︿