Skip to content

深入理解 JavaScript 之手写 call, apply, bind 方法

首先我们先学习下,在ECMAScript 5中,函数内部存在两个特殊的对象:arguments和this

  • arguments:它是一个类数组对象,包含调用函数时传入的所有参数。这个对象只有以function关键字定义函数(相对于使用箭头语法创建函数)时才会有。
  • this: 在标准函数中,this引用的是把函数当成方法调用的上下文对象,这时候通常称其为this值(在网页的全局上下文中调用函数时,this指向windows)​。 这是老生常谈的手写了,今天想自己试着实现一下,做个笔记。

call 方法

javascript
Function.prototype.myCall = function (proxyObj) {
  if (proxyObj === undefined || proxyObj === null) {
    proxyObj = window //  null 和 undefined   this 值指向全局对象
  } else {
    proxyObj = Object(proxyObj) // 值为原始值的 this 会指向该原始值的实例对象
  }

  const funName = Symbol('uniqueName') // 用 Symbol 是防止跟上下文的原属性冲突
  proxyObj[funName] = this
  let arg = [...arguments].slice(1)
  let result = proxyObj[funName](...arg)
  delete proxyObj[funName]
  return result
}

apply 方法

javascript
Function.prototype.myApply = function (proxyObj) {
  if (proxyObj === undefined || proxyObj === null) {
    proxyObj = window
  } else {
    proxyObj = Object(proxyObj)
  }
  const fn = Symbol('uniqueName')
  proxyObj[fn] = this
  let args = arguments[1]
  let result
  if (!Array.isArray(args)) {
    throw new Error('参数错误') // 第二个参数必须是数组
  } else {
    args = Array.from(args) // 转为数组
    result = proxyObj[fn](...args)
  }
  delete proxyObj[fn]
  return result
}

bind 方法

javascript
Function.prototype.myBind = function (proxyObj) {
  if (typeof this !== 'function') {
    throw new Error('参数错误')
  }
  let self = this
  let args = [...arguments].slice(1)
  let fn = function (...innerArg) {
    const isNew = this instanceof fn 
    return self.apply(isNew ? this : Object(proxyObj), args.concat(innerArg)) 
  }
  if (self.prototype) {
    fn.prototype = Object.create(self.prototype)
  }
  return fn 
}