Coder Social home page Coder Social logo

blog's People

Contributors

santa955 avatar

Watchers

 avatar

blog's Issues

常见面试题总结

  1. Babel工作原理;
  2. Webpack工作流程;
  3. BFC;
  4. 浏览器渲染机制;
  5. 垃圾回收机制;
  6. 跨域;
  7. 浏览器与Node.js Event Loop;
  8. React Fiber 的作用和原理;
  9. HTTPS加密原理;
  10. HTTPS缓存;
  11. new关键字的工作原理及实现;
  12. V8垃圾回收机制;
  13. TypeScript相关;
  14. Redux实现原理;
  15. Node负载均衡原理、PM2工作原理;参考1参考2
  16. Node实现高并发的原理参考
  17. Node创建子进程的方式:spawn/exec/execFile/fork 参考

这里有更多

ES5实现ES6的class关键字

ES5实现class关键字主要是使用Object.defineProperty()方法实现

let createClass = {
  checkType: function (obj, constructor) {
    if (!(obj instanceof constructor)) {
      throw new TypeError('Can not call a class as a function.')
    }
  },

  create: function (constructor, protoProps, staticProps) {
    protoProps && this.defineProperties(constructor.propertype, protoProps)
    staticProps && this.defineProperties(constructor, staticProps)
    return constructor
  },

  defineProperties: function (target, props) {
    for (var descriptor of props) {
      descriptor.enumerable = descriptor.enumerable || false
      descriptor.configurale = true
      if ('value' in descriptor) descriptor.writable = true
      Object.defineProperty(target, descriptor.key, { ...descriptor })
    }
  },

  inherits: function (subClass, superClass) {
    if (typeof subClass !== 'function' && superClass !== null) {
      throw new TypeError('Super expression must either be null or a function')
    }

    subClass.propertype = Object.create(superClass && superClass.propertype, {
      constructor: {
        value: subClass,
        enumerable: false,
        writable: true,
        configurable: true
      }
    })

    if (superClass) {
      Object.setPrototypeOf
        ? Object.setPrototypeOf(subClass, superClass)
        : subClass.__proto__ = superClass
    }
  }
}

使用方法:

var Person = function () {
  function Person (name) {
    createClass.checkType(this, Person)
    this.name = name
  }

  createClass.create(Person, [{
    key: 'sayHello',
    value: function sayHello () {
      return 'hello, I am ' + this.name
    }
  }, {
    key: 'name',
    get: function get () {
      return 'kevin'
    },
    set: function set (newName) {
      console.log('new name 为:' + newName)
    }
  }], [{
    key: 'onlySayHello',
    value: function onlySayHello () {
      return 'hello'
    }
  }])
  return Person
}()

实现sleep函数

//使用Promise实现
const sleep = (time) => {
  return new Promise((r => setTimeout(() => r(), time)))
}

//使用回调实现
function sleep (callback, time) {
  if (typeof callback === 'function') {
    setTimeout(callback, time)
  }
}

使用方法:

async function sleepAsync() {
  console.log('before sleep')
  await sleep(1000)
  console.log('after sleep')
}

sleepAsync()

function output () {
  console.log(1)
}
sleep(output, 1000)

实现async/await

//https://github.com/sisterAn/JavaScript-Algorithms/issues/56
function uAsync(gen) {
  return new Promise((resolve, reject) => {
    let g = gen()
    function next(v) {
      try {
        let res = g.next(v)
        if (res.done) {
          resolve(res.value)
        }
        Promise
          .resolve(res.value)
          .then(next)
          .then(resolve, reject)
      } catch (error) {
        reject(error)
      }
    }
    next()
  })
}
function getNum (num) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num + 1)
    }, 1000)
  })
}

var func = function* (){
  var f1 = yield getNum(1)
  var f2 = yield getNum(f1)
  console.log(f2)  // 3
}
uAsync (func)

实现列表与数结构互转

列表转数结构

