# 通过call实现bind

Function.prototype.newBind = function (...arr) {
    let that = this
    return function () {
        that.call(...arr)
    }
}

obj = {
    a: 1
}

function say(a, b, c) {
    console.log(this)
    console.log(a, b, c)
}
say.newBind(obj, 1, 2, 3)()
//{ a: 1 }
//1 2 3

# 手写call

//通过对象调用方法来修改this指向
Function.prototype.call2 = function (context){
  const ctx = context || window
  ctx.func = this //调用call2时,this是调用call2的方法(call2是在函数对象上)
  //测试用例中这个this打印出来是:[function a]
  const args = Array.from(arguments).slice(1)//保存参数
  //通过在ctx中新建一个 函数对象等于调用时的对象 来调用执行来修改this指向
  const res = arguments.length > 1 ? ctx.func(...args) : ctx.func()
  delete ctx.func//避免造成全局污染
  return res
}

//测试用例:
obj={c:2}
function a(x,y){console.log(this,x,y)}
a.call(obj,1,2)//{c:2} 1 2
a.call2(obj,1,2)//{c:2,func:[function a]} 1 2

# 手写apply

Function.prototype.apply2 = function (context){
  const ctx = context || window
  ctx.func = this
  //测试用例中这个this打印出来是:[function a]
  //通过在ctx中新建一个 函数对象等于调用时的对象 来调用执行来修改this指向
  const res = arguments[1] ? ctx.func(...arguments[1]) : ctx.func()
  delete ctx.func//避免造成全局污染
  return res
}

//测试用例:
obj={c:2}
function a(x,y){console.log(this,x,y)}
a.call(obj,[1,2])//{c:2} 1 2
a.call2(obj,[1,2])//{c:2,func:[function a]} 1 2

# 手写bind

Function.prototype.bind2 = function (context) {
  //对context进行深拷贝,防止bind执行后返回函数未执行期间,context被修改
  const ctx = JSON.parse(JSON.stringify(context)) || window
  ctx.func = this
  const args = Array.from(arguments).slice(1)
  return function () {
    //注意bind方法需要合并两次函数执行的参数
    const Allargs = args.concat(Array.from(arguments))
    return Allargs.length > 0 ? ctx.func(...Allargs) : ctx.func()
  }
}

//测试
obj = { c: 2 }
function a(x,y,z) { console.log(this, x, y, z) }
a.bind(obj,1,2)(3)//{c:2} 1 2 3
a.bind2(obj,1,2)(3)//{c:2,func:[function a]} 1 2 3

参考:https://segmentfault.com/a/1190000018383649

Last Updated: 8/1/2021, 1:43:20 PM