JS中call和apply的实现原理

        在JS中的Object对象的原型链上存在着两个函数 call和apply,其主要作用是将调用的函数的this指向目标对象即

    Object.call(target_ele);

        在我的另一篇文章中也详细阐述了call和apply的用法,这里就不多做赘述了。

        这里call有两个变量,一个是改变后的this目标指向的元素,另一个是向Object函数传递的参数组。

        实现思路:

        这里由于Object对象是一个函数对象,所以我们可以在Object对象的原型链Function追加一个实现call的方法:

 Function.prototype.mycall = function() {

    }

        自此我们的准备工作已经完成。

        其实在call函数中,就是将目标函数的指向当前传递的元素。在之前我关于this在不同函数的指向中文章中写过,此时我们mycall函数中的this其实是指向调用的函数,例如:

 function User() {

    }

        我们创建一个User函数,这时User调用mycall函数:

 User.mycall();

        这时mycall函数中的this其实指向的就是User函数本身,我们既然要改变this的指向,那必然要对this进行重新指向,但是重新对User的this赋值是不允许的,所以我们再次借用原型链方法,再目标对象原型链中添加一个fun方法:

 Function.prototype.mycall = function(target_ele) {
        target_ele.__proto__.fun = this;
    }

        此时User函数就作为一个属性方法挂载到我们目标对象的原型链中,同时我们改写User中的函数体内容:

function User() {
        this.name = 123;
    }

        使其我们在测试中能看到效果,我们调用添加的fun属性,同时创建一个空对象a,测试方法,此时完整的方法代码如下:

function User() {
        this.name = 123;
    }
Function.prototype.mycall = function(target_ele) {
        target_ele.__proto__.fun = this;
        target_ele.fun();
    }
let a = {};
User.mycall(a);
console.log(a)

        User调用了我们在Function的原型链中创建的函数mycall,mycall需要接收一个目标对相关,traget_ele,接收完成后,在traget_ele对象原型链中将User函数挂载上去,并以fun属性调用,调用挂载的fun函数。此时fun是traget_ele对象的一个属性,fun又是指向User构造函数,所以User构造函数中的this其实就是指向的traget_ele对象,并为traget_ele对象初始化一个为name的属性,输出的结果为:

        此时我们顺利的实现了call函数的部分功能,接下来我们来实现函数的传递,这里我们使用ES6中新增的展开语法,将刚才的mycall函数改写为: 

 Function.prototype.mycall = function(target_ele, ...args) {
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
    }

        此时我们在User构造函数中增加一个需要传递的变量name,并在有值是赋值,没传值时赋值为123:

function User(name) {
        this.name = name || 123;
    }

        接着我们来测试:

User.mycall(a, '李四');

        输出结果为:

        到这里为止我们就已经完成call功能的实现了,因为我们call其实是有返回值的,且fun属性不应该存在,所以我们对mycall函数还要进行一下修改:         

 Function.prototype.mycall = function(target_ele, ...args) {
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
        delete target_ele.fun;
        return target_ele;
    }

        好了,以上就完成了call 的实现,apply函数与call函数大同小异,其就是将传递的函数变为数组形式,我们只需要改写如下:

Function.prototype.mycall = function(target_ele, args) {
        if (!(args instanceof Array))
            throw Error('请传递一个数组!');
        target_ele.__proto__.fun = this;
        target_ele.fun(...args);
        delete target_ele.fun;
        return target_ele;
    }

        添加对数组的判断,配合展开语法将其参数传入目标函数,我们再来改写一下User,添加一个age属性:

 function User(name, age) {
        this.name = name || 123;
        this.age = age || 1;
    }

        同时调用mycall:

User.mycall(a, ['李四', 18]);

        输出结果为:

        总结:以上就是对call和apply方法的实现,当然老版本中的许多方法也可以实现类似的参数传递,这里仅仅选用了较为简单的展开语法,有兴趣的可以自己做深入研究。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值