原创

js性能测评<apply和call>


让你以最快的速度了解apply,call,bind

var obj = {
    name: "零三"
  }
  function fn(n1,n2) {
    console.log("n1=" + n1 + ";n2="+n2)
    console.log(this)
  }

基本使用

fn(1,2)//结果 n1=1;n2=2  this指向window
fn.apply(obj,[1,2]) // 结果 n1=1;n2=2  this指向obj
fn.call(1,2)//n1=2;n2=undefined this指向1
fn.call(obj,1,2)//n1=1;n2=2 this指向obj

var f = fn.bind(obj,1,2)
f()//n1=1;n2=2 this指向obj

apply和call

  1. fn.call(obj,1,2) 和 fn.apply(obj,[1,2]) 他们执行的结果一样
  2. 在更改this指向的同时,它还会直接执行该方法

bind与apply、call有啥区别

  1. bind是预处理,绑定this之后不会立马执行该方法,它有返回值为一个方法,这个方法就是改变this之后的方法
  2. apply、call只要绑定了this会立即执行

apply和call既然功能一样,那谁的效率高?

  1. call的性能要比apply好,尤其是传递给函数的参数超过3个的时候
  2. 后期开发使用call多一点
  3. 什么时候使用apply? 在你的参数需要为一个数组的时候,这样就没必要拆分数组使用call了,而是直接使用apply,但是也可以使用es6的...运算符拆分数组
  4. 请看下面性能测评

测评apply和call

正常使用apply

  console.time("A")
  var sum = 0
  function fn3(a,b,c,d,e,f,g) {
    var s = a+b+c+d+e+f+g
    sum += s*this.n
  }
  var obj3 = {
    n:2
  }
  for (let i = 0; i < 1000000; i++) {
    fn3.apply(obj3,[i+1,i+2,i+3,i+4,i+5,i+6,i+7])
  }
  console.timeEnd("A")

以上代码测试执行3次,执行时间依次为
A: 57.385986328125ms
A: 57.306884765625ms
A: 57.360107421875ms

正常使用call

  console.time("B")
  var sum = 0
  function fn3(a,b,c,d,e,f,g) {
    var s = a+b+c+d+e+f+g
    sum += s*this.n
  }
  var obj3 = {
    n:2
  }
  for (let i = 0; i < 1000000; i++) {
    fn3.call(obj3,i+1,i+2,i+3,i+4,i+5,i+6,i+7)
  }
  console.timeEnd("B")

以上代码测试执行3次,执行时间依次为
B: 35.02587890625ms
B: 35.66015625ms
B: 34.576904296875ms

拆分数组使用call

 console.time("C")
  var sum = 0
  function fn3(a,b,c,d,e,f,g) {
    var s = a+b+c+d+e+f+g
    sum += s*this.n
  }
  var obj3 = {
    n:2
  }
  for (let i = 0; i < 1000000; i++) {
    fn3.call(obj3,...[i+1,i+2,i+3,i+4,i+5,i+6,i+7])
  }
  console.timeEnd("C")

以上代码测试执行3次,执行时间依次为
C: 58.659912109375ms
C: 59.14111328125ms
C: 58.8359375ms

总结

call优于apply apply优于call拆分数组

吐槽

在正常开发中,对于一般的项目数据处理,使用apply以及call都没太大差别,主要看个人喜好,但是自己心里知道哪个快,哪个效率高,哪个方便,心里有个数即可

练习

function fn1() {
    console.log(1);
    console.log(this)
  }
  function fn2() {
    console.log(2);
    console.log(this)
  }
  1. fn1.call(fn2); // ->1 this指向f2
    原理:首先fn1通过原型链机制找到Function.prototype上的call方法,并且让call方法执行,此时call这个方法中的this就是fn1,在call方法执行过程中,让fn1中this关键字变成fn2,然后让fn1执行
    结论:执行f1方法,f1方法里面的this指向f2

  2. fn1.call.call(fn2); // ->2 this指向window
    原理:首先fn1通过原型链机制找到Function.prototype上的call方法,然后再让call方法通过原型找到call方法
    Function.prototype原型上的call(因为fn.call这个东西也是个函数数据类型),在第二次找到call时,让call方法执行,此时的this是fn1.call。
    结论: fn1.call方法的this为fn2,call(fn2)返回值为一个this指向fn2的方法,而fn2为一个方法此时相当于fn1.call.fn2(),这时候fn1.call为一个方法的属性取值并没有()去执行它,而fn2()却有小括号,所以最终执行了fn2(),然而fn2的this是指向window的并未改变,所以fn2的this指向window

  3. fn1.call.call.call(fn2) 这个原理和上一条是一样的,无论多少个.call,依旧没执行,只执行了最后的括号内的方法

以上为个人观点,如有误或者有歧义请联系作者零三

JavaScript
性能测评
  • 作者:零三(联系作者)
  • 最后更新时间:2020-05-31 22:01
  • 版权声明:自由转载-非商用-非衍生-保持署名
  • 转载声明:来源地址 https://web03.cn