JS手写题-call-apply-bind

154 阅读4分钟

call,apply,bind有什么用?

call,apply和bind函数都是用于改变函数中this的指向

call和apply区别

call和apply函数只有参数上的区别。如下面代码所示

function foo(x, y) {
    console.log(x, y)
    console.log(this)
}

const obj = {
    name: 'obj'
}
// call函数在调用时传递给foo的参数要一个一个传递进去。
foo.call(obj, 1, 2) // 1, 2, obj对象
// apply函数在调用时传递给foo的参数要以数组的形式传递进去。
foo.apply(obj, [1, 2]) // 1, 2, obj对象

call,apply和bind的区别

call和apply函数都会执行原函数,而bind函数不会,它会返回一个绑定了this的函数。

function foo(x, y) {
    console.log(x, y)
    console.log(this)
}

const obj = {
    name: 'obj'
}
// bind函数会返回一个新的函数,这个函数的this会被绑定为obj
// 同时传递给原foo函数的参数,可以在bind的时候传递进去,也可以在执行的时候再传递。
// 同样也可以bind的时候传递一部分参数,执行的时候传递剩下的部分参数。
const newFoo = foo.bind(obj, 1)
newFoo(2)  // 1, 2, obj对象

call函数的实现

实现call函数的主要思路就是利用this的隐式绑定规则,当函数作为一个对象的属性时,通过对象 . 方法名的形式调用函数,函数中的this会被绑定为该调用对象。

Function.prototype.myCall = function (thisArgs, ...args) {
    // 获取调用该方法的函数,在上面的例子中,这里的fn就是foo函数
    const fn = this
    // 边界判断,当用户绑定的值为null或者undefined是,将该this赋值为全局的this
    // 当用户希望绑定的this为基本类型的数据,需要使用Object函数将其包装为包装对象
    thisArgs = (thisArgs === undefined || thisArgs === null) ? globalThis : Object(thisArgs)
    // 创建一个symbol作为key,防止属性名冲突。
    const sym = Symbol()
    // 将函数作为属性赋值给thisArgs
    thisArgs[sym] = fn
    // 执行fn函数,并将fn函数的执行结果返回出去
    // 这里函数参数中将thisArgs中的sym属性给删除掉,是为了在函数内打印this对象时,不会有我们添加的sym属性。
    return thisArgs[sym](...(delete thisArgs[sym] === true ? args : []))
}

apply函数的实现

在实现完call函数之后,apply函数就比较简单了,它和call函数只有参数上的不同。

// apply方法和call方法只有第二个参数的不同,在apply方法中这里的args必须为一个数组,所以我们不需要采用剩余参数的写法。
Function.prototype.myApply = function (thisArgs, args) {
    // 如果想要对args参数的类型进行判断,可以自行判断,这里就不对该参数的类型进行判断了。
    // 获取调用该方法的函数,在上面的例子中,这里的fn就是foo函数
    const fn = this
    // 边界判断,当用户绑定的值为null或者undefined是,将该this赋值为全局的this
    // 当用户希望绑定的this为基本类型的数据,需要使用Object函数将其包装为包装对象
    thisArgs = (thisArgs === undefined || thisArgs === null) ? globalThis : Object(thisArgs)
    // 创建一个symbol作为key,防止属性名冲突。
    const sym = Symbol()
    // 将函数作为属性赋值给thisArgs
    thisArgs[sym] = fn
    // 执行fn函数,并将fn函数的执行结果返回出去
    // 这里函数参数中将thisArgs中的sym属性给删除掉,是为了在函数内打印this对象时,不会有我们添加的sym属性。
    return thisArgs[sym](...(delete thisArgs[sym] === true ? args : []))
}

bind函数的实现

bind函数和call,apply函数最主要的区别就是bing函数会返回一个绑定了this的函数,而不是调用它。

Function.prototype.myBind = function (thisArgs, ...args) {
     // 获取调用该方法的函数,在上面的例子中,这里的fn就是foo函数
    const fn = this
    // 边界判断,当用户绑定的值为null或者undefined是,将该this赋值为全局的this
    // 当用户希望绑定的this为基本类型的数据,需要使用Object函数将其包装为包装对象
    thisArgs = (thisArgs === undefined || thisArgs === null) ? globalThis : Object(thisArgs)
    // 创建一个symbol作为key,防止属性名冲突。
    const sym = Symbol()
    // 返回一个函数
    return function(...args1) {
        // 将第一次传递的参数和第二次传递的参数合并起来
        const totalArgs = [...args, ...args1]
        thisArgs[sym] = fn
        
        return thisArgs[sym](...(delete thisArgs[sym] ? totalArgs : []))
    }
}

总结

总体上来说,call和apply和bind函数的实现并不是很难,主要是利用了this的隐式绑定。