超简洁版手写代码 这次一定记得住(二)

196 阅读2分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

上一期:超简洁版手写代码 这次一定记得住(一)

柯里化

重要程度:⭐⭐

手写频率:⭐⭐⭐

函数柯里化是一个不怎么常用,但是面试的时候经常考察的知识点,分为两种,一种是确定参数数量的版本,一种是不确定参数的版本

确定参数

function curry(fn, ...args) {
  return args.length < fn.length ? curry.bind(null, fn, ...args) : fn(...args)
}

实现的核心就是判断args.lengthfn.length的大小,如果args.length小于fn.length,说明参数还未传入完,那么继续返回带有之前args的函数,否则直接执行fn(...args)

还有一个关键点就是...argsbind,我们用bind将之前的参数传入给返回的函数,不定参数接收传入的所有参数,这样就实现了参数的保存,我们来看看args变化的过程

function curry(fn, ...args) {
  console.log(args)
  return args.length < fn.length ? curry.bind(null, fn, ...args) : fn(...args)
}

function add(a, b, c) {
  return a + b + c
}

const curryAdd = curry(add)

curryAdd(1)(2)(3)
// []
// [ 1 ]
// [ 1, 2 ]
// [ 1, 2, 3 ]

一目了然吧

不定参数

我们以实现add(1, 2)(1).toSum()的效果为例

function add(...args) {
  const sum = args.reduce((acc, cur) => acc + cur)
  function fn(...args2) {
    return add(args2.reduce((acc, cur) => acc + cur) + sum)
  }
  fn.toSum = () => sum
  return fn
}

这里用到了reduce,不熟悉的同学可以先去看看用法 reduce() - MDN

简单解析一下

args.reduce((acc, cur) => acc + cur)

这段代码是用来求传入的args的总和

那么接下来的代码就很容易看懂啦,函数fn就是一个接收新的args2,并加上之前算出的sum作为参数递归调用add函数,这里也用到了闭包

最后,我们需要一个返回结果的函数,直接在fn上定义一个toSum函数,返回计算的结果

各种 API

重要程度:⭐⭐⭐⭐

手写频率:⭐⭐⭐⭐

JS 中的各种 API 不仅非常重要,也是面试常常会考察的

map

Array.prototype.iMap = function iMap(fn, context) {
  return this.reduce(
    (acc, cur, index) => [...acc, fn.call(context, cur, index, this)],
    []
  )
}

reduce

Array.prototype.iReduce = function iReduce(fn, initial) {
  let result = initial || this[0]
  let start = initial ? 0 : 1
  for (let i = start; i < this.length; i++) {
    result = fn(result, this[i], i, this)
  }
  return result
}

filter

Array.prototype.iFilter = function iFilter(fn, context) {
  return this.reduce(
    (acc, cur, index) =>
      fn.call(context, cur, index, this) ? [...acc, cur] : acc,
    []
  )
}

flat

Array.prototype.iFlat = function iFlat(depth = 1) {
  let arr = this
  while (depth && arr.some(Array.isArray)) {
    arr = [].concat(...arr)
    depth -= 1
  }
  return arr
}

call

Function.prototype.iCall = function iCall(context = window, ...args) {
  const tmpKey = Symbol('tmp')
  context[tmpKey] = this
  const result = context[tmpKey](...args)
  delete context[tmpKey]
  return result
}

apply

Function.prototype.iApply = function iApply(context = window, args) {
  const tmpKey = Symbol('tmp')
  context[tmpKey] = this
  const result = context[tmpKey](...args)
  delete context[tmpKey]
  return result
}

bind

Function.prototype.iBind = function iBind(context, ...args) {
  return (...rest) => this.apply(context, [...args, ...rest])
}

Object.create

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的、__proto__。

Object.iCreate = function iCreate(proto) {
  function Noop() {}
  Noop.prototype = proto
  Noop.prototype.constructor = Noop
  return new Noop()
}

new

new 运算符用来创建用户自定义的对象类型的实例或者具有构造函数的内置对象的实例。

实现要点:

  • 如果构造函数返回的结果是引用数据类型就返回该引用类型 不是引用类型就返回实例
function iNew(Constructor, ...args) {
  const instance = Object.create(Constructor.prototype) // 创建 + 绑定原型
  const result = Constructor.call(instance, ...args)

  // 如果构造函数返回的结果是引用数据类型就返回该引用类型 不是引用类型就返回实例
  return result instanceof Object ? result : instance
}

instanceof

用迭代的方法实现

function iInstanceof(left, right) {
  let proto = Object.getPrototypeOf(left)
  while (true) {
    if (proto === null) return false
    if (proto === right.prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
}