重学javaScript (十四)|手写apply,call和bind

142 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情

前言

如题,这一章主要来手写一下apply,call和bind

call

先来看下原生js的call的用法

function sum (a,b) {
    console.log(this)
    return a+b
}

const num = sum.call('77',2,3)
console.log(num)

call改变了this指向,并接入了参数做相加操作,来看返回结果

image.png

我们继续来分析一下,call做了哪些事情

  • 改变this的指向,给传递进去的第一个参数
  • 后续的参数接收函数的参数,并执行操作

接下来我们针对这两点来使用js模拟下call函数

// 在原型上定义一个mycall
Function.prototype.mycall = function (thisArg,...args) {
    // 接收两个参数 
    // this 指向   剩余的函数参数

    // 1.获得要执行的函数
    // 这个this就是调用mycall的函数,因为隐式绑定,谁调用函数,函数里的this就是谁
    var fn = this

    // 2.处理绑定thisArg
    // 根据我们学的,call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向window
    thisArg = (thisArg!==null && thisArg!== undefined) ? Object(thisArg):window
    // 3.执行函数就是将thisArg 绑定到fn上
    // 这里做一个隐式绑定
    thisArg.fn = fn

    // 处理参数,执行函数
    let result = thisArg.fn(...args)
    delete thisArg.fn
    return result

}

// 
let nums = sum.mycall('777',2,3)
console.log(nums)

解释一下

  1. 在Function的原型上绑定一个mycall函数
  2. 这个函数接收两部分参数,对应call函数
    • 需要绑定this,形参写作thisArg
    • 调用该函数的函数的参数,用ES6的剩余运算符来传入,写作形参...args
  3. var fn = this 接收到调用mycall函数的函数fn,因为根据隐式绑定,谁调用mycall函数它的this就是谁
  4. thisArg = (thisArg!==null && thisArg!== undefined) ? Object(thisArg):window 这一行,因为根据我们学的call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向window
  5. thisArg.fn = fn 这里我们做个隐式绑定,将fn的this指向thisArg (因为根据隐式绑定,谁调用fn函数fn的this就是谁)
  6. let result = thisArg.fn(...args) 执行thisArg的fn函数thisArg的fn函数并传入参数,thisArg的fn函数就是传入的fn函数
  7. 删除多余的参数fn
  8. 返回第六步的执行结果

来看下执行的结果是不是跟call函数的相似

image.png

可以看下结果多了对象中多了fn,那是因为打印的时候我们还没删除,这个无伤大雅,达到call函数的功能即可

apply

再来看下原生js的apply的用法,它和call的唯一区别就是,它是接收一个数组来接收函数的参数

function sum (a,b) {
    console.log(this)
    return a+b
}

const num = sum.apply('77', [2,3])
console.log(num)

apply改变了this指向,并接入了参数做相加操作,来看返回结果

image.png 我们继续来分析一下,apply做了哪些事情

  • 改变this的指向,给传递进去的第一个参数
  • 后续的参数接收函数的参数,并执行操作

接下来我们针对这两点来使用js模拟下apply函数

function sum (a,b) {
    console.log(this)
    return a+b
}

// const num = sum.apply('77', [2,3])
// console.log(num)



// 在原型上定义一个mycall
Function.prototype.myapply = function (thisArg, array) {
    // 接收两个参数 
    // this 指向   剩余的函数参数

    // 1.获得要执行的函数
    // 这个this就是调用mycall的函数,因为隐式绑定,谁调用函数,函数里的this就是谁
    var fn = this

    // 2.处理绑定thisArg
    // 根据我们学的,call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向window
    thisArg = (thisArg!==null && thisArg!== undefined) ? Object(thisArg):window
    // 3.执行函数就是将thisArg 绑定到fn上
    // 这里做一个隐式绑定
    thisArg.fn = fn

    // 处理参数,执行函数
    let result = thisArg.fn(...array)
    delete thisArg.fn
    return result

}

// 
let nums = sum.myapply('777',[2,3])
console.log(nums)

// 
let nums = sum.mycall('777',2,3)
console.log(nums)

