# 实现 apply

# 实现要点

  1. 属性同名覆盖

    • ES5 随机数 + 缓存原始值
    • ES6 Symbol
  2. 参数传递

    • ES5 new Function
    • ES6 扩展运算符 (...)

注意:ES5 参数传递 使用了 new Function 而不是 eval ,是因为面试官或许会要求不使用 eval 如何实现?!

# 代码实现

提示: ES5 和 ES6 代码对参数的处理是一致的

# 👩‍💻👨‍💻 ES5

Function.prototype.apply3 = Function.prototype.apply || function (that, args) {
  var target = this
  if (typeof target !== 'function') {
    throw new TypeError('Function.prototype.apply3 called on incompatible ' + target)
  }

  if (args === 'undefined' || args === null) {
    args = []
  }

  if (args !== new Object(args)) {
    throw new TypeError('CreateListFromArrayLike called on non-object')
  }

  if (typeof that === 'undefined' || that === null) {
    that = typeof window !== 'undefined' ? window : global
  }

  that = new Object(that)

  // 与 ES6 不同的实现
  var fn = '$' + +new Date()
  var originalVal = that[fn]
  var hasOriginalVal = that.hasOwnProperty(fn)
  that[fn] = target
  var argsLength = args.length
  var spreadArgs = new Array(argsLength)
  for (let i = 0; i < argsLength; i++) {
    spreadArgs[i] = 'args[' + i + ']'
  }
  var result = Function('obj', 'fn', 'args', 'return obj[fn](' + spreadArgs.join(',') + ')')(that, fn, args)
  delete that[fn]
  if (hasOriginalVal) {
    that[fn] = originalVal
  }
  return result
}

# 👩‍💻👨‍💻 ES6

Function.prototype.apply3 = Function.prototype.apply || function (that, args) {
  const target = this
  if (typeof target !== 'function') {
    throw new TypeError('Function.prototype.apply3 called on incompatible ' + target)
  }

  if (args === 'undefined' || args === null) {
    args = []
  }

  if (args !== new Object(args)) {
    throw new TypeError('CreateListFromArrayLike called on non-object')
  }

  if (typeof that === 'undefined' || that === null) {
    that = typeof window !== 'undefined' ? window : global
  }

  that = new Object(that)

  // 与 ES5 不同的实现
  const fn = Symbol()
  that[fn] = target
  const result = that[fn](...args)
  delete that[fn]
  return result
}

# 参考: