Skip to content

Latest commit

 

History

History
99 lines (83 loc) · 3.14 KB

深入理解JavaScript之手写call, apply, bind方法.md

File metadata and controls

99 lines (83 loc) · 3.14 KB

深入理解 JavaScript 之手写 call, apply, bind 方法

这是老生常谈的手写了,今天想自己试着实现一下,做个笔记。

call 方法

Function.prototype.myCall = function (context) {
  if (context === undefined || context === null) {
    context = window // 指定为 null 和 undefined 的 this 值会自动指向全局对象
  } else {
    context = Object(context) // 值为原始值(数字,字符串,布尔值)的 this 会指向该原始值的实例对象
  }

  const fn = Symbol('uniqueFn') // 用 Symbol 是防止跟上下文的原属性冲突
  context[fn] = this
  let arg = [...arguments].slice(1)
  let result = context[fn](...arg)
  delete context[fn]
  return result
}

判断函数上下文网上也有写法是:

context = context ? Object(context) : window
context = context || window

但不是很严谨,因为遇到空字符串,0false时,context也会被判断为window

apply 方法

Function.prototype.myApply = function (context) {
  if (context === undefined || context === null) {
    context = window
  } else {
    context = Object(context)
  }

  // 判断是否为类数组对象
  function isArrayLike(o) {
    if (
      o && // o 不是null、undefined等
      typeof o === 'object' && // o是对象
      isFinite(o.length) && // o.length是有限数值
      o.length >= 0 && // o.length为非负值
      o.length === Math.floor(o.length) && // o.length是整数
      o.length < Math.pow(2, 32)
    ) {
      return true
    }
    return false
  }
  const fn = Symbol('uniqueFn')
  context[fn] = this
  let args = arguments[1]
  let result
  if (!Array.isArray(args) && !isArrayLike(args)) {
    throw new Error('CreateListFromArrayLike called on non-object') // 第二个参数不为数组且不为类对象数组
  } else {
    args = Array.from(args) // 转为数组
    result = context[fn](...args)
  }
  delete context[fn]
  return result
}

bind 方法

实现 bind 要额外考虑一个问题:方法一个绑定函数也能使用 new 操作符创建对象:这种行为就像把原函数当成构造器。提供的 this 值被忽略,同时调用时的参数被提供给模拟函数

Function.prototype.myBind = function (context) {
  if (typeof this !== 'function') {
    throw new Error('Expected this is a function')
  }
  let self = this
  let args = [...arguments].slice(1)
  let fn = function (...innerArg) {
    const isNew = this instanceof fn // 返回的fn是否通过new调用
    return self.apply(isNew ? this : Object(context), args.concat(innerArg)) // new调用就绑定到this上,否则就绑定到传入的context上
  }
  // 复制原函数的prototype给fn,一些情况下函数没有prototype,比如箭头函数
  if (self.prototype) {
    fn.prototype = Object.create(self.prototype)
  }
  return fn // 返回拷贝的函数
}