js手动实现函数的call,apply,bind方法,简单易懂

好的各位小伙伴,我们现在要手动实现这三个方法,我们都知道这三个方法都是用来改变函数内部的this指向的。首先就是我们怎么去更改函数的this指向呢,用人说用call方法,那不是扯犊子吗?我们就是要还原这个方法,没叫你直接用。

很遗憾js并没有提供修改函数this指向的语法。但这并不代表我们就修改不了,我们可以利用this指向的特性来进行修改,即:“对象方法里面的this指向调用该方法的对象”  。看以下代码

function update(name,hobby,sex){
        this.name=name
        this.hobby=hobby
        this.sex=sex
        console.log(this)
}
let ikun={
    name:"鸡你太美",
    hobby:"唱,跳,rap",
    sex:"女"
}
ikun.update=update
update()
ikun.update()

进行一次简单赋值以后,两次打印this结果如下,可以看到明明是同一个函数体,this已经发生了变化,这也印证了我们上面的理论

 那么接下来我们就可以利用这个特性实现call,apply,bind方法了,老规矩在Function原型上添加方   法,首先是call函数

 // call方法第一个参数是改变原函数this的对象,原函数所需参数从第二个开始依次传递
 //在这里我用es6的剩余参数...list把所有参数都放进list数组里面
 Function.prototype.mycall=function(obj,...list){

 }

OK我们现在开始写函数体

Function.prototype.mycall=function(obj,...list){
     obj=obj||window   //先初始化传入的obj对象,防止传个undefined进来报错
     obj.fn=this       //现在进行赋值操作,给obj新加一个属性,这里的this就是原函数
     //我们开始调用函数,用obj去调用fn方法,此时fn内部的this就已经指向了obj
     var result=obj.fn(...list) //用展开运算符把接收到的所有参数都放进去
     delete obj.fn     //最后删除fn方法,并返回调用结果
     return result
}

最后我们利用这个mycall方法将update函数的this改为ikun对象,执行完毕以后我们打印ikun对象,可以看到ikun对象已经被更改。

update.mycall(ikun,"坤坤","篮球","男")
console.log(ikun)

 

 只要实现了call方法,那apply方法就简单了,我只需要更改一个变量就行

 // apply方法第一个参数是改变原函数this的对象,原函数所需参数都放在第二个数组参数里面
 //在这里直接用list去接收第二个参数就行了,函数体和call方法一模一样
 Function.prototype.myapply=function(obj,list){
    obj=obj||window   
    obj.fn=this       
    var result=obj.fn(...list) 
    delete obj.fn     
    return result
 }

我们来试试效果,可以看到ikun对象的确被改变了,apply已实现

update.myapply(ikun,["坤坤","两年半篮球","男"])
console.log(ikun)

 最后就是bind方法了,该方法不会直接调用而是返回一个函数,让用户自己调用,我们把call方法稍作修改就行

Function.prototype.mybind=function(obj,...list){
    //我们直接返回一个函数,把调用的部分放在函数里面让用户自己去调用
    return ()=>{
        obj=obj||window  
        obj.fn=this
        var result=obj.fn(...list)
        delete obj.fn
        return result
    }
}

最后我们用自己的mybind函数去调用一下,也是可以的

update.mybind(ikun,"坤坤","性感篮球","男女难辨")()
console.log(ikun)

 你是不是以为到这里就结束了呢,其实还是有点小瑕疵的,这三个方法里面我们都添加了一个fn方法,调用之后再删除掉,那如果该对象本身就有一个fn属性,那我们这么写不是完犊子了吗?就像这样

 

let ikun={
   name:"鸡你太美",
   hobby:"唱,跳,rap",
   sex:"女",
   fn:"你干嘛!!!"
}
update.mycall(ikun,"真ikun","打篮球","男")
console.log(ikun)

打印ikun发现fn消失了,你这不开玩笑嘛,这肯定不行,让你改个this,数据都给人弄丢了

 最后说一下解决办法,用es6的Symbol类型作为对象的唯一属性即可,代码如下

Function.prototype.mycall=function(obj,...list){
    //创建一个Symbol数据类型的fn
     let fn=Symbol("fn")
     obj=obj||window   
     obj[fn]=this       
     var result=obj[fn](...list) 
     delete obj[fn]    
     return result
}

调用之后ikun的fn属性也不会被误删,apply和bind方法也是同样的道理,就不赘述了

 最后也希望大家有什么好的想法和代码也多多分享一下,毕竟技术要多交流才能进步

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值