const list = [
  { id: 1, name: '部门A', parentId: null },
  { id: 2, name: '部门B', parentId: 1 },
  { id: 3, name: '部门C', parentId: 1 },
  { id: 4, name: '部门D', parentId: 1 },
  { id: 5, name: '部门E', parentId: 2 },
  { id: 6, name: '部门F', parentId: 3 },
  { id: 7, name: '部门G', parentId: 2 },
  { id: 8, name: '部门H', parentId: 4 }
]
// 实现方式1
// 参考:https://juejin.cn/post/7021517725306617869
function listToTree(list) {
  var map = {}
  var tree = []
  var len = list.length
  for (var i = 0; i < len; i++) {
    var item = list[i]
    var { id, children } = item
    map[id] = item
    if (!children) {
      map[id].children = []
    }
  }

  for (var i = 0; i < len; i++) {
    var item = list[i]
    var { id, parentId } = item
    if (map[parentId]) {
      map[parentId].children.push(item)
    } else {
      tree.push(item)
    }
  }
  return tree
}
//实现方式2
function toTree(arr, parentId) {
  function loop(parentId) {
    return arr.reduce((acc, cur) => {
      if (cur.parentId === parentId) {
        cur.children = loop(cur.id)
        acc.push(cur)
      }
      return acc
    }, [])
  }
  return loop(parentId)
}

实现`Promise.allSettled()`

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilledrejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

Promise.allSettled = Promise.allSettled || function (promises) {
  if (!Array.isArray(promises)) {
    throw new TypeError('Type Error')
  }
  return new Promise((resolve, reject) => {
    let len = promises.length
    let res = new Array(len)
    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then(
        (val) => {
          res[i] = val
          len--
          if (len === 0) {
            resolve(res)
          }
        },
        err => {
          res[i] = err
          len--
          if (len === 0) {
            resolve(res)
          }
        })
    }
  })
}
var p1 = new Promise(r => {
  setTimeout(() => {
    r('done')
  }, 1000)
})

var p2 = new Promise((r, j) => {
  setTimeout(() => {
    j('error')
  }, 1500)
})

Promise.allSettled([p1, p2]).then(r => {
  console.log(r) // [ 'done', 'error' ]
})

二分法查找模板

知乎参考链接

def lower_bound(array, first, last, value):  # 求非降序范围[first, last)内第一个不小于value的值的位置
    while first < last: # 搜索区间[first, last)不为空
        mid = first + (last - first) // 2  # 防溢出
        if array[mid] < value: first = mid + 1 
        else: last = mid
    return first  # last也行,因为[first, last)为空的时候它们重合

实现一个符合 Promises/A+ 规范的 Promise

// 参考:https://febook.hzfe.org/awesome-interview/book1/coding-promise

