call、apply、bind 原理

343 阅读1分钟

call、apply、bind 介绍

方法区别

  1. 都是用来修改函数this指向的,第一个参数都是指定this绑定的对象,第二个参数是传参
  2. apply 第二参数是数组形式
  3. call 和 bind第二个参数都是参数列表形式
  4. apply和call都是立即执行函数,bind不会立即执行函数而是返回新的函数

call 函数

  1. 使用示例

本例中函数 fn 函数定义在全局,无对象调用本fn方法,所以fn函数默认 this 指向全局 window 对象,然后本例中 call 方法修改了函数 fn 的 this 指向,将 this 指向了对象 obj。 2)

let obj = {names: 'zhangsan'}
function fn(a,b,c) {
    //{names: "zhangsan"} 1 2 3
    console.log(this, a,b,c)
}
fn.call(obj,1,2,3)
  1. 手写源码
Function.prototype.myCall = function(context) {
        context = context ? Object(context) : window;
        // console.log('this', this)
        context.fn = this

        let args = []

        for(let i = 1; i<arguments.length; i++) {
            args.push(arguments[i])
        } 

        // let r = eval('context.fn(' + args.toString() + ')')
        let r = eval(`context.fn(${args.toString()})`)

        delete context.fn

        return r
    }

apply 函数

  1. 使用示例

本例中使用 apply 函数与上述call函数方式基本一致,仅存在传参区别。其中call方法接受多个形参,而apply方法接受一个数组作为形参,这里我们只需修改调用 apply 的传参方式即可。

    let obj = {names: 'zhangsan'}
    function fn(a,b,c) {
        console.log(this, a,b,c)
    }
    fn.apply(obj,[1,2,3])
  1. 手写源码
 Function.prototype.myApply = function(context) {
        context = context ? Object(context) : window

        context.fn = this

        let args = arguments[1]

        let r = eval(`context.fn(${args.toString()})`)

        delete context.fn

        return r
    }

bind 函数

bind 函数 与 call、apply 函数均只有一个维度的区别,区别如下:

  • bind 相对于 call 传参方式相同,不同之处在于 call 在修改了函数 this 指向的过程中同时执行了函数,而bind 修改函数 this 指向后不会立即执行函数,会返回修改后 this 指向的函数,用户后续开发人员调用执行。

  • bind 传参方式与 call 一致,不同于 apply 传参方式

  1. 使用示例
    let obj = {names: 'zhangsan'}
    function fn(a,b,c) {
        console.log(this, a,b,c)
    }
    let back = fn.bind(obj,1,2,3)
    back()
  1. 手写源码
  Function.prototype.myBind = function(context) {
        context = context ? Object(context) : window
        
        let that = this

        let args = []

        for(let i = 1; i < arguments.length; i++) {
            args.push(arguments[i])
        }

        let r = eval(`context.fn(${args.toString()})`)

        delete context.fn

        return function() {
            that()
        }
    }