JS中call和Apply原理分析研究

call和apply共同点

call和apply都是为了改变某个函数运行时内部this的指向,会立即调用该函数

call和apply不同点

call方法接收的是若干个参数的列表
apply方法接收的是一个包含多个参数的数组

call方法使用说明

let bar = {
   sex: "男"
};

function foo(age) {
   console.log(this,this.sex, age);
}

// foo(10)  //Window undefined 10
foo.call(bar, 10) //{sex: "男"} "男" 10

通过call方法调用foo函数,会立即执行foo函数,同时this会指向传递的第一个参数bar,第二个或后边的以参数的形式传递给foo函数

模拟call方法

let bar = {
    sex: "男"
};

function foo(age) {
    console.log(this.sex, age); //男 10
    console.log(this); //{sex: "男", fn: ƒ}
}

Function.prototype.myCall = function (context) {
    //第一种获取形参的方法
    // let arr = [];
    // for(let i=0; i<arguments.length;i++){
    //     arr.push(arguments[i])
    // }
    //从角标1开始删除,并返回被删除的项目
    // arr = arr.splice(1);

    //第二种获取形参的方法,获取参数列表(去除第一个参数)
    // let arr = [...arguments].splice(1);
    // console.log(...arr); //10


    //第一个参数content为调用call方法的函数内this的指向
    console.log(context);//{sex: "男"}
    console.log(this);//this是调用myCall的foo()函数
    //如果不传入参数,默认指向window
    context = context || window;
    //动态给content添加fn属性并将this赋值给它
    context.fn = this;
    //调用content的fn函数,并把除了第一个参数的参数传递过去
    // let result = context.fn(...arr);
    let result;
    //调用content的fn函数,并判断是否有第二个参数,如果有就传递过去
    if (arguments[1]) {
        let arr = [...arguments].splice(1); //10
        result = context.fn(...arr)
    } else {
        result = context.fn()
    }
    //删除fn属性,但this已经指向了传入的那个对象
    delete context.fn;
    //测试this对象中是否还有fn属性
    // context.test = this;
    // context.test();
    return result
};

foo.myCall(bar, 10);

当调用foo函数时,如果属性或方法没有找到,会向原型链上查找,foo构造函数的__proto__指向的是Function.prototype对象,所以我们在模拟call方法时,可以给Function的原型添加方法。

foo调用myCall方法 等同于给bar添加一个fn属性,而fn属性又对应一个foo方法,然后就是调用fn方法。如:

 function bar(){
    this.sex = "男";
    this.fn = function foo(age) {
        console.log(this.sex, age);
    }
}

具体步骤是:

  1. 首先通过arguments拿到所所有的实际参数,再通过splice截取除了第一个参数的列表

  2. 把this对象(this是foo函数)赋值到bar对象的fn属性上,并执行fn函数

  3. 把先前处理好的参数传递过去,并获取返回结果(如果无就返回undefined)

  4. 删除bar的fn属性,并返回结果

    注意
    在foo函数中打印的this为什么还是有fn函数呢,我们已经通过delete context.fn; 把fn函数删除了啊?我个人理解:这里的显示是因为delete context.fn在打印之后执行的,而我们看到的是删除之前的数据,如果我们点开,里边显示的才是foo真正具有的属性(如下图),或者我们在delete context.fn之后再给context赋值一个属性,再次打印就看不到fn属性了
    在这里插入图片描述

apply方法使用说明

let bar = {
     sex: "男"
 };

 function foo(age) {
     console.log(this.sex, age); //男 10
 }

 foo.apply(bar, [10])

模拟apply方法

 let bar = {
     sex: "男"
 };

 function foo(age) {
     console.log(this.sex, age); //男 10
 }

 Function.prototype.myApply = function(context){
     //第一个参数content为调用call方法的函数内this的指向
     console.log(context);//{sex: "男"}
     console.log(this);//this是调用myCall的foo()函数
     //如果不传入参数,默认指向window
     context = context || window;
     //动态的将this赋值给content的fn属性
     context.fn = this;
     let result;
     //调用content的fn函数,并判断是否有第二个参数,如果有就传递过去
     if(arguments[1]) {
         result = context.fn(...arguments[1])
     } else {
         result = context.fn()
     }
     //删除fn属性
     delete context.fn;
     return result
 };

 foo.myApply(bar, [10]); //男 10

apply和call方法实现类似,只不过参数是以数组的形式传递

模拟bind方法

    Function.prototype.myBind = function (context) {
        let self = this; 
        let arg = Array.prototype.slice.call(arguments, 1); 
        let fNOP = function () {};
        let fbound = function () { 
            let innerArg = Array.prototype.slice.call(arguments);
            let totalArg = arg.concat(innerArg); 
            return self.apply(context, totalArg);
        }
        fNOP.prototype = this.prototype;
        fbound.prototype = new fNOP();
        return fbound;
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值