js里对call的定义:
调用一个对象的一个方法,以另一个对象替换当前对象。call方法接受以下参数(obj,argument1,argument2,...)。
我们用call写一个小demo:
如上图我们就可以很直观的理解js对call的定义。调用window对象下的fn方法,用obj对象替换了window对象。可能这句话理解还是有些歧义,我们之后再解释。
为什么说obj对象替换了window对象?见图:
我们从代码里看到直接调用fn
输出window
下的name
。因为fn
在window
对象下,kack
也在window
下。 当我们用call
的时候,把window
对象替换成了obj
,所以输出了obj的name:'tom'
。
对于call的实现分析:
- call改变了this指向;
- 调用的方法执行了;
- call接受多个参数;
问题一:我们如何改变一个方法的this的指向呢? 问题二:如何去改变this指向,有去调用这个方法? 问题三:如何接受多个参数?再去执行?
可以看到我们给一个对象添加一个fn1属性,并把fn方法赋给这个属性,就相当于改变了fn里的this指向!然后再调用obj.fn1这个属性,不就执行了fn方法吗?是不是很简单~ 那多出来的fn1属性怎么办?也很简单,把这个对象的属性删除不就可以么。。。那多个参数怎么办?我们获取一个方法的参数可以通过arguments来获取,arguments是一个伪数组对象。然后遍历这个对象把属性再放到一个数组里,这样就可以取出来了。 最后一个问题:得到参数数组后怎么放到方法里去执行呢?我们要用到eval()
函数,eval()
函数可计算某个字符串,并执行其中的的 JavaScript 代码。
我们先按上面的分析实现call:
Function.prototype._call = function(){
var args = [];
var obj = arguments[0] || window; // call 如果第一个参数传null,则这个对象是window
for(var i=1, len = arguments.length; i < len; i++){
// 因为执行一个函数传参一般都是传string类型,所以此处需要用字符串拼接,之后用eval去解析这个字符串。
args.push('arguments['+ i +']'');
}
obj.fn = this; // this就是调用_call的函数
// args = ['arguments[1]', 'arguments[2]', 'arguments[3]', ...];
// 此处执行eval时,会先调用args.toString(),然后执行eval时会从对应的arguments对象中取参数。
eval('obj.fn(' + args +')');
delete obj.fn;
};
复制代码
运行以上代码:
大功告成,可以看出我们实现了call方法!