call,apply,bind理解以及手动实现

130 阅读2分钟

1.相同点与不同点

相同点:都是改变函数this指向 不同点:

1.apply传参方式与其余不同,apply是以数组形式传参,fn.apply(this,[...arguments])

2.返回值不同,apply和call会直接执行这个函数,并且返回这个函数执行后的返回值,bind则会返回原函数的拷贝,并且指定好了this,还有初始参数

2.手动实现call,apply,bind

1.call

Function.prototype.myCall = function (youThis, ...arg){
    youThis = youThis || window || global //如果this是undefind或者null,则将this指向全局对象
    const prop = Symbol() //设置唯一值
    youThis[prop] = this //这里的this就是调用myCAll的函数,谁调用,函数就指向谁
    let result = youThis[prop](...arg)//用result将函数运行结果存储并返回
    delete youThis[prop]//删除函数参数
    return result
}

const obj = {
    a: 1,
}
function log() {
    console.log(this);
 }


log()// global or window
log.myCall(obj)// 1

为什么用Symbol做唯一值

Function.prototype.myCall = function (youThis, ...arg){ 
    youThis = youThis || window || global
   
    youThis.fn = this
    let result = youThis.fn(...arg)
    return result
}

const obj = {
    a: 1,
}
function dd1() {
    console.log(this);
}
 
function dd2() { 
    console.log(this);
}


function dd3() { 
    console.log(this);
}

dd1.myCall(obj)
dd2.myCall(obj)
dd3.myCall(obj)

如果想这样的代码,youThis.fn 一直会被dd3这个函数覆盖,无法拿到dd2,dd1

2.apply

apply方法和call只是传参方式不同,知道call方法怎么写,apply就差不多,就扩展运算符传把数组给函数就行了

Function.prototype.myApply = function (youThis, argArray){ 
    youThis = youThis || window || global
    const prop = Symbol()
    youThis[prop] = this
    let result = youThis[prop](...argArray)
    delete youThis[prop]
    return result
}

const obj = {
    ff: 1,
    gg:[1,2,3,4]
}
function dd(a,b,c,d) {
    console.log(this.gg[a]);
    console.log(this.gg[b]);
    console.log(this.gg[c]);
    console.log(this.gg[d]);
 }


dd.myApply(obj,[0,1,2,3])

3.bind

利用闭包保存this,返回一个函数,其实方法都差不多

Function.prototype.myBind = function (yourThis, ...arg) { 
    let self = this //利用闭包保存this,因为bind赋给变量是函数体,不使用闭包的话无法在全局中找到保存的this
    return function (...other) { //返回函数,这里的this保证函数可以继续传参
        return self.apply( yourThis,[...arg,...other])//返回指定函数this和参数的返回值
    }
}

const obj = {
    ff: 1,
    gg:[1,2,3,4]
}
function dd(a,b,c,d) {
    console.log(this.gg[a]);
    console.log(this.gg[b]);
    console.log(this.gg[c]);
    console.log(this.gg[d]);
 }


const newFn = dd.myBind(obj, 0,1,2,3)
newFn()

如果定义的函数怎么使用呢

Function.prototype.myBind = function (yourThis, ...arg) { 
    let self = this this
    return function (...other) { 
        console.log(this)//这里this是什么
        return self.apply( yourThis,[...arg,...other])
    }
}
const obj =  dd.myBind(obj, 0,1,2,3)
const aa = new newFn()

这里this会打印出这个obj ,因为new操作符会把函数体里面的this指向实例,所以这个myBind还有点问题 不符合MDN描述

image.png 以下为完善的版本

Function.prototype.myBind = function (yourThis, ...arg) { 
    let self = this this
    const bound = function (...other) { 
        const args = [...arg,...other]
        const isNew = this instanceof bound
        if(isNew){
            return new self(...args)
        }else{
        
            return self.apply( yourThis,args)
        }
        
    }
    return  bound
}