解释一下

  1. 在Function的原型上绑定一个myapply函数
  2. 这个函数接收两部分参数,对应apply函数
    • 需要绑定this,形参写作thisArg
    • 调用该函数的函数的参数,传入一个array
  3. var fn = this 接收到调用myapply函数的函数fn,因为根据隐式绑定,谁调用myapply函数它的this就是谁
  4. thisArg = (thisArg!==null && thisArg!== undefined) ? Object(thisArg):window 这一行,因为根据我们学的call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向window
  5. thisArg.fn = fn 这里我们做个隐式绑定,将fn的this指向thisArg (因为根据隐式绑定,谁调用fn函数fn的this就是谁)
  6. let result = thisArg.fn(...args) 执行thisArg的fn函数thisArg的fn函数并传入参数,thisArg的fn函数就是传入的fn函数
  7. 删除多余的参数fn
  8. 返回第六步的执行结果

来看下执行的结果是不是跟appky函数的相似

image.png 可以看下结果多了对象中多了fn,那是因为打印的时候我们还没删除,这个无伤大雅,达到apply函数的功能即可

bind

再来看下原生js的bind的用法,它和call的唯一区别就是,它是返回一个数组

function sum (a,b) {
    console.log(this)
    return a+b
}

const num = sum.bind('77', 2)
let res = num(3)
console.log(res)

bind改变了this指向,并接入了参数做相加操作,来看返回结果

image.png 我们继续来分析一下,bind做了哪些事情

  • 改变this的指向,给传递进去的第一个参数
  • 后续的参数接收函数的参数,并执行操作
  • 我们会发现,函数的形参既可以传入bind函数,也可以传入bind返回的函数里,还可以分开传

接下来我们针对这两点来使用js模拟下bind函数

function sum (a,b) {
    console.log(this)
    return a+b
}

// const num = sum.apply('77', [2,3])
// console.log(num)



function sum(a, b) {
    console.log(this)
    return a + b
}

// const num = sum.bind('77', 2)
// let res = num(3)
// console.log(res)



// 在原型上定义一个mybind
Function.prototype.mybind = function (thisArg, ...array) {
    // 接收两个参数 
    // this 指向   剩余的函数参数

    // 1.获得要执行的函数
    // 这个this就是调用mycall的函数,因为隐式绑定,谁调用函数,函数里的this就是谁
    var fn = this


    thisArg = (thisArg !== null && thisArg !== undefined) ? Object(thisArg) : window

    function proxyfunc(...args) {
        // 2.处理绑定thisArg
        // 根据我们学的,call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向window
        // 3.执行函数就是将thisArg 绑定到fn上
        // 这里做一个隐式绑定
        thisArg.fn = fn
        let finallyarr = [...args, ...array]
        // 处理参数,执行函数
        let result = thisArg.fn(...finallyarr)
        delete thisArg.fn
        return result
    }

    return proxyfunc

}

// 
let nums = sum.mybind('777', 2)
console.log(nums(3))


// 
let nums = sum.myapply('777',[2,3])
console.log(nums)

// 
let nums = sum.mycall('777',2,3)
console.log(nums)

解释一下

  1. 在Function的原型上绑定一个mybind函数
  2. 这个函数接收两部分参数,对应bind函数
    • 需要绑定this,形参写作thisArg
    • 调用该函数的函数的参数,传入一个array
  3. var fn = this 接收到调用myapply函数的函数fn,因为根据隐式绑定,谁调用myapply函数它的this就是谁
  4. thisArg = (thisArg!==null && thisArg!== undefined) ? Object(thisArg):window 这一行,因为根据我们学的call绑定的有两种情况,正常规则内的 或者 传入null或者undefiend的时候指向windowm,再定义一个函数proxyfn
  5. thisArg.fn = fn 这里我们做个隐式绑定,将fn的this指向thisArg (因为根据隐式绑定,谁调用fn函数fn的this就是谁)
  6. let finallyarr = [...args, ...array] 展开运算符处理参数
  7. let result = thisArg.fn(...args) 执行thisArg的fn函数thisArg的fn函数并传入参数,thisArg的fn函数就是传入的fn函数
  8. 删除多余的参数fn
  9. 返回函数proxyfn

来看下执行的结果是不是跟appky函数的相似

image.png

可以看下结果多了对象中多了fn,那是因为打印的时候我们还没删除,这个无伤大雅,达到bind函数的功能即可