常见的js手写

3/13/2022 1001 阅读需要5分钟
0
0
AI总结
本文总结了JavaScript中常见的编程技巧和实现方法,包括防抖、节流、new操作符、call/apply/bind、发布订阅模式、instanceOf、Promise、async/await、深拷贝和数组扁平化。防抖和节流用于控制函数执行的频率,new操作符用于创建对象并链接原型链。call、apply和bind用于改变函数执行时的上下文。发布订阅模式用于事件管理,instanceOf用于判断对象的原型链。Promise和async/await用于处理异步操作。深拷贝用于复制复杂对象,数组扁平化用于将多维数组转换为一维数组。这些技巧和实现方法在JavaScript开发中非常实用,能够提高代码的效率和可维护性。

1.防抖

作用:停止动作后多久才开始执行函数

function debounce (fn, delay = 300, imm) {
    let timer

    return function () {
        if (imm) {
            fn.apply(this, arguments)
        }
        if (timer) {
            clearTimeout(timer)
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments)
        }, delay)
    }
}

2.节流

多久执行一次

function throttle (fn, delay = 300) {
    let lastTime
    return function () {
        const now = +new Date()
        if (now - lastTime > wait) {
            fn.apply(this, arguments)
        }
    }
}

3.new

new操作符做了这些事:

1.它创建了一个全新的对象。

2.它会被执行[[Prototype]](也就是__proto__)链接。

3.它使this指向新创建的对象。。

4.通过new创建的每个对象将最终被[[Prototype]]链接到这个函数的prototype对象上。 如果函数没有返回对象类型Object(包含Functoin, Array, Date, RegExg, Error),那么new表达式中的函数调用将返回该对象引用。


function MyNew (func, ...args) {
    const obj = {}

    if (func.prototype !== null) {
        obj.__proto__ = func.prototype;
    }

    let res = func.call(obj, ...args)

    if (res && (typeof obj === 'object' || typeof obj === 'function')) {
        return res
    }

    return obj
}

4.call, apply, bind

Function.prototype.call2 = function (ctx = globalThis) {
    ctx.fn = this

    const res = ctx.fn(...args)

    delete ctx.fn

    return res
}

5.发布订阅


function E () {}

E.prototype = {
    event: {},
    on: function (ename, fn) {
        if (this.event[ename]) {
            this.event[ename].push(fn)
        } else {
            this.event[ename] = [fn]
        }
    },
    
    remove: function (ename) {
        delete this.events[ename]
    },

    emit: function (ename) {
        this.events[ename].forEach(fn => {
            fn.apply(this, args)
        })
    }
}


6.instanceOf

function myInstanceOf(obj, constructor) {
  let proto = Object.getPrototypeOf(obj);
  while (proto) {
    if (proto === constructor.prototype) {
      return true;
    }
    proto = Object.getPrototypeOf(proto);
  }
  return false;
}

7.Promise

class MyPromise {
  constructor(executor) {
    this.state = 'pending';
    this.value = undefined;
    this.reason = undefined;
    this.onFulfilledCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.state === 'pending') {
        this.state = 'fulfilled';
        this.value = value;
        this.onFulfilledCallbacks.forEach((callback) => callback(this.value));
      }
    };

    const reject = (reason) => {
      if (this.state === 'pending') {
        this.state = 'rejected';
        this.reason = reason;
        this.onRejectedCallbacks.forEach((callback) => callback(this.reason));
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function'? onFulfilled : (value) => value;
    onRejected = typeof onRejected === 'function'? onRejected : (reason) => {
      throw reason;
    };

    let promise2;
    if (this.state === 'fulfilled') {
      return (promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }));
    } else if (this.state === 'rejected') {
      return (promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      }));
    } else {
      return (promise2 = new MyPromise((resolve, reject) => {
        this.onFulfilledCallbacks.push((value) => {
          setTimeout(() => {
            try {
              let x = onFulfilled(value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push((reason) => {
          setTimeout(() => {
            try {
              let x = onRejected(reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error);
            }
          }, 0);
        });
      }));
    }
  }

  catch(onRejected) {
    return this.then(null, onRejected);
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  let called;
  if (x instanceof MyPromise) {
    if (x.state === 'pending') {
      x.then((y) => {
        resolvePromise(promise2, y, resolve, reject);
      }, reject);
    } else {
      x.then(resolve, reject);
    }
  } else if (x!== null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}

8.async/await

function asyncToGenerator(generatorFunc) {
    return function() {
      const gen = generatorFunc.apply(this, arguments)
      return new Promise((resolve, reject) => {
        function step(key, arg) {
          let generatorResult
          try {
            generatorResult = gen[key](arg)
          } catch (error) {
            return reject(error)
          }
          const { value, done } = generatorResult
          if (done) {
            return resolve(value)
          } else {
            return Promise.resolve(value).then(val => step('next', val), err => step('throw', err))
          }
        }
        step("next")
      })
    }
}

9.深拷贝

简单实现一下


const isObject = o => Object.prototype.toString.call(o) === '[object Object]'

const isArray = o => Object.prototype.toString.call(o) === '[object Array]'

const isDate = o => Object.prototype.toString.call(o) === '[object Date]'

const isNull = o => o === null
function deepClone (Obj) {
    if (typeof Obj !== 'object') return Obj
    let cloneObj = isArray(Obj[key]) ? [] : {}
    for (const key in Obj) {
        if (isObject(Obj[key]) || isArray(Obj[key])) {
            cloneObj[key] = deepClone(Obj[key])
        } else if (isDate(Obj[key])) {
            cloneObj[key] = new Date(Obj[key])
        } else {
            cloneObj[key] = Obj[key]
        }
    }

    return cloneObj
}

10.数组flatten扁平化

function flatten (arr, n = 1) {
    if (Array.isArray(arr)) {
        throw Error('First argument must be an array')
    }

    if (!arr.length) return []
    let count = 0
    
    let res = []
    const loop = a => {
        res = []
        a.forEach(item => {
            if (Array.isArray(item)) {
                res = res.concat([...item])
            } else {
                res.push(item)
            }
        })

        count++

        if (count < n) {
            loop(res)
        }
    }

    loop(arr)

    return res
}