Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lodash源码解读(8)-call真的比apply效率更快吗 #8

Open
sevenCon opened this issue Jul 18, 2019 · 0 comments
Open

lodash源码解读(8)-call真的比apply效率更快吗 #8

sevenCon opened this issue Jul 18, 2019 · 0 comments

Comments

@sevenCon
Copy link
Owner

sevenCon commented Jul 18, 2019

前言

阅读lodash源码的时候, 发现了一个很奇怪的写法, 如下

  function apply(func, thisArg, args) {
    switch (args.length) {
      case 0:
        return func.call(thisArg);
      case 1:
        return func.call(thisArg, args[0]);
      case 2:
        return func.call(thisArg, args[0], args[1]);
      case 3:
        return func.call(thisArg, args[0], args[1], args[2]);
    }
    return func.apply(thisArg, args);
  }

首先这是反柯里化的方法, 这不奇怪, 为了统一调用的代码组织方法.
但是里面的实现, 优先使用call来调用func,直到超过3个参数的时候, 才用apply, 而且使用的参数apply, 为什么不直接使用apply, 而在注释的那里找到了原因, callapply快.

// A faster alternative to `Function#apply`, this function invokes `func` with the `this` binding of `thisArg` and the arguments of `args`.

这么一看, apply传的是数组, 而call, 的参数则是一个萝卜一个坑的, applycall慢, 倒也正常. 毕竟需要检查和遍历赋值到调用函数接收的list.

所以, call 比 apply 速度快多少?

测试一下

function callTest(){
console.time('call  9 prarameters');
var minInNumbers = Math.max.call(Math, 5, 4, 3, 333, -334.33, 323, 55555, -3405, 340359);
console.timeEnd('call  9 prarameters');
}
function applyTest(){
console.time('apply 9 prarameters');
Math.max.apply(Math, [5, 4, 3, 333, -334.33, 323, 55555, -3405, 340359]);
console.timeEnd('apply 9 prarameters');
}
var i = 0;
while(i++<100){
    callTest();
    applyTest();
    console.log(`------------${i}----------`)
}

image

由上可知, 理论上callapply快, 但是很难从程序的角度上去感知. 只能感性去理解了(反正我是测试不出来需要的效果了, 看到其他博主怎么辣么niuB?).

但是, 如果我们转化个角度, call因为在参数上调用, 天然吻合函数参数的list, 而apply函数, 因为参数需要转化, 赋值和类型检查, 耗损时间, 那么通过解构的运算...转化, 是不是就会好些?

解构的call 会比apply 快吗?

const prarameterLength = 100000;
let i = 0;
while (++i < prarameterLength) {
  a.push(Math.random() * 10000);
}

function callTest1() {
  console.time(`call ${prarameterLength} prarameter`);
  var minInNumbers = Math.max.call(Math, ...a);
  console.timeEnd(`call ${prarameterLength} prarameter`);
  return minInNumbers;
}

function applyTest1() {
  console.time(`apply ${prarameterLength} prarameter`);
  var maxInNumbers = Math.max.apply(Math, a);
  console.timeEnd(`apply ${prarameterLength} prarameter`);
  return maxInNumbers;
}

let int = 0;
while (++int < 100) {
  callTest1();
  applyTest1();
  console.log(`-------------${int}--------------`);
}

如果参数的长度在100000那么大的差别下, call 的效率还是比较低的. 但是也可以方向证明, 也只有在巨大的参数数量下, 性能才体现出来. 在参数极少的情形下, 让call和apply之间的做性能对比也只是鸡蛋里面挑骨头, 因为差距几乎可以忽略不计.

image

image

看到ES6的标准内容. apply函数比call的函数多那么一个步骤, ECMA-6的call, ECMA-6的apply,

其中的createListFromArrayLike的方法细节繁多

image

总的来说有那么几条

  • 检查ObjectLike的类型, 是对象进入下一步
  • 新建一个空List
  • 遍历对象, 检查[[key]]是否为基本类型
  • 检查Object[[key]]是否为基本类型
  • append进List, 循环

对比call的方法调用, 没有那么多的类型检查, 这个也是大家觉得性能明细差别的原因.

https://ecma-international.org/ecma-262/6.0/#sec-createlistfromarraylike

https://ecma-international.org/ecma-262/6.0/#sec-function.prototype.call

小结

callapply 更快? 理论上是的, 但是实际也还看计算机的运行状态, 测试之中没有得到我想要的结果,因为参数方式不同, 运行时需要多个参数检查,但是这个影响太细微了,反而 计算机的运行状态对传参是否多几个判断的影响更大, 所以测试不出来

注: 本文的github地址为:lodash源码解读(8)-call真的比apply效率更快吗 如有版权问题,或其他问题, 请给作者留言,感谢!

@sevenCon sevenCon changed the title lodash源码解读(9)-call真的比apply效率更快吗 lodash源码解读(8)-call真的比apply效率更快吗 Jul 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant