引言
你是否留意到,每逢 JavaScript 面试,考官总爱询问 call、bind 和 apply 的差异?仿佛这三个方法已成为关键凭证,一旦精通,即可轻松通过。其实道理很简单,考官提出这些疑问,不仅意在检测你对 this 的掌握程度,更意在评估你是否能够巧妙运用这些技巧。下面,让我们逐个剖析这三位面试中的常见问题,以便你在下次面对时能够从容应对。
我们先来看一段代码
var a = {
name: 'Cherry',
fn: function(a, b) {
console.log(this.name)
return a + b;
}
}
var b = a.fn;
执行函数b时,将a作为上下文,并将数组[1, 2]作为参数列表传入,随后将结果输出到控制台
console.log(b.call(a, 1, 2))
调用b函数时,将a对象作为上下文,同时传入一个包含数字1和2的数组作为参数,然后在控制台输出结果
调用函数b时绑定了对象a和参数1,2,然后执行了该函数,最后将结果输出
输出结果
能够发现,在运用.apply和.call传递参数时,首个参数用于明确this的指向,当传递a,b的值时,.apply以数组形态传递能够准确输出a+b的值为3,而使用.call却显示1,2undefined,并且采用.bind传递时需要在其后补充一个括号,这是何故呢?
这就需要考虑:
调用方法时参数的传递形式存在不同,其中call方法会单独发送每一个参数,而apply方法则将所有参数打包在一个数组中发送,bind方法则用于创建一个新函数,该函数在执行时会将指定参数绑定到函数内部,这些方法在处理函数调用时各有特点
var b = a.fn;
调用b时绑定a作为上下文,传入参数1和2,结果为Cherry 3
调用b时绑定a作为上下文,参数为包含1和2的数组,结果会显示1,2,undefined
2..apply() —以数组的形式传递参数
调用函数b时,将对象a作为上下文,同时传入参数1和2,结果为Cherry 3
区别:
3..bind():—返回一个新函数,参数以数组的形式传递
绑定方法会生成一个全新函数,这个函数预设了特定的上下文对象和接收到的初始参数。一旦这个新生成的函数被触发执行,预设的上下文对象和参数就会原封不动地传递给原始函数。
调用b时绑定a并传递参数1和2,然后执行该函数,结果为Cherry 3
区别:
执行时机的区别
参数的传递方法不一样,.call()、.apply() 和 .bind() 最大的不同点在于它们运作的时刻,这一点也很重要。
1..call() 和 .apply():立即执行函数
调用方法会立刻运行目标函数,同时明确指定该函数的执行环境,并传递给函数所需的参数值,这是这两种方法的主要作用。
代码示例:
function greet(age, city) {
输出这个人的姓名,他现在多少岁,以及他的居住城市,姓名是 this.name,年龄是 age,城市是 city。
}
那个人的名字是Cherry,他是个对象,里面包含姓名属性,值是Cherry,这个对象被赋值给了变量person
打招呼,称呼那个人,年龄是三十岁,居住在纽约;立刻进行,显示:樱桃三十岁了,住在纽约。
调用greet函数时,将参数30和'New York'传递给person对象,该函数会立刻运行,并显示Cherry已经30岁了,她的居住地是纽约
2..bind():返回一个新函数,延迟执行
绑定方法与调用方法及适用方法三者之间最显著的差异在于它不会立刻实施函数,而是产出另一个函数,这个新函数在将来被调用时才会实施。
代码示例:
function greet(age, city) {
console.log(`${this.name} is ${age} years old and lives in ${city}`);
}
那个人的名字是艾丽斯,她是一个对象,里面包含着姓名信息
创建一个绑定函数,将greet函数与person对象以及两个参数30和New York关联起来,这个绑定函数就是boundGreet
启动问候功能,稍后显示,内容为:艾丽斯已经三十岁了,居住在纽约
3.实际应用示例

让我们再举一个更为繁杂的案例,用以说明 .call()、.apply() 以及 .bind() 在非同步场景下的具体运用,尤其要关注它们在 setTimeout 函数中的运行效果。
代码示例: 使用 .call()(会立即执行)
var obj = {
name: "Cherry",
func1: function() {
显示 obj 的 name 值,通过调用当前上下文的 name 属性实现,结果输出到控制台
},
func2: function() {
setTimeout(function() {
该函数被调用, 它的执行环境是 setTimeout 的主体, 而不是 obj 对象, 这是其固有的行为模式
}.call(obj), 5000);
}
}
obj.func2, 立即会显示Cherry, 但不会在五秒之后进行
解释:
修正:使用 .bind()(延迟执行)
var obj = {
name: "Cherry",
func1: function() {
console.log(this.name); // 输出 obj 的 name 属性
},
func2: function() {
setTimeout(function() {
这个方法调用时,要借助 .bind() 技术,确保内部 this 始终指向 obj 对象,这样就不会出现混淆
}.bind(obj), 5000);
}
}
obj的方法二被调用时,func1的上下文会准确绑定到obj上,随后经过五秒钟的延时,将会打印出字符串Cherry
解释:
总结
特性
.call()
.apply()
.bind()
执行时机
立即执行函数
立即执行函数
返回一个新函数,需要手动调用
参数传递方式
单独列出参数
参数以数组的形式传递
参数以数组的形式传递,返回新函数
返回值
返回函数执行的结果
返回函数执行的结果
返回一个新的函数
主要用途
确定 this,并立即调用函数
确定 this,并立即调用函数
确定 this,并返回一个新的函数,在后续调用时执行
理解这些不同点能让你在开发中针对具体要求选用恰当的方法,特别是在应对非同步任务和函数调用环境时。
Copyright C 2018 All Rights Reserved 版权所有 聚贤人力 皖ICP备20008326号-40
地址:安徽省合肥市高新技术开发区人力资源产业园 EMAIL:qlwl@foxmail.com
Powered by PHPYun.