乐趣区

callapply比我想象的要难

问题由来:当我看到这句代码的时候

Function.prototype.apply.call(Math.floor, undefined, [1.75])

普通人的第一反应:执行 Math.floor.apply(undefined,[1.75])
OK, 你为什么会觉得是这样执行?
会不会是因为代码中,恰巧给你标记好了 undefined,让你有的答案?
那么假如代码本身是这样子:

Function.prototype.apply.call(undefined,Math.floor, [1.75])

或者

Function.prototype.apply.call(Math.floor, Math.ceil, [1.75])

这时候,你是不是也能立刻给出答案?
反正我是没有立马搞清楚。
那么事实究竟如何?
call 和 apply 不是很简单嘛,无非就是改变 this 指向,然后 aplly 传参为数组罢了。
对,它们就是这么简单。
看看源码

// ES3 call 实现
Function.prototype.es3call = function (context) {
  var content = context || window;
  content.fn = this;// 注意这一行
  var args = [];
  // arguments 是传入的参数 (实参) 的类数组对象 函数自带
  for (var i = 1, len = arguments.length ; i < len; i++) {args.push('arguments[' + i + ']');
  }
  var result = eval('content.fn('+args+')');// 注意这一行
  delete content.fn;
  return result;
}
// ES3 apply 实现
Function.prototype.es3apply = function (context, arr) {
  var context = context || window;
  context.fn = this;// 注意这一行
  var result;
  if (!arr) {result = context.fn();
  } else {
    // 获取参数
    var args = [];
    for (var i = 0, len = arr.length; i < len; i++) {args.push('arr[' + i + ']');
    }
    // 执行函数
    result = eval('context.fn(' + args + ')')// 注意这一行
  }
  delete context.fn;
  return result
}

其中有两行需要注意:
1、context.fn=this
2、result=eval(‘context.fn(‘ + args + ‘)’)
可以看出 call 和 apply 做了两件很重要的事:
1、代码在给 context 绑定 this,而我们都知道,this 指向的是当前执行上下文
比如:

function test(){}
test.call()

由于是由 test 调用的 call 方法,因此,this 指向的是 test
2、代码 context.fn(),无非就是 this()

那么上面的代码

Function.prototype.apply.call(Math.floor, undefined, [1.75])

正确打开方式:
1、Function.prototype.apply 本身就是一个函数,相当于 test,我们给它起个昵称 FunctionApply,相当于

FunctionApply.call(Math.floor, undefined, [1.75])

2、call 方法无非就是在给 FunctionApply 绑定 this 指向 Math.floor

牢记
FunctionApply(Function.prototype.apply) 的 this 指向 Math.floor
牢记
FunctionApply(Function.prototype.apply) 的 this 指向 Math.floor
牢记
FunctionApply(Function.prototype.apply) 的 this 指向 Math.floor

相当于执行

Function.prototype.apply(undefined, [1.75])

这不就是一个普通函数执行嘛。

但是别忘了刚刚提到的。
call 和 apply 做了两件最重要的事情:
1、绑定 this
2、this()
而 Function.prototype.apply 的 this 指向的 Math.floor
因此最终才是执行的 Math.floor(1.75)

退出移动版