JavaScript的call()和apply()的用法及模拟实现
call()方法使用一个指定的this值和一个或多个参数来调用一个函数;
var value = 2;
var bar = {
value: 1;
}
function foo(name) {
console.log(this.value + '-' + name);
}
foo('Lora'); //2-Lora 此时foo的this指向全局的window对象
foo.call(bar, 'Lora'); //1-Lora 使用call()方法调用了foo方法,并将foo的this指向bar
下面我们来一步步实现call()
第一步,实现通过指定的this值来调用函数。
Function.prototype.myCall = function (context) {
context.fn = this; // 这里的this指向将来调用myCall()的那个函数
context.fn();
delete context.fn;
}
foo.myCall(bar) //1
- 为
bar添加属性fn,将foo赋值给bar.fn; - 调用函数
bar.fn,此时函数内部的this指向的就是bar; - 函数调用完成后,删除属性
bar.fn;
第二步,实现传递参数
由于不清楚要传的参数具体有多少个,所以myCall的形参就只写一个context,来接收要指定的this值,其余参数通过arguments来获取;
Function.prototype.myCall = function (context) {
context.fn = this;
var args = []; //使用args来接收除了context以外的参数
for (var i = 1; i < arguments.length; i++) {
//因为arguments的第一个元素是参数context,所以从i = 1开始
args.push(arguments[i]);
}
context.fn(...args);
delete context.fn;
}
//测试一下
function f(name, age) {
console.log(`${this.vlaue}-${name}-${age}`);
}
f.myCall(bar, 'Lora', 20); //1-Lora-20
第三步,优化
this参数可以传null,当为null的时候,视为指向window;- 有些时候函数是有返回值的,而不仅仅是处理一些逻辑,例如:
function p(name, age) {
return { name, age };
}
console.log(p('Lora', 20)); //{ name: 'Lora', age: 20 }
上面这个函数返回的就是一个对象。
现在我们针对上面两个问题进行优化;
Function.prototype.myCall = function (context) {
var context = context || window;
context.fn = this;
var args = [];
for (var i = 1; i < arguments.length; i++) {
args.push(arguments[i]);
}
var res = context.fn(...args)
delete context.fn;
return res;
}
//测试一下
function p2(name, age) {
return {
value: this.value,
name,
age
}
}
var result = p2.myCall(bar, 'Lora', 20);
console.log(result); //{value: 1, name: 'Lora', age: 20}
至此,模拟实现call()方法已全部完成。
apply()的模拟实现
apply()的用法与call()类似,区别在于传递参数的类型不同。
apply()的第一个参数为指定的this,剩余参数以一个数组的形式传递,如下:
function foo(name, age) {
console.log(`${this.value}-${name}-${age}`)
}
var bar = {
value: 1
}
foo.apply(bar, ['Lora', 20]); //1-Lora-20
因此只需在myCall()的基础上,对参数的处理做下改动即可,如下:
Function.prototype.myApply = function (context, array) {
var context = context || window;
context.fn = this;
var res;
//判断是否传入数组
if (!array) {
res = context.fn();
} else {
res = context.fn(...array);
}
delete context.fn;
return res;
}
//测试一下
function testApply(name, age) {
return {
value: this.value,
name,
age
}
}
var result = testApply.myApply(bar, ['Lora', 20]);
console.log(result); //{value: 1, name: 'Lora', age: 20}
完结
本篇文章主要内容是如何手写call()和apply(),毕竟是面试中会经常出现的。
关于call()和apply()的更多应用场景,请参看MDN文档:

浙公网安备 33010602011771号