Skip to content

手写代码1

this bind call

js
let number = 5
let obj = {
  number: 3,
  fn: (function () {
    let number: number
    this.number *= 2
    number = number * 2
    number = 3
    return function () {
      let num = this.number
      this.number *= 2
      console.log(num)
      number *= 3
      console.log(number)
    }
  })(),
}
let myFun = obj.fn
myFun.call(null)
obj.fn()
console.log(this.number)
let number = 5
let obj = {
  number: 3,
  fn: (function () {
    let number: number
    this.number *= 2
    number = number * 2
    number = 3
    return function () {
      let num = this.number
      this.number *= 2
      console.log(num)
      number *= 3
      console.log(number)
    }
  })(),
}
let myFun = obj.fn
myFun.call(null)
obj.fn()
console.log(this.number)

实现call

js
Function.prototype.mycall = function (context = window, ...args) {
  context.fn = this
  console.log(this)
  let result = context.fn(...args)
  delete fn
  return result
}
function adddd(a, b) {
  let c = a + b
  console.log(c)
}
let c = {}
adddd.mycall(c, 45, 555)
Function.prototype.mycall = function (context = window, ...args) {
  context.fn = this
  console.log(this)
  let result = context.fn(...args)
  delete fn
  return result
}
function adddd(a, b) {
  let c = a + b
  console.log(c)
}
let c = {}
adddd.mycall(c, 45, 555)

实现一个instanceof和new

js
function create() {
  const obj = {};
  const constructort = [].shift().call(arguments);
  obj.__proto__ = constructort.prototype;
  const result = constructort.apply(obj, arguments);
  return typeof result === `object` ? result : obj;
}
function myInstanceOf(left: string, right: string) {
  let lproto = Object.getPrototypeOf(left)
  while (true) {
    if (lproto == null) {
      return false
    }
    if (lproto === right.prototype) {
      return true
    }
    lproto = Object.getPrototypeOf(lproto)
  }
}

console.log(myInstanceOf([1, 2], Array)) // true
function create() {
  const obj = {};
  const constructort = [].shift().call(arguments);
  obj.__proto__ = constructort.prototype;
  const result = constructort.apply(obj, arguments);
  return typeof result === `object` ? result : obj;
}
function myInstanceOf(left: string, right: string) {
  let lproto = Object.getPrototypeOf(left)
  while (true) {
    if (lproto == null) {
      return false
    }
    if (lproto === right.prototype) {
      return true
    }
    lproto = Object.getPrototypeOf(lproto)
  }
}

console.log(myInstanceOf([1, 2], Array)) // true

实现promise

js
function Mypromise(func) {
  this.fullfilled = false
  this.rejected = false
  this.pending = true
  this.handlers = []
  this.errorHandlers = []
  function resolve(...args) {
    this.handlers.forEach((handler) => handler(...args))
  }
  function reject(...args) {
    this.errorHandlers.forEach((handler) => handler(...args))
  }
  func.call(this, resolve.bind(this), reject.bind(this))
}

Mypromise.prototype.then = function (func) {
  this.handlers.push(func)
  return this
}
Mypromise.prototype.catch = function (func) {
  this.errorHandlers.push(func)
  return this
}

Mypromise.race = (promises) =>
  new Mypromise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(resolve, reject)
    })
  })

Mypromise.all = (promises) =>
  new Mypromise((resolve, reject) => {
    let len = promises.length
    let res = []
    promises.forEach((p, i) => {
      p.then((r) => {
        if (len === 1) {
          resolve(res)
        } else {
          res[i] = r
        }
        len--
      }, reject)
    })
  })

// test
const p1 = new Mypromise((resolve) =>
  setTimeout(resolve.bind(null, 'resolved'), 3000)
)
p1.then(console.log).then((...args) => console.log('second', ...args))

const p2 = new Mypromise((resolve, reject) =>
  setTimeout(reject.bind(null, 'rejected'), 3000)
)
p2.then(console.log).catch((...args) => console.log('fail', ...args))
function Mypromise(func) {
  this.fullfilled = false
  this.rejected = false
  this.pending = true
  this.handlers = []
  this.errorHandlers = []
  function resolve(...args) {
    this.handlers.forEach((handler) => handler(...args))
  }
  function reject(...args) {
    this.errorHandlers.forEach((handler) => handler(...args))
  }
  func.call(this, resolve.bind(this), reject.bind(this))
}

