手写call、apply和bind的实现

158 阅读1分钟

call的实现

function getType(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1)
}

// call
Function.prototype._call = function (context, ...args) {
    let _self = this
    context = (getType(context) === 'Null' || getType(context) === 'Undefined') ? window : Object(context)
    context.fn = _self
    let result = context.fn(...args)
    delete context.fn
    return result
}

apply的实现

// apply
Function.prototype._apply = function (context = window, ...args) {
    let _self = this
    context = (getType(context) === 'Null' || getType(context) === 'Undefined') ? window : Object(context)
    context.fn = _self
    args = !args ? Array.from(args) : []
    let result = context.fn(args)
    delete context.fn
    return result
}

apply接受的第二参数不要求一定是数组,类数组对象也可以,所以使用Array.from转化一下

bind的实现

function A(sex, age) {
    this.sex = sex
    this.age = age
    return this
}
let obj = {}
let B = A.bind(obj)
console.log(B(1, 20)) //  {sex: 1, age: 20}

根据上面可以实现:

function getType(obj) {
    return Object.prototype.toString.call(obj).slice(8, -1)
}

Function.prototype._bind = function (obj, ...args) {
    let _self = this
    obj = (getType(obj) === 'Null' || getType(obj) === 'Undefined') ? window : Object(obj)
    return function (...argv) {
        return _self.apply(obj, [...args, ...argv])
    }
}

还要考虑到bind返回的函数作为构造函数被调用:

function A(sex, age) {
    this.sex = sex
    this.age = age
    return this
}
let obj = {}
let B = A.bind(obj)
B(1, 20) //  {sex: 1, age: 20}
let newObj = new B(0, 18)
console.log(obj) // {sex: 1, age: 20}
console.log(newObj) // {sex: 0, age: 18}

当bind返回的函数作为构造函数被调用时,this会指向通过该构造函数实例化后得到的对象,由此可以实现:

Function.prototype._bind = function (obj, ...args) {
    let _self = this
    obj = (getType(obj) === 'Null' || getType(obj) === 'Undefined') ? window : Object(obj)
    function bindFn(...argv) {
        return _self.apply(new.target ? this : obj, [...args, ...argv])
    }
    // 维护原型,使用一个空函数做中转,防止修改构造函数的原型时同时修改原函数的原型
    function FNnop() { }
    FNnop.prototype = _self.prototype
    bindFn.prototype = new FNnop()
    return bindFn
}
obj = (getType(obj) === 'Null' || getType(obj) === 'Undefined') ? window : Object(obj)

这段代码是因为call、apply和bind传入的第一个参数如果是null或者undefined时,非严格模式下函数中的this指向windows,其余的类型如数字、字符串等会被转为基础封装类型(Number { }、String{ })