当 apply、call 和 bind 开始“内卷”:JavaScript 函数调用的那些事儿

160 阅读4分钟

引言

嘿!如果你正在准备 JavaScript 面试或者已经在日常编码中遇到过applycallbind这三个家伙,那今天就是你的幸运日。在这篇文章中我们将一起揭开它们神秘的底层原理,看看它们到底有什么区别,为什么有些面试官会执着于这三个毫不起眼的方法?

改变 this 的指向

通过改变this的指向,我们可以更加灵活地处理函数调用,使代码更加模块化、可重用,并且更容易维护。而applycallbind恰好就是改变this指向的一把好手,那么我们接下来就分别对它们进行介绍。

call 方法

call 方法允许我们指定一个对象作为当前正在执行的函数内部 this 的值,并立即调用该函数。此外,call 还能接收额外的参数,这些参数会按顺序传递给被调用的函数。例如:

var a = {
    name: 'Cherry',
    fn: function(a, b){
        console.log(this.name); // 输出 "Cherry"
        console.log(a + b);     // 输出 3
    }
}
var b = a.fn;
b.call(a, 1, 2); // 模板:call(thisArg, arg1, arg2, ...) 

在这里我们通过callthis指向了a,并且传入(1,2)两个参数执行输出参数之和

apply 方法

apply 方法与 call 类似,同样用于改变函数执行时的 this 值并立即调用函数。不过,apply 接受的是一个参数数组(或类数组对象),而不是一系列独立的参数。例如:

// 在上述代码中的call替换为
console.log(b.apply(a, [1, 2])); // 模板:apply(thisArg, [argsArray])

bind 方法

bind 创建一个新的函数实例,在这个新函数中,this 被永久绑定到指定的对象上。这使得我们可以创建预设了部分参数的新函数,而不需要立即执行它们。例如:

// 替换为以下代码
console.log(b.bind(a, 1, 2)()); // 输出 "Cherry" 和 3

假如我们要决定一个方法去邀请别人来参加活动,而applycallbind就像是三种不同的邀请方式,例如:

  • apply喜欢直接邀请一群人(参数数组)一起出现。
  • call则更倾向于直接点名邀请(一个个列出参数)。
  • bind先不说定邀请谁,而是把邀请函留着(创建函数),等到时候再揭晓(再看是否调用)。

这样我们就初步了解了applycallbind这三个方法各自的作用,但是我们不妨将它们串起来,看下会摩擦出什么样的火花吧

示例:

来看下面的一串代码:

var a = {
    name: 'Cherry',
    fn: function(a, b){
        console.log(this.name)
        console.log(a + b)
    }
}
var b = a.fn; // 普通函数

console.log(b.apply(a, [1, 2]))
console.log(b.call(a, [1, 2]))
console.log(b.bind(a, 1, 2))

在上述代码中我们发现调用这三个方法的形态极其相似,那么打印结果有什么区别吗?

image.png

我们可以看到this的指向都没有问题,通过这三个方法我们的成功让this指向了a。但是我们也发现了一丝异常。

  • 异常一 : 为什么会打印这么多undefined
  • 异常二 : 调用call()的结果为什么是打印了一串字符串?

异常一:

首先先打印undefined是因为我们通过这句代码var b = a.fn;将b定义为了函数,但是我们发现在a中的fn函数本身没有返回任何值,所以就会隐式返回undefined,在进行输出时就会将undefined打印出。如果改为:

b.apply(a, [1, 2]);
b.call(a, [1, 2]);
b.bind(a, 1, 2)(); 

这样就不会打印undefined了。

异常二:

我们知道call()对于参数是一个一个调用的,但是我们这里直接传入一个数组进去,这里就发生了变化,执行fn()时的参数就变成了fn(a=[1,2],b),对于b我们并没有传参进去,所以其默认为undefined,然后对ab进行加法运算符,但是数组和undefined怎么加呀?

在 JS 中,当数组与其他类型进行加法运算时,数组会被隐式地执行 [].toString()转化为字符串。同理undefinedstring进行加法运算时,undefined会被隐式地执行 String(undefined)转化为字符串。所以加法的功能就从数字相加变成了拼接字符串,即"1,2"+"undefined"。这就有了最后输出的"1,2undefined"

结论:

回顾上述内容,thiscallapplybind 都是 JavaScript 中非常重要的概念。熟练运用它们可以帮助我们更好地控制函数的执行环境,从而编写出更加优雅和高效的代码。

---欢迎各位点赞、收藏、关注,如果觉得有收获或者需要改进的地方,希望评论在下方,不定期更新

0bae-hcffhsw0416753.gif