JavaScript实现new、call、apply和bind

303 阅读2分钟

new的实现

new操作符的作用过程

在使用new操作符调用函数时,函数内部的[[constructor]]会被调用,最终返回一个对象。new操作符的作用过程如下:

  • 创建一个新对象,将新对象的原型链修正
  • 将函数的this指向赋予新对象
  • 执行函数
  • 默认返回this(即新对象),如果函数显式指定了返回值,且返回值为对象,则返回显式指定的对象,如果返回值不是对象,则按默认返回新对象。

new操作符的实现

function _new(ctor, ...args) {
  if (typeof ctor !== 'function') {
    throw 'ctor must be function'
  }
  // 创建新对象,修正原型链
  const obj = Object.create(ctor.prototype)
  // 将this指向obj,并调用函数
  const res = ctor.apply(obj, args)
  // 根据函数的调用结果来确定返回值
  const isObject = typeof res === 'object' && res !== null
  const isFunction = typeof res === 'function'
  return isObject || isFunction? res: obj
}

call和apply的实现

call和apply的作用过程

call和apply都是函数的实例方法,在函数调用call和apply的时候,实际上是以指定this值的方式来调用函数,只是call和apply接收参数的方式有所差异,其他的一致。

call的实现

Function.prototype.call = function(context, ...args) {
  // 根据是否传入context来确上下文
  let context = context || window
  // 将要调用的函数绑定到context的属性fn上,以便使用context.fn()调用函数,即指定了函数this值为context
  context.fn = this
  const res = context.fn(...args)
  delete context.fn
  return res
}

apply的实现

Function.prototype.apply = function(context, args) {
  // 根据是否传入context来确上下文
  let context = context || window
  // 将要调用的函数绑定到context的属性fn上,以便使用context.fn()调用函数,即指定了函数this值为context
  context.fn = this
  const res = context.fn(...args)
  delete context.fn
  return res
}

bind的实现

bind的作用过程

bind方法是函数的实例方法,同样可以改变函数A调用时内部的this指向,只是返回值为函数B。函数B调用时才会真正调用函数A并改变函数A内部的this值。 因此,bind的实现过程中,会使用闭包机制将上下文对象绑定到函数B内部。

bind的实现

Function.prototype.bind = function(context, ...args) {
  if (typeof this !== 'function') {
    throw 'this must be function'
  }
  let self = this
  const boundFunc = function() {
    self.apply(this instanceof self? this: context, args.concat(Array.prototype.slice.call(arguments)))
  }
  // 原型链也要记录,因为boundFunc可能会被当做构造函数用
  if (this.prorotype) {
    boundFunc.prototype = this.prototype
  }
  return boundFunc
}