面试题:题目: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
考点:call方法的了解答案解析:- 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;
}参考:
https://blog.csdn.net/ahiai/article/details/105145776