call的语法如下
语法:函数.call([context],[params1],....)
函数基于原型链找到Function.prototype.call这个方法,并且把它执行,在call方法执行的时候完成了一些功能
- 让当前函数执行
- 把函数中的THIS指向改为第一个传递给CALL的实参
- 把传递给CALL其余的实参,当做参数信息传递给当前函数
- 如果执行CALL一个实参都没有传递,非严格模式下是让函数中的THIS指向WINDOW,严格模式下指向的是UNDEFINED
一个简单的铺垫
window.name = 'window';
let obj = {
name: 'hello'
};
function fn() {
console.log(this.name);
}
fn(); //window
// obj.fn(); //报错
obj.fn = fn;
obj.fn(); //'hello'
说明:当执行obj.fn = fn后,相当于执行全局下的fn,此时fn方法中的this变为了obj,call方法正是利用了这一点来实现的。
内置call的实现原理
window.name = 'window';
let obj = {
name: 'hello'
};
function fn(n, m) {
console.log(this.name); //hello
console.log(n + m); //30
}
Function.prototype.call = function call(context, ...arg) {
// this:fn 【重点】
context = context || window;
context.this = this;
context.this(...arg);
delete context.this; //把临时添加在obj上的fn属性删除
}
fn.call(obj, 10, 20);
// console.log(obj);//{name: "hello"}
一道经典的面试题
function fn1() {
console.log(1);
}
function fn2() {
console.log(2);
}
fn1.call(fn2); //1
fn1.call.call(fn2);//2
Function.prototype.call(fn1); //没输出
Function.prototype.call.call(fn1); //1
解释说明:
- fn1.call(fn2);
* 先找到Function原型上的call方法让其执行
* call执行的时候会让fn1执行,并且把fn1中的this改为了fn2,结果为1.
- fn1.call.call(fn2);
第一次执行call方法:fn1.call.call(fn2)
* context:fn2
* this: fn1.call
*
* fn2.(fn1.call) = fn1.call;
* 让fn2.(fn1.call)执行,相当于让fn1.call执行,此时fn1.call中的this变为fn2
* 即第一次执行call后的结果:fn1.call() -->this:fn2
* 第二次执行call方法:fn1.call()
* context:undefined || window; -->所以context = window
* this: fn2
*
* window.fn2 = fn2;
* window.fn2() -->等价于fn2(),this:window -->结果:输出2
- Function.prototype.call(fn1);
* 本质是让Function.prototype执行,所以并没有任何输出结果
- Function.prototype.call.call(fn1);
本质:让Function.prototype.call()执行,方法中的this:fn1
* context:window
* this: fn1
*
* window.fn1 = fn1;
* 让window.fn1执行,相当于让fn1执行,此时fn1中的this变为window -->结果:输出1
小技巧:一个call了是让左边的函数执行,多个call是让传参的函数执行(this是window/undefined)
注:文章灵感来源于“珠峰培训”教学课程(http://www.zhufengpeixun.cn/)