call函数的定义是这样的:调用一个对象的一个方法,以另一个对象替换当前对象。
怎么理解这句话呢,本人是这样理解的,先看下面的这一段代码:
它的输出是“这是newObj的姓名”,也就是说,通过call函数,newObj这个对象调用了fun函数里面的方法,并且此时fun函数中的this指向了newObj对象。就类似于Java中的继承,子类调用了父类的方法这种意思。并且通过call修改了原本函数的this指向。也就是说可以通过call函数将“newObj的属性加到fun上,并且调用fun的方法。”
也就是说将我们的fun函数放在了newObj对象中执行。
那么我们现在开始按照这种想法,写一下call函数。首先要明白call函数存在在哪里。
每个JS函数都是一个Function对象,而Function对象是构造函数,构造函数是有原型对象的,也就是Function.prototype,这个call函数就在原型对象的属性里面。
所以我们要在Function.prototype中添加。
也就是上图这样,我们将obj(也就是call函数中第一个参数)作为参数传入。我们知道通过call函数,这个this会指向obj,而此时,我们看一下this的指向
也就是说此时的this指向的是fun函数。那么,通过上面对call的理解,我们知道,相当于将fun函数放到了newObj中执行,所以我们现在将this放在obj中的一个属性(方法)中,让他执行。也就是如下:
需要注意的是:我们运行完需要删除obj.a 因为我们不能修改人家定义下的对象。
这时我们完成了call函数的第一步。那么第二步就是:假如call函数中传入了参数怎么班?如下图:
首先,我们需要知道的一点是:在newCall中,传入的参数和fun函数的参数个数是不一样的,上图中newCall是传入了4个,而fun函数中只有a,b,c三个。
第二点我们需要知道的是,在JS中,函数接受参数是通过arguments伪数组接收的。所以此时我们向newCall中添加接受参数的语句:
i从1开始,也就是我们从除了this以外的第二个参数开始接收,现在来看打印的结果:
此时,我们明白了newArguments确实收到了这几个参数,但是为什么会打印出undefined呢?因为我们在写newCall的时候,内部执行的其实是不带参数的函数,也就是说我们并没有使用参数。所以我们现在将newArguments赋值给obj的a。
此时来看输出结果:
发现,数组的三个元素都到了第一个参数的为,剩下的还是undefined。
这是因为直接将newArguments这个数组给了a,那么也就是给了一个整体,那么a将会把
newArguments这个数组当成一个数组放到第一个位置。所以此时我们需要对数组进行处理。将他们拆分开,到各自的位置上去,也就是下面图中的这个意思:(字符串和数组拼接时会调用toString方法)
而此时的newArguments数组是这样的:
但我们并不知道newCall函数中会传入几个参数,不知道其长度,所以我们可以采用字符串拼接,也就是加上引号,如果实现了字符串拼接,就可以利用eval函数运行。
我们这样拼接,那我们想要的效果其实是这样的:
但实际的效果确是这样的:
为什么会出现这种情况呢,是因为newArguments中的数组会直接显示带引号的参数,所以再进行字符串拼接就会成了上面这样,这些参数就没有了引号。所以我们现在用字符串的形式先隐藏掉这些参数,我们只需要修改for循环里面的arguments:
此时数组就会显示为:
此时再进行拼接就会变成这样:
那么此时我们就可以用这个了:
这些步骤完成后,我们就可以用eval来执行了:
但是,我们创建一个函数,要不就是让他返回一个值,要不然就是执行一些动作,有时候甚至可能返回一个对象,但此时我们上面的代码并不能够返回一个对象。
所以需要将eval函数运行的结果赋值给一个变量,并返回。
此时看我们目前写的执行结果:
运行结果:
所以完整代码如下: