一道call()函数的面试题
call()函数能改变this指针的指向,能方便的为对象扩展方法,在实际项目中属于重度应用的方法。
下面来看题目:
function fn1() {console.log(1);}
function fn2() {console.log(2);}
fn1.call(fn2);
fn1.call.call(fn2);
fn1.call.call.call.call(fn2);
fn2.call();
Function.prototype.call(fn2);
Function.prototype.call.call.call(fn2);
Function.prototype.call.call.call(fn1);
//Array.prototype.call([1,2,3]); // 所以这样调用会报 TypeError 错误,Array.prototype.call is not a function
-
fn1.call(fn2);
这样的调用方式大家应该比较熟悉。call()函数的第一个参数应该是对象,fn2是函数,在Js中函数的本质也是对象;所以就是在fn2对象上调用fn1方法(注意fn2上本来是没有fn1这个方法的,调用call时会给fn2临时添加一个属性,它的值就是fn1方法的地址),等同的效果就是直接执行fn1(); -
fn2.call();
call()可以不传参,这时候默认的就是全局Global对象,web环境中全局Global对象就是window。这里假定是web环境,所以相当于fn1.call(window);fn1.call(window);这个比较简单啦,这就是call的普通用法,等价于window.fn1();,实际上就是调用fn1()函数。
-
Function.prototype.call(fn2);
首先,Function的原型比较特殊,它的原型是匿名空函数,别的类型的原型对象都是对象。
所以这行代码表示在fn2上调用匿名空函数,所以就不会输出内容。 -
Function.prototype.call.call.call(fn2);,实际上和fn1.call.call(fn2)类似
- 可以拆分一下,这样容易说明问题
(Function.prototype.call.call).call(fn2);, 我们把Function.prototype.call.call给扩了起来。 - 来看
Function.prototype.call.call,可以理解为Function.prototype.call的call方法;Function.prototype.call本身也是一个函数,如果不重写实际上所有函数的call方法都是Function原型中的call方法, 即Function.prototype.call。
也可以说Function.prototype.call和Function.prototype.call.call、Function.prototype.call.call.call...都是等价的,即Function原型上的call方法。 - 这样
(Function.prototype.call.call).call(fn2);就相当于在fn2对象(js中函数也是对象)上调用原型中的call方法,且没有传递参数;实际上就是fn2.call();。
4)fn2.call();我们观察下call函数中没传值,默认就是window对象,fn2.call(window);又等价于window.fn2();。
模拟call()函数实现
call()的内部原理是怎样的?我们来模拟一下call函数:
function call(context = window, ...args) {
// this -> 指向调用call的那个函数(记住call前面必须是函数在调用)
// 实际上就是执行 this(...args);
// 是哪个对象要执行这个this函数?答案是context
// 所以先把这个函数赋值给context的一个属性
// 然后调用context这个新属性,接收传入的参数
context.$fn = this;
// 模拟call要处理的问题
let res = context.$fn(...args);
delete context.$fn; // 移除我们私自添加的函数 $fn
return res;
}
本文探讨了一道关于Js中call函数的面试题,解释了call的使用方式和其改变this指针的功能。通过示例,阐述了不同参数情况下的call行为,并分析了Function原型上的call方法。最后,尝试模拟实现call函数的内部原理。

2379

被折叠的 条评论
为什么被折叠?