// 1. 实现Promise构造函数
function Promise(executor) {
  // 2.1. Promise 的状态
  // Promise 必须处于以下三种状态之一:pending,fulfilled 或者 rejected。
  this.state = "pending";
  // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
  this.onFulfilledCallback = [];
  // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
  this.onRejectedCallback = [];

  const self = this;

  function resolve(value) {
    setTimeout(function () {
      // 2.1.1. 当 Promise 处于 pending 状态时:
      // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
      // 2.1.2. 当 Promise 处于 fulfilled 状态时:
      // 2.1.2.1. 不得过渡到任何其他状态。
      // 2.1.2.2. 必须有一个不能改变的值。
      if (self.state === "pending") {
        self.state = "fulfilled";
        self.data = value;
        // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
        for (let i = 0; i < self.onFulfilledCallback.length; i++) {
          self.onFulfilledCallback[i](value);
        }
      }
    });
  }

  function reject(reason) {
    setTimeout(function () {
      // 2.1.1. 当 Promise 处于 pending 状态时:
      // 2.1.1.1. 可以转换到 fulfilled 或 rejected 状态。
      // 2.1.3. 当 Promise 处于 rejected 状态时:
      // 2.1.2.1. 不得过渡到任何其他状态。
      // 2.1.2.2. 必须有一个不能改变的值。
      if (self.state === "pending") {
        self.state = "rejected";
        self.data = reason;
        // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
        for (let i = 0; i < self.onRejectedCallback.length; i++) {
          self.onRejectedCallback[i](reason);
        }
      }
    });
  }

  // 补充说明:用户传入的函数可能也会执行异常,所以这里用 try...catch 包裹
  try {
    executor(resolve, reject);
  } catch (reason) {
    reject(reason);
  }
}
// 2. 实现方法 then
// 一个 promise 必须提供一个 then 方法来访问其当前值或最终值或 rejected 的原因。
// 一个 promise 的 then 方法接受两个参数:
// promise.then(onFulfilled, onRejected)
Promise.prototype.then = function (onFulfilled, onRejected) {
  const self = this;

  let promise2;
  // 2.2.7. then 必须返回一个 promise
  return (promise2 = new Promise(function (resolve, reject) {
    // 2.2.2. 如果 onFulfilled 是一个函数:
    // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
    // 2.2.2.2. 它一定不能在 promise 的状态变为 fulfilled 前被调用。
    // 2.2.2.3. 它最多只能被调用一次。
    if (self.state === "fulfilled") {
      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
      // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
      setTimeout(function () {
        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:
        // 2.2.1.1. 如果 onFulfilled 不是一个函数,它必须被忽略。
        if (typeof onFulfilled === "function") {
          try {
            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
            const x = onFulfilled(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
            reject(e);
          }
        } else {
          // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
          resolve(self.data);
        }
      });
    }
    // 2.2.3. 如果 onRejected 是一个函数,
    // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
    // 2.2.3.2. 它一定不能在 promise 的状态变为 rejected 前被调用。
    // 2.2.3.3. 它最多只能被调用一次。
    else if (self.state === "rejected") {
      // 2.2.4. onFulfilled 或 onRejected 在执行上下文堆栈仅包含平台代码之前不得调用。
      // 3.1. 这可以通过“宏任务”机制(例如 setTimeout 或 setImmediate)或“微任务”机制(例如 MutationObserver 或 process.nextTick)来实现。
      setTimeout(function () {
        // 2.2.1. onFulfilled 和 onRejected 都是可选参数:
        // 2.2.1.2. 如果 onRejected 不是一个函数,它必须被忽略。
        if (typeof onRejected === "function") {
          try {
            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
            const x = onRejected(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
            reject(e);
          }
        }
        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
        else {
          reject(self.data);
        }
      });
    } else if (self.state === "pending") {
      // 2.2.6. then 可能会被同一个 promise 多次调用。

      // 2.2.6.1. 如果 promise 处于 fulfilled 状态,所有相应的 onFulfilled 回调必须按照它们对应的 then 的原始调用顺序来执行。
      self.onFulfilledCallback.push(function (promise1Value) {
        if (typeof onFulfilled === "function") {
          try {
            // 2.2.2.1. 它必须在 promise 的状态变为 fulfilled 后被调用,并将 promise 的值作为它的第一个参数。
            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
            const x = onFulfilled(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
            reject(e);
          }
        }
        // 2.2.7.3. 如果 onFulfilled 不是一个函数且 promise1 为 fulfilled 状态,promise2 必须用和 promise1 一样的值来变为 fulfilled 状态。
        else {
          resolve(promise1Value);
        }
      });
      // 2.2.6.2. 如果 promise 处于 rejected 状态,所有相应的 onRejected 回调必须按照它们对应的 then 的原始调用顺序来执行。
      self.onRejectedCallback.push(function (promise1Reason) {
        if (typeof onRejected === "function") {
          try {
            // 2.2.3.1. 它必须在 promise 的状态变为 rejected 后被调用,并将 promise 的 reason 作为它的第一个参数。
            // 2.2.5. onFulfilled 和 onRejected 必须作为函数调用。
            const x = onRejected(self.data);
            // 2.2.7.1. 如果 onFulfilled 或 onRejected 返回了一个值 x,则运行 Promise 处理程序 [[Resolve]](promise2, x)。
            promiseResolutionProcedure(promise2, x, resolve, reject);
          } catch (e) {
            // 2.2.7.2. 如果 onFulfilled 或 onRejected 抛出了一个异常,promise2 必须用 e 作为 reason 来变为 rejected 状态。
            reject(e);
          }
        }
        // 2.2.7.4. 如果 onRejected 不是一个函数且 promise1 为 rejected 状态,promise2 必须用和 promise1 一样的 reason 来变为 rejected 状态。
        else {
          reject(promise1Reason);
        }
      });
    }
  }));
};
// 3. Promise 处理程序
// Promise 处理程序是一个将 promise 和 value 作为输入的抽象操作,我们将其表示为 [[Resolve]](promise, x)。
// 补充说明:这里我们将 resolve 和 reject 也传入进来,因为后续要根据不同的逻辑对 promise 执行 fulfill 或 reject 操作。
function promiseResolutionProcedure(promise2, x, resolve, reject) {
  // 2.3.1. 如果 promise 和 x 引用的是同一个对象,promise 将以一个 TypeError 作为 reason 来进行 reject。
  if (promise2 === x) {
    return reject(new TypeError("Chaining cycle detected for promise"));
  }

  // 2.3.2. 如果 x 是一个 promise,根据它的状态:
  if (x instanceof Promise) {
    // 2.3.2.1. 如果 x 的状态为 pending,promise 必须保持 pending 状态直到 x 的状态变为 fulfilled 或 rejected。
    if (x.state === "pending") {
      x.then(function (value) {
        promiseResolutionProcedure(promise2, value, resolve, reject);
      }, reject);
    }
    // 2.3.2.2. 如果 x 的状态为 fulfilled,那么 promise 也用同样的值来执行 fulfill 操作。
    else if (x.state === "fulfilled") {
      resolve(x.data);
    }
    // 2.3.2.3. 如果 x 的状态为 rejected,那么 promise 也用同样的 reason 来执行 reject 操作。
    else if (x.state === "rejected") {
      reject(x.data);
    }
    return;
  }

  // 2.3.3. 除此之外,如果 x 是一个对象或者函数,
  if (x && (typeof x === "object" || typeof x === "function")) {
    // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
    let isCalled = false;

    try {
      // 2.3.3.1. 声明一个 then 变量来保存 then
      let then = x.then;
      // 2.3.3.3. 如果 then 是一个函数,将 x 作为 this 来调用它,第一个参数为 resolvePromise,第二个参数为 rejectPromise,其中:
      if (typeof then === "function") {
        then.call(
          x,
          // 2.3.3.3.1. 假设 resolvePromise 使用一个名为 y 的值来调用,运行 promise 处理程序 [[Resolve]](promise, y)。
          function resolvePromise(y) {
            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
            if (isCalled) return;
            isCalled = true;
            return promiseResolutionProcedure(promise2, y, resolve, reject);
          },
          // 2.3.3.3.2. 假设 rejectPromise 使用一个名为 r 的 reason 来调用,则用 r 作为 reason 对 promise 执行 reject 操作。
          function rejectPromise(r) {
            // 2.3.3.3.3. 如果 resolvePromise 和 rejectPromise 都被调用,或者多次调用同样的参数,则第一次调用优先,任何之后的调用都将被忽略。
            if (isCalled) return;
            isCalled = true;
            return reject(r);
          }
        );
      }
      // 2.3.3.4. 如果 then 不是一个函数,使用 x 作为值对 promise 执行 fulfill 操作。
      else {
        resolve(x);
      }
    } catch (e) {
      // 2.3.3.2. 如果检索 x.then 的结果抛出异常 e,使用 e 作为 reason 对 promise 执行 reject 操作。
      // 2.3.3.3.4. 如果调用 then 时抛出一个异常 e,
      // 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise 已经被调用过了,则忽略异常。
      if (isCalled) return;
      isCalled = true;
      // 2.3.3.3.4.2. 否则,使用 e 作为 reason 对 promise 执行 reject 操作。
      reject(e);
    }
  }
  // 2.3.4. 如果 x 不是一个对象或者函数,使用 x 作为值对 promise 执行 fulfill 操作。
  else {
    resolve(x);
  }
}

实现发布/订阅模式

主要实现下面的API:

  • on(event, listener):为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。
  • emit(event, [arg1], [arg2]): 按监听器的顺序执行执行每个监听器
  • once(event, listener): 和on类似,但只触发一次,随后便解除事件监听
  • off(event, listener): 移除指定事件的某个监听回调
function EventEmitter () {
  /**
   * {
   *    event1: [fn1, fn2, fn3, ...],
   *    event2: [fn1, fn2, fn3, ...]
   * }
   */
  this.listeners = {}
}

EventEmitter.prototype.on = function (event, fn) {
  let listeners = this.listeners
  if (!listeners[event]) {
    this.listeners[event] = []
  }
  this.listeners[event].push(fn)
}

EventEmitter.prototype.emit = function (event) {
  let listeners = this.listeners
  let args = [].slice.call(arguments)
  let fns = listeners[event] || []
  //删除事件名称
  args.shift()
  for (const fn of fns) {
    fn.apply(this, args)
  }
}

EventEmitter.prototype.off = function (event, fn) {
  let listeners = this.listeners
  if (listeners[event]) {
    let index = listeners[event].indexOf(fn)
    if (index >= 0) {
      this.listeners[event].splice(index, 1)
    }
  }
}

EventEmitter.prototype.once = function (event, fn) {
  const cb = () => {
    let args = [].slice.call(arguments)
    fn.apply(this, args)
    // 注意此处要解除绑定的是函数cb
    this.off(event, cb)
  }
  this.on(event, cb)
}

测试用例:

var evt = new EventEmitter()
var hello = function (from) {
  console.log(`${from} hello greet`)
}

var today = function () {
  console.log(new Date().toLocaleString())
}

evt.on('hello', hello)
evt.once('today', today)
evt.emit('hello', 'jack') // jack hello greet
evt.emit('hello', 'make && joe') // make && joe hello greet
evt.off('hello', hello)
evt.emit('hello', 'pony')  // nothing
evt.emit('today') // 2021/4/2上午11:27:25
evt.emit('today') // nothing

主要参考文章:

知乎-EventEmitter的前端实现
MDN-EventTarget 的简单实现

实现失败可自动重试一定次数的fetchWithRetry函数

实现一个函数fetchWithRetry(xhr, times = 5, delay = 1000),在请求失败delay时间后自动发起times次重试。

const fetchWithRetry = function (xhr, times = 5, delay = 1000) {
  return new Promise((resolve, reject) => {
    const run = () => {
      xhr()
        .then(resolve)
        .catch(err => {
          times--
          console.log(`还剩${times}次重试`)
          if (times >= 0) {
            setTimeout(() => { run() }, delay)
          } else {
            reject(err)
          }
        })
    }
    run()
  })
}

测试用例

let xhr = () => {
  return new Promise((r, j) => {
    setTimeout(() => { j('出错了') }, 1000)
  })
}

fetchWithRetry(xhr)
// 还剩4次重试
// 还剩3次重试
// 还剩2次重试
// 还剩1次重试
// 还剩0次重试

实现 `Promise.all()`

Promise.all = Promise.all || function (promises) {
  if (!Array.isArray(promises)) {
    throw new TypeError('Type Error')
  }

  return new Promise((resolve, reject) => {
    let len = promises.length
    let res = new Array(len)
    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then((val) => {
        res[i] = val
        len--
        if (len === 0) {
          resolve(res)
        }
      }, err => {
        reject(err)
      })
    }
  }
var p1 = new Promise(r => {
  setTimeout(() => {
    r('done')
  }, 1000)
})

var p2 = new Promise((r, j) => {
  setTimeout(() => {
    j('error')
  }, 1500)
})

Promise.all([p1, p2]).then(r => {
  console.log(r)
}).catch(err => {
  console.error(err) //error
})

实现 `Promise.race()`

Promise.race = Promise.race || function (promises) {
  if (!Array.isArray(promises)) {
    throw new TypeError('Type Error')
  }

  return new Promise((resolve, reject) => {
    for (let p of promises) {
      Promise.resolve(p).then(resolve, reject)
    }
  })
}
var p1 = new Promise(r => {
  setTimeout(() => {
    r('done')
  }, 1000)
})

var p2 = new Promise((r, j) => {
  setTimeout(() => {
    j('error')
  }, 1500)
})

Promise.race([p1, p2]).then(r => {
  console.log(r)
}).catch(err => {
  console.error(err)
})

实现关键字instanceof

instanceof 运算符用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上。
由于实例对象存在一个隐形的__proto__对象,这个对象指向实例所对应的构造函数的原型对象prototype。回溯__proto__对象到最后会为null所以只要我们不断遍历,检测__proto__与构造函数的原型对象prototype是否相等即可。

function uInstanceof (instance, constructor) {
  while (instance) {
    if (instance.__proto__ === constructor.prototype) {
      return true
    }
    instance = instance.__proto__
  }
  return false
}

测试用例如下:

class A{}
var c = new A()
console.log(uInstanceof(c, A));       // true
console.log(uInstanceof(c, Object));  // true 
console.log(uInstanceof(c, Array));   // false

实现`Promise.any()`

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise ,如果所有的promise 都失败,返回失败结果的数组(和Promise.all返回的格式相同)。

Promise.any = Promise.any || function (promises) {
  if (!Array.isArray(promises)) {
    throw new TypeError('Type Error')
  }

  return new Promise((resolve, reject) => {
    let len = promises.length
    let errs = new Array(len)
    for (let i = 0; i < len; i++) {
      Promise.resolve(promises[i]).then(val => {
        resolve(val)
      }, err => {
        errs[i] = err
        len--
        if (len === 0) {
          reject(errs)
        }
      })
    }
  }
var p1 = new Promise(r => {
  setTimeout(() => {
    r('done')
  }, 1000)
})

var p3 = new Promise(r => {
  setTimeout(() => {
    r(500)
  }, 500)
})

var p2 = new Promise((r, j) => {
  setTimeout(() => {
    j('error')
  }, 1500)
})

var p4 = new Promise((r, j) => {
  setTimeout(() => {
    j('error2000')
  }, 2000)
})

Promise.any([p1, p2, p4]).then(r => {
  console.log(r) // 'done'
}).catch(err => {
  console.error(err)
})

Promise.any([p2, p4]).then(r => {
  console.log(r) // 'done'
}).catch(err => {
  console.error(err) //[ 'error', 'error2000' ]
})

常见排序算法

快速排序算法(快排)

原地分割版本

function quicklySort(nums, left, right) {
    if (left >= right) return
    var index = partition(nums, left, right)
    quicklySort(nums, left, index)
    quicklySort(nums, index + 1, right)
    return nums
}

function swap(nums, i, j) {
    var temp = nums[i]
    nums[i] = nums[j]
    nums[j] = temp
    return nums
}

function partition(nums, left, right) {
    var pivot = nums[left]
    var i = left
    swap(nums, left, right)
    //注意j的起始值是i,不是0
    for (var j = i; j < right; j++) {
        if (nums[j] > pivot) {
            swap(nums, i, j)
            i++
        }
    }
    //注意最后交换的是i与right的值
    swap(nums, i, right)
    return i
}

var arr = [1, 9, 8, 9, 0, 1, 45, 3];
console.log(quicklySort(arr, 0, arr.length - 1))

两路快排

var sortArray = function (nums) {
    quickSort(nums, 0, nums.length - 1)
    return nums
};

function quickSort(nums, left, right) {
    if (left >= right) return
    var pivot = nums[left]
    var i = left
    var j = right
    while (i < j) {
       //一定要从右边开始,i和j的顺序不能调换,先j--,再i++
        while (i < j && nums[j] >= pivot) j--
        while (i < j && nums[i] <= pivot) i++
        var temp = nums[i]
        nums[i] = nums[j]
        nums[j] = temp
    }
    nums[left] = nums[i]
    nums[i] = pivot
    quickSort(nums, left, i - 1)
    quickSort(nums, i + 1, right)
}

实现一个 Compose 函数

我们在完成函数依赖调用(前一个函数的返回结果作为后一个函数的参数)时,常常会嵌套多层调用。compose函数的作用主要是将需要嵌套执行的函数平铺,并按照参数的顺序从右往左执行,结果作为最终函数的参数。

具体实现如下:

function compose () {
  let args = [].slice.call(arguments)
  return function () {
    let fn = args.pop()
    //注意此处的参数和后面循环的差异
    let res = fn.apply(this, arguments)
    while (args.length > 0) {
      fn = args.pop()
      res = fn.call(this, res)
    }
    return res
  }
}

测试用例:

现在实现一个函数,输入一个数,先执行加1,再乘2, 最后减5操作

function subtract (a) {
  return a - 5
}

function multi (a) {
  return a * 2
}

function addOne (a) {
  return a + 1
}

//使用compose前
subtract(multi(addOne(2))) //输出1

//使用compose
compose(subtract, multi, addOne)(2) //输出1

实现Promise.finally方法

//参考:https://github.com/matthew-andrews/Promise.prototype.finally/blob/master/finally.js
Promise.prototype.finally = function (callback) {
  let constructor = this.constructor
  return this.then(
    value => constructor.resolve(callback()).then(() => value),
    reason => constructor.resolve(callback()).then(() => { throw reason })
  )
}

Promise finally方法不管成功还是失败都会执行,而不会永远最后执行。例如:

new Promise((resolve, reject) => {
    resolve();
}).finally(() => {
    console.log('finally1');
}).then(() => {
    console.log('then');
}).finally(() => {
    console.log('finally2');
});

// finally1
// then
// finally2

实现柯里化(Curry)函数

柯里化要求函数具有固定数量的参数。使用 rest 参数的函数,例如 f(...args),不能以这种方式进行柯里化。

//参考: https://github.com/mqyqingfeng/Blog/issues/42#issuecomment-342688679
// 实现步骤:
// 检查传入的参数长度与原函数参数个数比较
// 1. 如果少于原函数参数个数,说明还不能执行原函数,拼接参数,返回函数
// 2. 如果大于或等于原函数参数个数,返回原函数执行结果
function curry(fn, args = []) {
  //function.length 返回函数参数长度
  var length = fn.length;
  return function () {
    var params = [...args]
    for(var i = 0; i < arguments.length; i++) {
      var arg = arguments[i];
      params.push(arg);
    }
    // 情况 1
    if (params.length < length) {
      return curry.call(this, fn, params);
    }else {
      //情况 2
      return fn.apply(this, params);
    }
  }
}

//实现方式2
//参考:https://zh.javascript.info/currying-partials
function curry(fn) {
  var len = fn.length
  return function curried(...args) {
    if (args.length >= len) {
      return fn.apply(this, args)
    } else {
     //注意此处返回函数
      return function (...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  }
}

实现`shuffle() `函数打乱数组,使产生的各种可能的组合概率相等

Fisher-Yates 洗牌算法

function shuffle (nums) {
  let getRandom = function (low, high) {
   //返回随机数在区间[low, high]上,包含最大值和最小值
    return Math.floor(Math.random() * (high - low + 1)) + low
  }

  let len = nums.length
  for (let i = 0; i < len; i++) {
    let rIndex = getRandom(i, len - 1)
    let temp = nums[i]
    nums[i] = nums[rIndex]
    nums[rIndex] = temp
  }
  return nums
}

console.log(shuffle([1, 2, 3, 4]))

实现Object.create()方法

Object.create(proto,[propertiesObject])方法用于创建一个新对象,并把创建对象的__proto__属性指向proto参数所指对象。实现如下:

Object.ucreate = function (proto) {
  function Fn () { }
 //因为构造函数的实例的__proto__指向构造函数的prototype对象
  Fn.prototype = proto
 //重新指定构造函数原型对象的指向
  Fn.prototype.constructor = Fn
 // Fn实列的__proto__指向 Fn.prototype
  return new Fn()
}

测试用例如下:

var person = {
  isHuman: false,
  printIntroduction: function() {
    console.log(`My name is ${this.name}. Am I human? ${this.isHuman}`)
  }
}

var me = Object.ucreate(person)
me.name = 'Matthew'   // "name" is a property set on "me", but not on "person"
me.isHuman = true     // inherited properties can be overwritten

me.printIntroduction()

实现可取消的Promise

function PromiseWithAbort (promise) {
  // 在这里包装一个 promise,可以控制原来的promise是成功还是失败
  let abort
  let newPromise = new Promise((resolve, reject) => {
    abort = reject
  })
  // 任何一个先成功或者失败 就可以获取到结果
  let p = Promise.race([promise, newPromise])
  p.abort = abort
  return p
}

使用方法

//模拟请求超时
let promise = function () {
  return new Promise((r, j) => {
    setTimeout(() => { r('超时了') }, 3000)
  })
}

let wrappedPromise = PromiseWithAbort(promise())

//超时前取消
setTimeout(() => {
  wrappedPromise.abort('我取消请求了')
}, 1000)

wrappedPromise.then((data => {
  console.log('成功的结果: ' + data)
})).catch(e => {
  console.log('失败的结果: ' + e)
})

来源:
掘金

实现节流throttle和防抖debounce

节流函数与防抖函数动画演示:

16c87d929467ab9c

节流throttle

节流函数的主要特点是:规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。现在主流的实现方式有两种:一种是使用时间戳,另外一种是使用定时器。

  1. 使用时间戳实现:
function throttle (fn, delay = 500) {
  let prev = 0
  return function () {
    let now = Date.now()
    if (now - prev > delay) {
      fn.apply(this, arguments)
      prev = now
    }
  }
}

调用方式:

const throttleFn = throttle(fn, 300)
container.onmousemove = throttle(fn, 1000)
  1. 使用定时器实现:

这种方式实现的本质是加一个锁,如果函数执行了就重置状态

//方式一
function throttle (fn, delay = 500) {
  let timer = null
  return function () {
    let context = this
    let args = arguments
    if (timer) return
    timer = setTimeout(() => {
      fn.apply(context, args)
      timer = null
    }, delay)
  }
}

//方式二
function throttle (fn, delay = 500) {
  let flag = true
  return (...args) => {
    if (!flag) return
    flag = false
    setTimeout(() => {
      fn.apply(this, args)
      flag = true
    }, delay)
  }

防抖debounce

防抖的主要特点是:无论触发事件多少次,但是我一定在事件触发 n 秒后才执行,如果你在一个事件触发的 n 秒内又触发了这个事件,那我就以新的事件的时间为准,n 秒后才执行,总之,就是要触发完事件 n 秒内不再触发事件,我才执行。

function debounce (fn, delay = 500) {
  let timeout

  return function () {
    let context = this
    let args = arguments

    clearTimeout(timeout)
    timeout = setTimeout(function () {
      //注意this指向和参数问题
      fn.apply(context, args)
    }, delay)
  }
}

实现可取消的防抖和节流

防抖和节流的可取消操作是一样的实现方式,就是在返回的函数上加上一个方法,清除定时器。下面以防抖为例:

function debounce (fn, delay = 500) {
  let timeout
  let debounced = function () {
    let context = this
    let args = arguments

    clearTimeout(timeout)
    timeout = setTimeout(function () {
      //注意this指向和参数问题
      fn.apply(context, args)
    }, delay)
  }

 // 设置取消方式
  debounced.cancel = function () {
    clearTimeout(timeout)
    timeout = null
  }
 //记得返回
  return debounced
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.