call、bind和apply的简单原理以及bind的优先级

本文详细介绍了JavaScript中call、apply和bind函数的实现原理,包括如何改变函数执行时的this上下文,以及它们之间的区别。通过实例代码解释了call和apply分别如何处理参数,以及bind如何返回一个新的绑定this的函数,并强调了bind的优先级高于apply和call。
摘要由CSDN通过智能技术生成

一、简单实现call函数

call函数原理:给函数的this绑定一个对象并且执行该函数,参数是一个个传的(不是以数组的形式)
Function.prototype.myCall = function(context,...args){
    // context:myfun函数中的this要绑定的对象
    // 给context对象增加一个$myProps属性(自己随便命名),用于保存myfunc函数
    // this的隐试绑定原理:谁调用这个函数,那么this就指向谁
    // 在本例中,外部函数myfun调用了本函数(myCall),那么本函数中的this就是myfun函数
    // 所以这里的this就是myfun函数
    context.$myProps = this
    // 此时的context参数中的值
    /**
     * {
     *    name: 'fish',
     *    age: 18
     *    $myProps: myfun
     * }
     */

    // 参数: ...args
    // 得到的就是外部传入的:"是一只小鱼仔"

    // 通过context对象调用myfun函数(根据上述的this隐式绑定原理)
    // 因为是通过context对象调用myfun函数,
    // 所以myfun函数中的this指向的是context对象
    // 到这里就实现了myCall的this绑定对象的功能
    const res = context.$myProps(...args)
    
	// 执行完函数之后,需要把原先的context对象中我们自己定义的$myProps属性给删除
	// 因为对象的传参形式是引用传参(引用传参:传进来的是一个地址引用,我们通过地址操作对象的时候,外部的obj对象也会受到影响)
	// 在本函数中,context和外部的obj,它们的地址都指向同一个内存空间,相当于它们是同一个对象
	delete context.$myProps

    // 要有返回值res的原因:
    // 因为myfun函数(也就是别人定义的函数)可能会有返回值
    // 所以我们要在执行完别人的函数之后将别人函数的执行结果
    // 返回出去,这样别人才能接收到函数的返回值
    return res
}

const obj= {
    name:'fish',
    age:18
}

function myfun(params){
    console.log(this.name,params)
    // 返回值
    return params
}

// 执行函数,接收返回值
const result = myfun.myCall(obj,"是一只小鱼仔")

console.log(result)

// 控制台输出内容:
/**
 * 第一行(myfun函数中打印的内容,可见myfun中的this指向了obj):  fish 是一只小鱼仔
 * 第二行(外部打印的myfun函数的返回值内容,可见我们自己的myCall函数中返回值的作用): 是一只小鱼仔
 */

二、简单实现apply函数

apply函数原理:给函数的this绑定一个对象并且执行该函数,参数是以数组的形式传入
apply函数与call函数的区别:传参数的形式不同,apply参数是以数组的形式传入。call是逐个传入,以逗号隔开
Function.prototype.myApply = function(context,arr){
    context.$myProps = this
    const res = context.$myProps(...arr)
    delete context.$myProps 
    return res
}

const obj= {
    name:'laosha',
    age:18
}

function fn(param1,param2){
    console.log(this.name, param1, param2) // 控制台输出:laosha 小马 小鸡
    return this.age
}

const res = fn.myApply(obj,["小马","小鸡"])
console.log(res) // 控制台输出:18

三、简单实现bind函数

bind函数:只能通过函数调用,返回一个绑定this后的函数
Function.prototype.myBind = function(context){
    // 判断当前调用的myBind是不是函数,不是函数则抛出错误
    if(typeof this !== "function"){
        throw new Error("不是函数,无法调用myBind")
    }
    // 以闭包的形式保存外部调用myBind函数的fn函数
    const thatFun = this
    return function(...params){
      context.$myProps = thatFun
      const res = context.$myProps(...params)
      delete context.$myProps
      return res
    }
}

function fn(param1,param2){
    console.log(this.name, param1, param2)  // 控制台输出:laosha2 小鸟 小尼姑
}

const obj= {
    name:'laosha2',
}

// fn函数绑定obj对象
const newFn = fn.myBind(obj)
newFn("小鸟","小尼姑")

四、bind函数优先级最高

说明:一个函数(fn),一旦经过bind绑定之后,那么这个返回的新函数(newFn)通过apply或者call
都无法再更改this的指向
Function.prototype.myBind = function(context){
    // 判断当前调用的myBind是不是函数,不是函数则抛出错误
    if(typeof this !== "function"){
        throw new Error("不是函数,无法调用myBind")
    }
    // 以闭包的形式保存thatFun 
    // 注意:此处thatFun保存this是一个关键点
    // 返回的函数中没有用到任何一个this
    const thatFun = this
    return function(...params){
      context.$myProps = thatFun
      const res = context.$myProps(...params)
      delete context.$myProps
      return res
    }
}

const obj111= {
    name:'laosha11111',
}
const obj222= {
    name:'laosha22222',
}

function fn(param1,param2){
    console.log(this.name, param1, param2)  // 控制台输出:laosha11111 小鸟 小尼姑
}
// fn函数绑定obj111对象,得到新函数newFn
const newFn = fn.myBind(obj111)
// 新函数newFn通过apply将this绑定到obj222,然后调用
newFn.apply(obj222,["小鸟","小尼姑"])
// 最后控制台输出的是:laosha11111 小鸟 小尼姑
// 可见apply是无法更改已经被bind绑定过this的函数
// call也同apply一样

结论:

一个函数fn被bind绑定this后返回的新函数newFn, apply和call无法通过更改newFn的this指向象来影响fn的this指向。

原因解析:

apply和call更改的是newFn函数中this指向,但是newFn在执行的过程中没有用到this,所以就算改变了newFn的 this指向,也不影响fn已经被bind绑定的this对象
Function.prototype.myCall = function(ctx,...args){
    // ctx是obj222对象
    // 此处的this指向newFn,因为是newFn调用的myCall
    ctx.$myProps = this
    // 此时的ctx参数中的值
    /**
     * {
     *    name:'laosha22222',
     *    $myProps: function(...params){
     *                context.$myProps = thatFun
     *                const res = context.$myProps(...params)
     *                delete context.$myProps
     *                console.log("我是返回的newFn,我的this指向是",this)
     * // 控制台输出:我是返回的newFn,我的this指向是 { name: 'laosha22222', '$myProps': [Function (anonymous)] }
     *                return res
     *             }
     * }
     */
    // 执行newFn函数
    // 注意看:在newFn执行的过程中
    // context是:fn.bind(obj111)中的obj111对象
    // 而不是newFn.myCall(obj222)中的obj222对象
    const res = ctx.$myProps(...args)
    delete ctx.$myProps
    return res
}

Function.prototype.myBind = function(context){
    const thatFun = this
    // 返回的newFn
    return function(...params){
      context.$myProps = thatFun
      const res = context.$myProps(...params)
      delete context.$myProps
      console.log("我是返回的newFn,我的this指向是",this)
      return res
    }
}

const obj111= { name:'laosha11111'}
const obj222= { name:'laosha22222'}

function fn(){
    console.log(this.name)  // 控制台输出:laosha11111
}

const newFn = fn.myBind(obj111)
newFn.myCall(obj222)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值