Mypromise.prototype.then = function (func) {
  this.handlers.push(func)
  return this
}
Mypromise.prototype.catch = function (func) {
  this.errorHandlers.push(func)
  return this
}

Mypromise.race = (promises) =>
  new Mypromise((resolve, reject) => {
    promises.forEach((promise) => {
      promise.then(resolve, reject)
    })
  })

Mypromise.all = (promises) =>
  new Mypromise((resolve, reject) => {
    let len = promises.length
    let res = []
    promises.forEach((p, i) => {
      p.then((r) => {
        if (len === 1) {
          resolve(res)
        } else {
          res[i] = r
        }
        len--
      }, reject)
    })
  })

// test
const p1 = new Mypromise((resolve) =>
  setTimeout(resolve.bind(null, 'resolved'), 3000)
)
p1.then(console.log).then((...args) => console.log('second', ...args))

const p2 = new Mypromise((resolve, reject) =>
  setTimeout(reject.bind(null, 'rejected'), 3000)
)
p2.then(console.log).catch((...args) => console.log('fail', ...args))

promise2

js
function resolvePromise(promise2, x, resolve, reject) {
  // console.log(promise2===x)
  //判断x和promise2之间的关系
  //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
  if (x === promise2) {
    //相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
    return reject(new TypeError('循环引用'))
  }
  //如果x不是null,是对象或者方法
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    //为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)
    let called
    try {
      //防止then出现异常,Object.defineProperty
      let then = x.then //取x的then方法可能会取到{then:{}},并没有执行
      if (typeof then === 'function') {
        //我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
        then.call(
          x,
          (y) => {
            //第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
            if (called) return
            called = true
            //因为可能promise中还有promise,所以需要递归
            // resolvePromise(promise2,y,resolve,reject)
            resolve(y)
          },
          (err) => {
            if (called) return
            called = true
            //一次错误就直接返回
            reject(err)
          }
        )
      } else {
        //如果是个普通对象就直接返回resolve作为结果
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    //这里返回的是非函数,非对象的值,就直接放在promise2的resolve中作为结果
    resolve(x)
  }
}

class MyPromise {
  constructor(executor, name) {
    this.name = name
    //控制状态,使用了一次之后,接下来的都不被使用
    this.status = 'pendding'
    this.value = undefined
    this.reason = undefined
    //存放成功回调的函数
    this.onResolvedCallbacks = []
    //存放失败回调的函数
    this.onRejectedCallbacks = []

    //定义resolve函数
    let resolve = (data) => {
      if (this.status === 'pendding') {
        // this.status = 'resolve'
        rv(data)
      }
    }
    let rv = (data) => {
      this.value = data
      //假如第一次实力的promise的
      if (data instanceof MyPromise) {
        this.status = 'pendding'
        data.then(rv, rj)
      } else {
        this.status = 'resolve'
        //监听回调函数(如果data是promise,则上一个的会等待这个data执行了这个rv方法之后在执行,它本身的成功回调)
        this.onResolvedCallbacks.forEach((fn) => fn())
      }
    }
    //定义reject函数
    let reject = (data) => {
      if (this.status === 'pendding') {
        rj(data)
      }
    }
    let rj = (data) => {
      this.reason = data
      //假如第一次实力的promise的
      if (data instanceof MyPromise) {
        this.status === 'pendding'
        data.then(rv, rj)
      } else {
        this.status = 'reject'
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }
    try {
      //将resolve和reject函数给使用者
      executor(resolve, reject)
    } catch (e) {
      //如果在函数中抛出异常则将它注入reject中
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    // console.log(onFufilled.a)
    //解决onFufilled,onRejected没有传值的问题
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (y) => y
    //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (err) => {
            throw err
          }
    //声明一个promise对象
    let promise2
    if (this.status === 'resolve') {
      //因为在.then之后又是一个promise对象,所以这里肯定要返回一个promise对象
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          //因为穿透值的缘故,在默认的跑出一个error后,不能再用下一个的reject来接受,只能通过try,catch
          try {
            //因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断
            //如果返回值为promise对象,则需要取出结果当作promise2的resolve结果
            //如果不是,直接作为promise2的resolve结果
            let x = onFulfilled(this.value)
            //抽离出一个公共方法来判断他们是否为promise对象
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
    }
    if (this.status === 'reject') {
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
    }
    if (this.status === 'pendding') {
      promise2 = new MyPromise((resolve, reject) => {
        this.onResolvedCallbacks.push(() => {
          // to do....
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      })
    }
    return promise2
  }
  //catch方法
  catch(onRejected) {
    return this.then(null, onRejected)
  }
  finally(callback) {
    let P = this.constructor
    return this.then(
      (value) => P.resolve(callback()).then(() => value),
      (reason) =>
        P.resolve(callback()).then(() => {
          throw reason
        })
    )
  }
  done(onFulfilled, onRejected) {
    this.catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => {
        throw reason
      }, 0)
    })
  }
}
//resolve方法
MyPromise.resolve = function (val) {
  return new MyPromise((resolve, reject) => {
    resolve(val)
  })
}
//reject方法
MyPromise.reject = function (val) {
  return new MyPromise((resolve, reject) => {
    reject(val)
  })
}
//race方法
MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject)
    }
  })
}
//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
MyPromise.all = function (promises) {
  let arr = []
  let i = 0
  function processData(index, data) {
    arr[index] = data
    i++
    if (i == promises.length) {
      resolve(arr)
    }
  }
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((data) => {
        processData(i, data)
      }, reject)
    }
  })
}
//promise语法糖 也用来测试
MyPromise.deferred = MyPromise.defer = function () {
  let dfd = {}
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

//npm install promises-aplus-tests 用来测试自己的promise 符不符合promise规范  使用
module.exports = MyPromise
// export default Promise
function resolvePromise(promise2, x, resolve, reject) {
  // console.log(promise2===x)
  //判断x和promise2之间的关系
  //因为promise2是上一个promise.then后的返回结果,所以如果相同,会导致下面的.then会是同一个promise2,一直都是,没有尽头
  if (x === promise2) {
    //相当于promise.then之后return了自己,因为then会等待return后的promise,导致自己等待自己,一直处于等待
    return reject(new TypeError('循环引用'))
  }
  //如果x不是null,是对象或者方法
  if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
    //为了判断resolve过的就不用再reject了,(比如有reject和resolve的时候)
    let called
    try {
      //防止then出现异常,Object.defineProperty
      let then = x.then //取x的then方法可能会取到{then:{}},并没有执行
      if (typeof then === 'function') {
        //我们就认为他是promise,call他,因为then方法中的this来自自己的promise对象
        then.call(
          x,
          (y) => {
            //第一个参数是将x这个promise方法作为this指向,后两个参数分别为成功失败回调
            if (called) return
            called = true
            //因为可能promise中还有promise,所以需要递归
            // resolvePromise(promise2,y,resolve,reject)
            resolve(y)
          },
          (err) => {
            if (called) return
            called = true
            //一次错误就直接返回
            reject(err)
          }
        )
      } else {
        //如果是个普通对象就直接返回resolve作为结果
        resolve(x)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    //这里返回的是非函数,非对象的值,就直接放在promise2的resolve中作为结果
    resolve(x)
  }
}

class MyPromise {
  constructor(executor, name) {
    this.name = name
    //控制状态,使用了一次之后,接下来的都不被使用
    this.status = 'pendding'
    this.value = undefined
    this.reason = undefined
    //存放成功回调的函数
    this.onResolvedCallbacks = []
    //存放失败回调的函数
    this.onRejectedCallbacks = []

    //定义resolve函数
    let resolve = (data) => {
      if (this.status === 'pendding') {
        // this.status = 'resolve'
        rv(data)
      }
    }
    let rv = (data) => {
      this.value = data
      //假如第一次实力的promise的
      if (data instanceof MyPromise) {
        this.status = 'pendding'
        data.then(rv, rj)
      } else {
        this.status = 'resolve'
        //监听回调函数(如果data是promise,则上一个的会等待这个data执行了这个rv方法之后在执行,它本身的成功回调)
        this.onResolvedCallbacks.forEach((fn) => fn())
      }
    }
    //定义reject函数
    let reject = (data) => {
      if (this.status === 'pendding') {
        rj(data)
      }
    }
    let rj = (data) => {
      this.reason = data
      //假如第一次实力的promise的
      if (data instanceof MyPromise) {
        this.status === 'pendding'
        data.then(rv, rj)
      } else {
        this.status = 'reject'
        this.onRejectedCallbacks.forEach((fn) => fn())
      }
    }
    try {
      //将resolve和reject函数给使用者
      executor(resolve, reject)
    } catch (e) {
      //如果在函数中抛出异常则将它注入reject中
      reject(e)
    }
  }
  then(onFulfilled, onRejected) {
    // console.log(onFufilled.a)
    //解决onFufilled,onRejected没有传值的问题
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (y) => y
    //因为错误的值要让后面访问到,所以这里也要跑出个错误,不然会在之后then的resolve中捕获
    onRejected =
      typeof onRejected === 'function'
        ? onRejected
        : (err) => {
            throw err
          }
    //声明一个promise对象
    let promise2
    if (this.status === 'resolve') {
      //因为在.then之后又是一个promise对象,所以这里肯定要返回一个promise对象
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          //因为穿透值的缘故,在默认的跑出一个error后,不能再用下一个的reject来接受,只能通过try,catch
          try {
            //因为有的时候需要判断then中的方法是否返回一个promise对象,所以需要判断
            //如果返回值为promise对象,则需要取出结果当作promise2的resolve结果
            //如果不是,直接作为promise2的resolve结果
            let x = onFulfilled(this.value)
            //抽离出一个公共方法来判断他们是否为promise对象
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
    }
    if (this.status === 'reject') {
      promise2 = new MyPromise((resolve, reject) => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      })
    }
    if (this.status === 'pendding') {
      promise2 = new MyPromise((resolve, reject) => {
        this.onResolvedCallbacks.push(() => {
          // to do....
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          })
        })
      })
    }
    return promise2
  }
  //catch方法
  catch(onRejected) {
    return this.then(null, onRejected)
  }
  finally(callback) {
    let P = this.constructor
    return this.then(
      (value) => P.resolve(callback()).then(() => value),
      (reason) =>
        P.resolve(callback()).then(() => {
          throw reason
        })
    )
  }
  done(onFulfilled, onRejected) {
    this.catch(function (reason) {
      // 抛出一个全局错误
      setTimeout(() => {
        throw reason
      }, 0)
    })
  }
}
//resolve方法
MyPromise.resolve = function (val) {
  return new MyPromise((resolve, reject) => {
    resolve(val)
  })
}
//reject方法
MyPromise.reject = function (val) {
  return new MyPromise((resolve, reject) => {
    reject(val)
  })
}
//race方法
MyPromise.race = function (promises) {
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then(resolve, reject)
    }
  })
}
//all方法(获取所有的promise,都执行then,把结果放到数组,一起返回)
MyPromise.all = function (promises) {
  let arr = []
  let i = 0
  function processData(index, data) {
    arr[index] = data
    i++
    if (i == promises.length) {
      resolve(arr)
    }
  }
  return new MyPromise((resolve, reject) => {
    for (let i = 0; i < promises.length; i++) {
      promises[i].then((data) => {
        processData(i, data)
      }, reject)
    }
  })
}
//promise语法糖 也用来测试
MyPromise.deferred = MyPromise.defer = function () {
  let dfd = {}
  dfd.promise = new MyPromise((resolve, reject) => {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}

//npm install promises-aplus-tests 用来测试自己的promise 符不符合promise规范  使用
module.exports = MyPromise
// export default Promise

ctrip

js
function handleStr(str) {
  let arr = str.split('')
  let nums = ''
  let words = ''

  arr.forEach(function (element) {
    if (/\d/.test(element)) {
      nums += element
    } else if (/[a-zA-Z]/.test(element)) {
      words += element
    }
  })

  return uniqueStr(nums) + words
}
function uniqueStr(str) {
  let arr = str.split('')

  return arr
    .filter(function (element, index) {
      return arr.indexOf(element) === index
    })
    .join('')
}
let str = '携程C2t0r1i8p2020校招'
console.log(handleStr(str))
function handleStr(str) {
  let arr = str.split('')
  let nums = ''
  let words = ''

  arr.forEach(function (element) {
    if (/\d/.test(element)) {
      nums += element
    } else if (/[a-zA-Z]/.test(element)) {
      words += element
    }
  })

  return uniqueStr(nums) + words
}
function uniqueStr(str) {
  let arr = str.split('')

  return arr
    .filter(function (element, index) {
      return arr.indexOf(element) === index
    })
    .join('')
}
let str = '携程C2t0r1i8p2020校招'
console.log(handleStr(str))

写一个函数,列出一个整数所有的分解类型,比如对于数字4,可以做拆分得到下列字符串

js
function f(...args: string[]) {
  let before = args.slice(0, args.length - 1)
  let n = args[args.length - 1]
  for (let i = 1; i < n; i++) {
    f(...before, i, n - i)
  }
  console.log(args)
}
f(9)
function f(...args: string[]) {
  let before = args.slice(0, args.length - 1)
  let n = args[args.length - 1]
  for (let i = 1; i < n; i++) {
    f(...before, i, n - i)
  }
  console.log(args)
}
f(9)

实现flat函数

js
export let arr = [
  1,
  2,
  3,
  4,
  [1, 2, 3, [1, 2, 3, [1, 2, 3]]],
  5,
  'string',
  { name: '弹铁蛋同学' },
]
function flat(arr) {
  return arr.reduce((acc, cur, index) => {
    return acc.concat(Array.isArray(cur) ? flat(cur) : cur)
  }, [])
}
console.log(flat(arr))
export let arr = [
  1,
  2,
  3,
  4,
  [1, 2, 3, [1, 2, 3, [1, 2, 3]]],
  5,
  'string',
  { name: '弹铁蛋同学' },
]
function flat(arr) {
  return arr.reduce((acc, cur, index) => {
    return acc.concat(Array.isArray(cur) ? flat(cur) : cur)
  }, [])
}
console.log(flat(arr))

group

/**

  • 蚂蚁的笔试题

给定整数 n 和 m,写一个函数 dispatch ,把 1-n 尽量平均地分成m个组 如

let n = 2, m = 2;

dispatch(n, m) 得到[[1], [2]]; 我自己实现的太烂了,所以想看看大家有没有什么好的实现方式

js
* @param {number} n
* @param {*} m
 */
function dispatch(n, m) {
  let base = Math.floor(n / m)
  let extra = n - m * base

  return [...Array(m)].map((v, i) => {
    let hasExtra = i < extra
    let jMax = base + (hasExtra ? 1 : 0)
    let baseNum = i * base + 1 + (hasExtra ? i : extra)

    return [...Array(jMax)].map((v, j) => j + baseNum)
  })
}
console.log(dispatch(7, 4))

/**

* @param {number} n
* @param {number} m
 */
function dispatch2(n, m) {
  let i,
    j,
    arrays = []

  for (i = 0; i < m; i++) arrays.push([])

  for (i = 1, j = 0; i <= n; i++, j = (j + 1) % m) arrays[j].push(i)

  return arrays
}
* @param {number} n
* @param {*} m
 */
function dispatch(n, m) {
  let base = Math.floor(n / m)
  let extra = n - m * base

  return [...Array(m)].map((v, i) => {
    let hasExtra = i < extra
    let jMax = base + (hasExtra ? 1 : 0)
    let baseNum = i * base + 1 + (hasExtra ? i : extra)

    return [...Array(jMax)].map((v, j) => j + baseNum)
  })
}
console.log(dispatch(7, 4))

/**

* @param {number} n
* @param {number} m
 */
function dispatch2(n, m) {
  let i,
    j,
    arrays = []

  for (i = 0; i < m; i++) arrays.push([])

  for (i = 1, j = 0; i <= n; i++, j = (j + 1) % m) arrays[j].push(i)

  return arrays
}

柯里化实现

js
let add = (a, b, c, d) => {
  console.log(a + b + c + d)
}
export const curry = (fn, arr = []) => {
  return (...args) => {
    console.log(args)
    console.log(arr)
    //判断参数总数是否和fn参数个数相等
    if ([...arr, ...args].length === fn.length) {
      return fn(...arr, ...args) //拓展参数,调用fn
    } else {
      return curry(fn, [...arr, ...args]) //迭代,传入现有的所有参数
    }
  }
}
const newadd = curry(add)
newadd(1, 2)(3)(4)
let add = (a, b, c, d) => {
  console.log(a + b + c + d)
}
export const curry = (fn, arr = []) => {
  return (...args) => {
    console.log(args)
    console.log(arr)
    //判断参数总数是否和fn参数个数相等
    if ([...arr, ...args].length === fn.length) {
      return fn(...arr, ...args) //拓展参数,调用fn
    } else {
      return curry(fn, [...arr, ...args]) //迭代,传入现有的所有参数
    }
  }
}
const newadd = curry(add)
newadd(1, 2)(3)(4)

Released under the MIT License.