1. 防抖

function debounce(func, ms = 1000) {  let timer;  return function (...args) {    if (timer) {      clearTimeout(timer)    }    timer = setTimeout(() => {      func.apply(this, args)    }, ms)  }}// 测试const task = () => { console.log('run task') }const debounceTask = debounce(task, 1000)window.addEventListener('scroll', debounceTask)

2. 节流

function throttle(func, ms = 1000) {  let canRun = true  return function (...args) {    if (!canRun) return    canRun = false    setTimeout(() => {      func.apply(this, args)      canRun = true    }, ms)  }}// 测试const task = () => { console.log('run task') }const throttleTask = throttle(task, 1000)window.addEventListener('scroll', throttleTask)

3. new

function myNew(Func, ...args) {  const instance = {};  if (Func.prototype) {    Object.setPrototypeOf(instance, Func.prototype)  }  const res = Func.apply(instance, args)  if (typeof res === "function" || (typeof res === "object" && res !== null)) {    return res  }  return instance}// 测试function Person(name) {  this.name = name}Person.prototype.sayName = function() {  console.log(`My name is ${this.name}`)}const me = myNew(Person, 'Jack')me.sayName()console.log(me)

4. bind

Function.prototype.myBind = function (context = globalThis) {  const fn = this  const args = Array.from(arguments).slice(1)  const newFunc = function () {    const newArgs = args.concat(...arguments)    if (this instanceof newFunc) {      // 通过 new 调用,绑定 this 为实例对象      fn.apply(this, newArgs)    } else {      // 通过一般函数模式调用,绑定 context      fn.apply(context, newArgs)    }  }  // 反对 new 调用形式  newFunc.prototype = Object.create(fn.prototype)  return newFunc}// 测试const me = { name: 'Jack' }const other = { name: 'Jackson' }function say() {  console.log(`My name is ${this.name || 'default'}`);}const meSay = say.bind(me)meSay()const otherSay = say.bind(other)otherSay()

5. call

Function.prototype.myCall = function (context = globalThis) {  // 关键步骤,在 context 上调用办法,触发 this 绑定为 context,应用 Symbol 避免原有属性的笼罩  const key = Symbol('key')  context[key] = this  let args = [].slice.call(arguments, 1)  let res = context[key](...args)  delete context[key]  return res};// 测试const me = { name: 'Jack' }function say() {  console.log(`My name is ${this.name || 'default'}`);}say.myCall(me)

6. apply

Function.prototype.myApply = function (context = globalThis) {  // 关键步骤,在 context 上调用办法,触发 this 绑定为 context,应用 Symbol 避免原有属性的笼罩  const key = Symbol('key')  context[key] = this  let res  if (arguments[1]) {    res = context[key](...arguments[1])  } else {    res = context[key]()  }  delete context[key]  return res}// 测试const me = { name: 'Jack' }function say() {  console.log(`My name is ${this.name || 'default'}`);}say.myApply(me)

7. deepCopy

function deepCopy(obj, cache = new WeakMap()) {  if (!obj instanceof Object) return obj  // 避免循环援用  if (cache.get(obj)) return cache.get(obj)  // 反对函数  if (obj instanceof Function) {    return function () {      obj.apply(this, arguments)    }  }  // 反对日期  if (obj instanceof Date) return new Date(obj)  // 反对正则对象  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)  // 还能够减少其余对象,比方:Map, Set等,依据状况判断减少即可,面试点到为止就能够了  // 数组是 key 为数字素银的非凡对象  const res = Array.isArray(obj) ? [] : {}  // 缓存 copy 的对象,用于解决循环援用的状况  cache.set(obj, res)  Object.keys(obj).forEach((key) => {    if (obj[key] instanceof Object) {      res[key] = deepCopy(obj[key], cache)    } else {      res[key] = obj[key]    }  });  return res}// 测试const source = {  name: 'Jack',  meta: {    age: 12,    birth: new Date('1997-10-10'),    ary: [1, 2, { a: 1 }],    say() {      console.log('Hello');    }  }}source.source = sourceconst newObj = deepCopy(source)console.log(newObj.meta.ary[2] === source.meta.ary[2]);

8. 事件总线 | 公布订阅模式

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) {    const tasks = this.cache[name]    if (tasks) {      const index = tasks.findIndex((f) => f === fn || f.callback === fn)      if (index >= 0) {        tasks.splice(index, 1)      }    }  }  emit(name) {    if (this.cache[name]) {      // 创立正本,如果回调函数内持续注册雷同事件,会造成死循环      const tasks = this.cache[name].slice()      for (let fn of tasks) {        fn();      }    }  }  emit(name, once = false) {    if (this.cache[name]) {      // 创立正本,如果回调函数内持续注册雷同事件,会造成死循环      const tasks = this.cache[name].slice()      for (let fn of tasks) {        fn();      }      if (once) {        delete this.cache[name]      }    }  }}// 测试const eventBus = new EventEmitter()const task1 = () => { console.log('task1'); }const task2 = () => { console.log('task2'); }eventBus.on('task', task1)eventBus.on('task', task2)setTimeout(() => {  eventBus.emit('task')}, 1000)

9. 柯里化:只传递给函数一部分参数来调用它,让它返回一个函数去解决剩下的参数

function curry(func) {  return function curried(...args) {    // 要害知识点:function.length 用来获取函数的形参个数    // 补充:arguments.length 获取的是实参个数    if (args.length >= func.length) {      return func.apply(this, args)    }    return function (...args2) {      return curried.apply(this, args.concat(args2))    }  }}// 测试function sum (a, b, c) {  return a + b + c}const curriedSum = curry(sum)console.log(curriedSum(1, 2, 3))console.log(curriedSum(1)(2,3))console.log(curriedSum(1)(2)(3))

10. es5 实现继承

function create(proto) {  function F() {}  F.prototype = proto;  return new F();}// Parentfunction Parent(name) {  this.name = name}Parent.prototype.sayName = function () {  console.log(this.name)};// Childfunction Child(age, name) {  Parent.call(this, name)  this.age = age}Child.prototype = create(Parent.prototype)Child.prototype.constructor = ChildChild.prototype.sayAge = function () {  console.log(this.age)}// 测试const child = new Child(18, 'Jack')child.sayName()child.sayAge()

11. instanceof

function isInstanceOf(instance, klass) {  let proto = instance.__proto__  let prototype = klass.prototype  while (true) {    if (proto === null) return false    if (proto === prototype) return true    proto = proto.__proto__  }}// 测试class Parent {}class Child extends Parent {}const child = new Child()console.log(isInstanceOf(child, Parent), isInstanceOf(child, Child), isInstanceOf(child, Array));

12. 异步并发数限度

/** * 关键点 * 1. new promise 一经创立,立刻执行 * 2. 应用 Promise.resolve().then 能够把工作加到微工作队列,避免立刻执行迭代办法 * 3. 微工作处理过程中,产生的新的微工作,会在同一事件循环内,追加到微工作队列里 * 4. 应用 race 在某个工作实现时,持续增加工作,放弃工作依照最大并发数进行执行 * 5. 工作实现后,须要从 doingTasks 中移出 */function limit(count, array, iterateFunc) {  const tasks = []  const doingTasks = []  let i = 0  const enqueue = () => {    if (i === array.length) {      return Promise.resolve()    }    const task = Promise.resolve().then(() => iterateFunc(array[i++]))    tasks.push(task)    const doing = task.then(() => doingTasks.splice(doingTasks.indexOf(doing), 1))    doingTasks.push(doing)    const res = doingTasks.length >= count ? Promise.race(doingTasks) : Promise.resolve()    return res.then(enqueue)  };  return enqueue().then(() => Promise.all(tasks))}// testconst timeout = i => new Promise(resolve => setTimeout(() => resolve(i), i))limit(2, [1000, 1000, 1000, 1000], timeout).then((res) => {  console.log(res)})

13. 异步串行 | 异步并行

// 字节面试题,实现一个异步加法function asyncAdd(a, b, callback) {  setTimeout(function () {    callback(null, a + b);  }, 500);}// 解决方案// 1. promisifyconst promiseAdd = (a, b) => new Promise((resolve, reject) => {  asyncAdd(a, b, (err, res) => {    if (err) {      reject(err)    } else {      resolve(res)    }  })})// 2. 串行解决async function serialSum(...args) {  return args.reduce((task, now) => task.then(res => promiseAdd(res, now)), Promise.resolve(0))}// 3. 并行处理async function parallelSum(...args) {  if (args.length === 1) return args[0]  const tasks = []  for (let i = 0; i < args.length; i += 2) {    tasks.push(promiseAdd(args[i], args[i + 1] || 0))  }  const results = await Promise.all(tasks)  return parallelSum(...results)}// 测试(async () => {  console.log('Running...');  const res1 = await serialSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)  console.log(res1)  const res2 = await parallelSum(1, 2, 3, 4, 5, 8, 9, 10, 11, 12)  console.log(res2)  console.log('Done');})()

14. vue reactive

// Dep moduleclass Dep {  static stack = []  static target = null  deps = null    constructor() {    this.deps = new Set()  }  depend() {    if (Dep.target) {      this.deps.add(Dep.target)    }  }  notify() {    this.deps.forEach(w => w.update())  }  static pushTarget(t) {    if (this.target) {      this.stack.push(this.target)    }    this.target = t  }  static popTarget() {    this.target = this.stack.pop()  }}// reactivefunction reactive(o) {  if (o && typeof o === 'object') {    Object.keys(o).forEach(k => {      defineReactive(o, k, o[k])    })  }  return o}function defineReactive(obj, k, val) {  let dep = new Dep()  Object.defineProperty(obj, k, {    get() {      dep.depend()      return val    },    set(newVal) {      val = newVal      dep.notify()    }  })  if (val && typeof val === 'object') {    reactive(val)  }}// watcherclass Watcher {  constructor(effect) {    this.effect = effect    this.update()  }  update() {    Dep.pushTarget(this)    this.value = this.effect()    Dep.popTarget()    return this.value  }}// 测试代码const data = reactive({  msg: 'aaa'})new Watcher(() => {  console.log('===> effect', data.msg);})setTimeout(() => {  data.msg = 'hello'}, 1000)

15. promise

// 倡议浏览 [Promises/A+ 规范](https://promisesaplus.com/)class MyPromise {  constructor(func) {    this.status = 'pending'    this.value = null    this.resolvedTasks = []    this.rejectedTasks = []    this._resolve = this._resolve.bind(this)    this._reject = this._reject.bind(this)    try {      func(this._resolve, this._reject)    } catch (error) {      this._reject(error)    }  }  _resolve(value) {    setTimeout(() => {      this.status = 'fulfilled'      this.value = value      this.resolvedTasks.forEach(t => t(value))    })  }  _reject(reason) {    setTimeout(() => {      this.status = 'reject'      this.value = reason      this.rejectedTasks.forEach(t => t(reason))    })  }  then(onFulfilled, onRejected) {    return new MyPromise((resolve, reject) => {      this.resolvedTasks.push((value) => {        try {          const res = onFulfilled(value)          if (res instanceof MyPromise) {            res.then(resolve, reject)          } else {            resolve(res)          }        } catch (error) {          reject(error)        }      })      this.rejectedTasks.push((value) => {        try {          const res = onRejected(value)          if (res instanceof MyPromise) {            res.then(resolve, reject)          } else {            reject(res)          }        } catch (error) {          reject(error)        }      })    })  }  catch(onRejected) {    return this.then(null, onRejected);  }}// 测试new MyPromise((resolve) => {  setTimeout(() => {    resolve(1);  }, 500);}).then((res) => {    console.log(res);    return new MyPromise((resolve) => {      setTimeout(() => {        resolve(2);      }, 500);    });  }).then((res) => {    console.log(res);    throw new Error('a error')  }).catch((err) => {    console.log('==>', err);  })

16. 数组扁平化

// 计划 1function recursionFlat(ary = []) {  const res = []  ary.forEach(item => {    if (Array.isArray(item)) {      res.push(...recursionFlat(item))    } else {      res.push(item)    }  })  return res}// 计划 2function reduceFlat(ary = []) {  return ary.reduce((res, item) => res.concat(Array.isArray(item) ? reduceFlat(item) : item), [])}// 测试const source = [1, 2, [3, 4, [5, 6]], '7']console.log(recursionFlat(source))console.log(reduceFlat(source))

17. 对象扁平化

function objectFlat(obj = {}) {  const res = {}  function flat(item, preKey = '') {    Object.entries(item).forEach(([key, val]) => {      const newKey = preKey ? `${preKey}.${key}` : key      if (val && typeof val === 'object') {        flat(val, newKey)      } else {        res[newKey] = val      }    })  }  flat(obj)  return res}// 测试const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }console.log(objectFlat(source));

18. 图片懒加载

// <img src="default.png" data-src="https://xxxx/real.png">function isVisible(el) {  const position = el.getBoundingClientRect()  const windowHeight = document.documentElement.clientHeight  // 顶部边缘可见  const topVisible = position.top > 0 && position.top < windowHeight;  // 底部边缘可见  const bottomVisible = position.bottom < windowHeight && position.bottom > 0;  return topVisible || bottomVisible;}function imageLazyLoad() {  const images = document.querySelectorAll('img')  for (let img of images) {    const realSrc = img.dataset.src    if (!realSrc) continue    if (isVisible(img)) {      img.src = realSrc      img.dataset.src = ''    }  }}// 测试window.addEventListener('load', imageLazyLoad)window.addEventListener('scroll', imageLazyLoad)// orwindow.addEventListener('scroll', throttle(imageLazyLoad, 1000))

欢送一起补充 ~ Github 地址