【手写系列】bind()、softBind()、call()、apply()的实现

884 阅读3分钟

~

  • bind会返回一个硬绑定的新函数,新函数会使用指定的第一个thisCtx去调用原始函数,并将其它参数传给原始函数。 硬绑定会降低函数的灵活性,在绑定之后不能通过显式或硬绑定的方式改变this,只能通过new改变
  • softBind 会对指定的函数进行封装,首先检查调用时的 this,如果 this 绑定到全局对象或者 undefined,那就用指定的thisCtx 去调用函数,否则不会修改 this
  • apply和call功能相同,都是以指定的thisCtx和参数去执行方法,并返回原方法的返回值,只是apply中参数以数组传递

bind()函数

bind 函数用于绑定this关键字,改变this指向,并且最终返回一个函数

用法: function.bind(thisArg,arg1,arg2,arg3,...)

例子:

function add(x,y){
    // console.log("yhis",this)
    return x+y;
}

function newArrBind(x,y){

    let bindAddFn=add.bind(this,x,y);
    // console.log("this",this)
    return bindAddFn()
}

console.log(newArrBind(10,20))//30

手动实现


//bind()实现
console.log("bind()函数的实现")

Function.prototype.newBind=function(obj,...args){
    return (...res)=>this.call(obj,...args,...res);
}

function func(arg){
    console.log("num:",this.num,"arg:",arg);
}


func.bind({num:999})(123);
//num: 999 arg: 123
func.newBind({num:999})(123);
//num: 999 arg: 123

关于softbind()软绑定

在了解softbind之前先看之前的例子带有//+的为增加的代码

案例



Function.prototype.newBind=function(obj,...args){
    return (...res)=>this.call(obj,...args,...res);
}

function func(arg){
    //+
    console.log("this=>",this)
    console.log("num:",this.num,"arg:",arg);
}

//+
func("啦啦啦~")
//输出 this=> Window {window: Window, self: Window, document: document, name: '', location: Location, …}
//      num: undefined arg: 啦啦啦~


func.bind({num:999})(123);
//this=> {num: 999}
//num: 999 arg: 123

func.newBind({num:999})(123);
// this=> {num: 999}
//num: 999 arg: 123

func.bind({num:999}).call({num:888},777)
// this=> {num: 999}
// index.js:9 num: 999 arg: 777

func.bind({num:999}).bind({num:666})(555);
// this=> {num: 999}
// index.js:9 num: 999 arg: 555

可以看到这个时候this指向已经被写死了,事实上,bind函数多次调用会已第一次绑定的this为准,softbind已最后一次绑定传入的this为准;

手动实现


Function.prototype.softBind=function(obj,...rest){
    const fn=this;
    const bound=function(...args){
        const o=!this || this===(window || global) ? obj : this;
        return fn.apply(o,[...rest,...args]);
    }
    bound.prototype=Object.create(fn.prototype);
    return bound;

}

func.softBind({num:999})(888);
// this=> {num: 999}
// index.js:9 num: 999 arg: 888

func.softBind({num:888})(777);
// this=> {num: 888}
// index.js:9 num: 888 arg: 777

call函数

call函数调用一个杉树时,会将该函数的执行对象的上下文改变为另一个对象

函数用法: function.call(thisArg,arg1,arg2,...)

例子

function add(x,y){
    return x+y;
}

function myAddCall(x,y){
    return add.call(this,x,y);
}

console.log(myAddCall(10,20));//输出30

手动实现


function func(arg){
    //+
    console.log("this=>",this)
    console.log("num:",this.num,"arg:",arg);
}

//+
func("啦啦啦~")
Function.prototype.newCall=function(ctx=globalThis){
    const args=Array.from(arguments).slice(1);
    const key=Symbol("key");
    ctx[key]=this;
    const res=ctx[key](...args);
    delete ctx[key];
    return res;
}
func.call({num:333},444)

func.newCall({num:111},222)

// index.js:8 this=> Window {window: Window,
//self: Window, document: document, name: '', location: Location, …}
// index.js:9 num: undefined arg: 啦啦啦~
// index.js:8 this=> {num: 333}
// index.js:9 num: 333 arg: 444
// index.js:8 this=> {num: 111, Symbol(key): ƒ}
// index.js:9 num: 111 arg: 222

apply()函数

apply函数作用域与call函数是一样的,只是在传递参数的形式上存在差别。
语法格式: function.apply(thisArg,[argsArray])

案例

function add(x,y){
    return x+y;
}

function myAddApply(x,y){
    return add.apply(this,[x,y]);
}

console.log(myAddApply(10,20))//30

手动实现


Function.prototype.newApply = function (ctx = globalThis) {
    const args = arguments[1];
    const key = Symbol("key");
    ctx[key] = this;
    const res=ctx[key](...args);
    delete ctx[key]
    return res
  };

func.apply({num:777},[888]);
// this=> {num: 777}
// index.js:9 num: 777 arg: 888
func.newApply({num:777},[888]);
// {num: 777, Symbol(key): ƒ}
// index.js:9 num: 777 arg: 888