call、apply、bind的实现

59 阅读1分钟

call

Function.prototype.myCall = function (context, ...args) {
  const fn = this // 通过this获取调用call的函数
  if (typeof fn !== 'function') {
    throw new Error('caller is required function')
  }
  context = context === null || context === void 0 ? window : Object(context)
  const symbol = Symbol('fn')
  context[symbol] = fn // 防止与context属性名冲突
  const result = context[symbol](...args)
  delete context[symbol]
  return result
}

foo.myCall(2)
foo.myCall('2')
foo.myCall(null)
foo.myCall(obj)
  1. call的第一个参数为上下文this
  2. 第一个参数为null/undefinedthis指向window
  3. 第一个参数也可以是基本类型,要转为对象类型;使用Object()
  4. 可以有返回值

apply

Function.prototype.myApply = function (context, ...args) {
  const fn = this // 通过this获取调用call的函数
  if (typeof fn !== 'function') {
    throw new Error('caller is required function')
  }
  context = context === null || context === void 0 ? window : Object(context)
  const symbol = Symbol('fn')
  context[symbol] = fn

  const result = context[symbol](args) // apply的参数是数组
  delete context[symbol]
  return result
}
  1. apply的第二个参数是数组

bind

基础版

Function.prototype.myBind = function (context, ...args) {
  const fn = this

  return function (...innerArgs) {
    const result = fn.apply(context, [...args, ...innerArgs])
    return result
  }
}
  1. bind返回值是函数
  2. 返回的函数可以继续传参
  3. 返回的函数可以作为构造函数使用,绑定的this会失效

添加可以作为构造函数

Function.prototype.myBind2 = function (context, ...args) {
  const fn = this
  function fBound(...innerArgs) {
    context === this instanceof fBound ? this : context
    const result = fn.apply(context, [...args, ...innerArgs])
    return result
  }
  fBound.prototype = fn.prototype
  return fBound
}

使用

const b = bar.myBind2(2, 1)
b.prototype.name = 'tianzhen'
const _b = new b(2)
console.log(_b, _b.name)

存在的问题

const b2 = bar.myBind2(2, 1)
const _b2 = new b(2)
console.log(_b2.name) // 也能找到name: tianzhen
b2.prototype.name = 'lisi'
console.log('b1.name', _b.name) // _b.name也会修改为lisi

fBound.prototype = fn.prototype; 修改fBound.prototype也会修改构造函数的Prototype

bind(优化版)

Function.prototype.myBind2 = function (context, ...args) {
  const fn = this
  function F() {}
  F.prototype = fn.prototype
  function fBound(...innerArgs) {
    context === this instanceof fBound ? this : context
    const result = fn.apply(context, [...args, ...innerArgs])
    return result
  }
  fBound.prototype = new F()
  return fBound
}

这里利用了原型式继承(道格拉斯提出)