call,bind,apply

131 阅读1分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

我们之前提到了函数动态this的特性,改变this的方法和锁定this的方法。

今天我们继续介绍 改变this方法的call, apply, bind.

call

参数

function.call(thisArg, arg1, arg2, ...)

  • thisArg
    函数运行时使用的 this 值
  • arg1, arg2, ...
    其他参数

来看一个sum的方法:

function sum(num1, num2){
    return (num1 + num2) * this.rate
}

sum.call({rate:1.5}, 100, 200)
// 450

看看第一个参数传null:

function sum(num1, num2){
    return (num1 + num2) * this.rate
}
sum.call(null, 100, 200)
// NaN

再看看严格模式null:

"use strict"
function sum(num1, num2){
    return (num1 + num2) * this.rate
}
sum.call( null, 100, 200)
// VM1673:3 Uncaught TypeError: Cannot read properties of null (reading 'rate')

sum.call( undefined, 100, 200)
// VM1673:3 Uncaught TypeError: Cannot read properties of undefined (reading 'rate')

严格模式下, nullundefined是不会被包装的。

相反的,非严格模式下,非引用数据类型会被包装为对象。

function log(){
    console.log(this)
}

log.call(1)
// Number {1}
log.call(false)
// Boolean {false}
log.call("")
// String {''}

可能你到这里觉得很简单,我们看一道题:

function a(){ 
    console.log(this,'a')
};
function b(){
    console.log(this,'b')
}
a.call.call(b,'b')  
a.call.call.call(b,'b')   
a.call.call.call.call(b,'b')

apply

appy和call非常的类似,除了第一个参数意外的参数,是以数组的形式传递的。

function sum(num1, num2){
    return num1 + num2
}

sum.apply(null, [1,2]);
// 3

后来,我看nodejsevents模块的源码时候发现, 再执行函数调用的时候,对四个参数以内的调用都是使用call,而不是简单粗暴的使用apply, 后来一查,原来有call的性能高于apply的说法。 我们今天就一起来验证一下:

function sum(){   
   return arguments.length
}


console.time("call")
for(let i=0; i< 10000000; i++){
    sum.call(null, 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20)
}
console.timeEnd("call")

console.time("apply")
for(let i=0; i< 10000000; i++){
    sum.apply(null, [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])
}
console.timeEnd("apply")

// call: 111.688232421875 ms
// apply: 154.789794921875 ms

// call: 114.31103515625 ms
// apply: 157.2490234375 ms

// call: 113.232177734375 ms
// apply: 160.802978515625 ms

循环次数,1万以内都不明显, 1000万后,还是比较明显的。

bind

bind和上面两个就有些区别得了,其返回的是一个函数,还可以提前绑定参数,非常的强大,比起用闭包来复用,我更喜欢这款。

function multi(a, b){
    return a*b
}

function getDoubleFunction(multiple){
   return multi.bind(null, multiple)
}

const m100 = getDoubleFunction(100)(100);
m100(10)
// 1000

const m15 = getDoubleFunction(100);
m15(10)
// 150

还有一点就是多次bind,第一个bind的this生效。

function log(){
    console.log(this);
}

log.bind({a:1}).bind({a:2})();
// {a:2}

小结

今天你收获了吗?

引用

call, call.call, call.call.call, 你也许还不懂这疯狂的call
call和apply的性能对比