JavaScript手写系列(五)(手写call、apply 、bind)

57 阅读1分钟

call & apply & bind

// call
Function.prototype.mycall = function (context, ...args) {
    // 1. 让fn中的this关键字变为context的值
    eval(this.toString().replace('this', context))
    // 2. 让fn方法执行
    this(...args)
}

方法二 原理就是将函数作为传入的上下文参数(context)的属性执行, 这里为了防止属性冲突使用了 ES6 的 Symbol 类型

Function.prototype.mycall = function (context = window, ...args) {
    let caller = Symbol('caller')
    context[caller] = this
    let res = context[caller](...args)
    delete context[caller]
    return res
}
// apply
Function.prototype.myapply = function (context, arr = []) {
    eval(this.toString().replace('this', context))
    this(...arr)
}
// bind
Function.prototype.myBind = function (context, ...args) {
    return function () {
        this.call(context, ...args)
    }
}

call方法的重点知识

首先通过原型链机制找到Function.prototype上的call方法, 并且让call方法执行, call方法中的this是fn1

然后, 在call方法执行的过程中先让fn1中的'this关键字变为fn2', 然后再让fn1方法执行

function fn1 () {console.log(1)}
function fn2 () {console.log(2)}
fn1.call(fn2) // 1

首先通过原型链机制找到Function.prototype上的call方法, 并且让call方法执行(即最后一个call执行), call方法中的this是fn1.call这个方法,然后, 在最后一个call方法执行的过程中先让fn1.call中的'this关键字', 变为fn2, 再让fn1.call执行

fn1.call.call(fn2) // 2
fn1.call.call.call.call.call(fn2) // 2

// call方法下面的情况执行this都是window
fn1.call() // window
fn1.call(null) // window
fn1.call(undefined) // window

bind & call

// bind区别于call和apply
fn1.call(fn2, 1, 2) // 改变fn1的this为fn2, 然后执行fn1(改变后的)
fn1.bind(fn2, 1, 2) // 只是改变了fn1中的this为fn2, 但是不执行fn1, 会返回改变this之后的fn1