js:手写call和apply函数

本文详细介绍了JavaScript中的call和apply方法,强调它们用于设置函数内部this值的能力,并通过示例展示了如何改变函数执行上下文。此外,还手把手教读者如何实现call和apply函数,通过实例解释了在实现过程中遇到的问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.关于call和apply的补充介绍

关于JavaScript这两个函数,

call和apply都是定义在函数上的方法,都会以指定的this值来调用函数,即会设置调用函数时函数

体内this对象的值。apply()方法接收两个参数:函数内this的值和一个参数数组。第二个参数可以是Array的实例,但是也可以是argument对象。

我们来看一个例子:

 function sum(num1,num2){
        return num1+num2;
    }
    function callsum(num1,num2){
        return sum.call(this,num1,num2);
    }
    console.log(callsum(10,10));//20

这里的callsum()函数必须逐个地把参数传到call()方法中去。

apply和call真正强大的地方不在于给函数传参,而在于控制函数调用上下文即函数体内this值的能力。考虑下面的例子:

  var name="林俊杰";
    let o={
        name:'周杰伦'
    };
    function sayname(){
        console.log(this.name)
    };
    sayname();//林俊杰
    sayname.call(window);//林俊杰
    sayname.call(this)//林俊杰
    sayname.call(o);//周杰伦

你看,sayname()是一个全局函数。如果在全局作用域中调用它,那么就会显示“林俊杰”。因为这里的this.name指向的是window。如果在全局作用域中调用this和window,就会同样的打印林俊杰,然而在使用call的时候,把函数执行的上下文即this切换为对象,这样结果就显示周杰伦了

2.手把手教大家写call函数

我们下面来看一个例子哈:

var name="haha";
var person={
    getName:function(){
        console.log(this)
        return this.name;
    }
    
};
var person1={
    name:"kobe"
}
console.log(person.getName())//undefined
console.log(person.getName.call(this))//这里是haha

大家看,首先第一个是undefined的原因是什么呢,因为此时this值不会指向window,而是person,除非我们显式调用this,也就是第二个打印的haha

下面我们

console.log(person.getName.call(person1))//kobe

这样的话打印的就是kobe。

ok 下面开始自己实现自己的call函数

首先:call是定义在function上的,其他函数要想调用,那么就必须在Function.prototype上面使用:,下面我们尝试书写一下!大家一定要跟着我的思路写!

Function.prototype.mycall=function(data) {  //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
    if(typeof this!="function"){
        throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法 
    }
    data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
    var args=[...arguments].slice(1)//因为call不仅能接受this,还可以接收其他的参数,这里调用ES6中的语法 
                                       //...arguments 这个能够起到解构的作用,比如var sad=[1,2,3]
                                                                       // console.log(...sad)输出的是1 2 3
    var result=this(...args);//这里的this就是getName
    return result;
}
console.log(person.getName.mycall(person1,1,2,3))

结果出来是多少呢,你会发现

还是haha!   出问题了啊啊啊啊,那么此时你肯定会很疑惑,到底是哪里出问题了啊,其实这个

当你打印出来的时候你会发现,调用最后一行代码的时候,这里打印出来了一个

window对象,只要你那个getName()里面有console.log(this)这行代码,你就会发现这里的this还是window,根本不是person1,所有我们最后肯定得不到kobe这个值

那么怎么把这里的person1传过去呢?

记住 person.getName.call(this)这里this是window是因为person.getName()本质也是window的一个属性,所有这里我们!

Function.prototype.mycall=function(data) {  //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
    if(typeof this!="function"){
        throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法 
    }
    data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
    var args=[...arguments].slice(1)//因为call不仅能接受this,还可以接收其他的参数,这里调用ES6中的语法 
                                       //...arguments 这个能够起到解构的作用,比如var sad=[1,2,3]
                                                                       // console.log(...sad)输出的是1 2 3
    //错误var result=this(...args);//这里的this就是getName,并且将数组args解构
    data.shuxing=this;//这行代码最关键!!!!!我们将person.getName()变成data的一个属性,那么调用data.shuxing就会指向data,而不是window了!!!
    return data.shuxing(...args);
}
console.log(person.getName.mycall(person1,1,2,3))

最关键的那行代码你看到了吗,这里随便让this成为data的一个属性,比如data.shuxing,比如data.p,随便你取,这样就打印出来了kobe!!!

手写apply函数

Function.prototype.myapply=function(data) {  //这里我们把mycall函数里面的传入的叫啥,data也行,其他也行。
    if(typeof this!="function"){
        throw new Error('error');//我们传入的肯定要必须是一个方法(函数)上文中的call里面的this就是getName这个方法 
    }
    data=data||window //如果没有传入值,比如上文中的call(),那么this就指向window了
    data.shuxing=this;
    var args=arguments[1];//只有这里有一点差别,因为apply是数组传入
    let result;
    if(args){
        result=data.shuxing(...arguments[1]);//将数组解构
    }
    else {
        result=data.shuxing();
    }
    
    return result;
}
console.log(person.getName.myapply(person1,[1,2,3]))

这里的apply函数就实现了!!!是不是很清晰呢。希望你可以通过此文对apply和call理解更为清晰!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值