JavaScript 之实现 call apply

2019-11-08  本文已影响0人  临安linan

[个人博客]:(https://github.com/zenglinan/blog)

如果对你有帮助,欢迎star。

实现call

call可以指定函数调用的this, 并传入多个参数执行

举个例子:

var a = {
  val: 1
}
var b = {
  fn: function(){
    console.log(this.val)
  }
}

b.fn.call(a)

观察一下call的调用形式, 只需将call指定的this提前到b前面, 即: a.fn()

但是a中并没有fn这个属性, 所以还需将fn属性添加到a中

总结一下: 只需要在a中添加fn, 然后执行a.fn, 然后删除这个属性(ÒωÓױ!必须要删除啊, 不然a会凭空多了个属性)

实现过程:

Function.prototype.call = function(target){
  target.fn = this  // 这里fn随便取名字
  target.fn()
  delete target.fn
}

基础版完成! 但还需支持传参, 原版的call除了传this还可以传函数的参数

但是这里不知道函数的参数有多少个, 这里可以借助arguments来取出参数

Function.prototype.call = function(target){
  const arg = []
  for(let i = 1; i < arguments.length; i ++){
    arg.push(arguments[i])
  }
  target.fn = this
  target.fn(...arg)  // 因为不知道有几个参数, 这里用...arg取出所有参数, 当然也可以用eval(还是算了 = =)
  delete target.fn
}

这里就实现了传参了, 但还差点意思, 当call传的是null或者undefined的时候应该返回window

Function.prototype.call = function(target){
  target = target || window  // 判断target为null或undefined时设为window
  const arg = []
  for(let i = 1; i < arguments.length; i ++){
    arg.push(arguments[i])
  }
  target.fn = this
  target.fn(...arg)
  delete target.fn
}

最后差一步, 原生的call是有返回值的, 这里我们给call设一下返回值

Function.prototype.call = function(target){
  target = target || window
  const arg = []
  let result
  for(let i = 1; i < arguments.length; i ++){
    arg.push(arguments[i])
  }
  target.fn = this
  result = target.fn(...arg)
  delete target.fn
  return result
}

基本完成(), 但是以上方法还存在的不足是: 当传入基础类型时, 原生的call对基础类型进行了类包装

比如将字符串包装成new String("xxx")的包装类对象

实现apply

跟call类似, 无非是参数形式变了, apply 里要求第二个参数形式是数组

Function.prototype.apply = function(target, arr){
  target = target || window
  let result
  target.fn = this
  if(!arr){
    result = target.fn() 
  }else{
    const arg = []
    for(let i = 0; i < arr.length; i ++){
      arg.push(arr[i])
    }
    result = target.fn(...arg)
  }
    delete target.fn
    return result
}
上一篇下一篇

猜你喜欢

热点阅读