手动实现new call apply bind

212 阅读2分钟

new

new用来创建一个新的对象

new的过程:

  1. 以构造函数原型对象为原型创建一个新对象
  2. 以新对象为 this,执行函数的
  3. 如果函数的返回值是对象,返回这个对象,否则返回第一步创建的新对象

实现

function myNew(fn, ...args) {
  const obj = Object.create(fn.prototype)
  const res = fn.call(obj, ...args)
  return res instanceof Object ? res : obj
}

call

call用来指定函数调用的this

call的使用

fn.call(thisArg, arg1, arg2, arg3 ...)

call的过程:

  1. 改变执行函数的this指向为第一个参数
  2. 执行函数

实现:实际上就是把方法挂在指定对象上,执行然后删除

Function.prototype.myCall = function(context, ...args) {
    // fn.call(...)  this就是fn
    if (typeof this !== 'function') {
        throw new TypeError('this is not a function')
    }
    context = context || window
    context.fn = this
    const res = context.fn(...args)
    delete context.fn
    return res
}

apply

apply同call 只是使用时传参不同

apply的使用

fn.apply(thisArg, [arg1, arg2, arg3...])

实现

Function.prototype.myApply = function (context, arg) {
    if (typeof this !== 'function') {
        throw new TypeError('this is not a function')
    }
    context = context || window
    context.fn = this
    let res
    if (arg) {
        res = context.fn(...args)
    } else {
        res = context.fn()
    }
    delete context.fn
    return res
}

bind

bind不同于call和apply,bind返回的是一个新的函数

bind的使用

fn.bind(thisArg, arg1, arg2, arg3)

bind的过程:

  1. 返回一个新的函数

  2. 第一个参数被指定为新函数的this

实现

Function.prototype.myBind = function(context, ...args) {
    const that = this
    return function() {
        that.apply(context, args.concat(...arguments))
    }
}

由于bind返回的是一个函数,函数可以用new运算符

通过new运算符操作bind生成的函数时,bind绑定的this会失效,此时的this会指向new生成的实例

改造下:

Function.prototype.myBind = function(context, ...args) {
    const that = this
    const Fn = new Function()
    function resFn() {
        // 判断这里面的this是否是构造函数的实例,如果是说明是用了new运算符
        that.apply(this instanceof resFn ? this : context, args.concat(...arguments))
    }
    Fn.prototype = this.prototype
  	//避免污染原型,new一个新对象
    resFn.prototype = new Fn()
    return resFn
}