lodash 工具函数 attempt

985 阅读2分钟

本文涉及类型检查,错误对象以及attempt函数本身

类型检查

getTag

const toString = Object.prototype.toString

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function getTag(value) {
  if (value == null) {
    // 兼容javascript低版本,特殊处理null和undefined
    return value === undefined ? '[object Undefined]' : '[object Null]'
  }
  return toString.call(value)
}

isObjectLike

/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 * 校验一个对象是否是一个object-like对象。一个object-like不是null并且typeof返回值为object
 * 
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * isObjectLike({})
 * // => true
 *
 * isObjectLike([1, 2, 3])
 * // => true
 *
 * isObjectLike(Function)
 * // => false
 *
 * isObjectLike(null)
 * // => false
 */
function isObjectLike(value) {
  return typeof value === 'object' && value !== null
}

isPlainObject

依赖于getTag函数以及isObjectLike函数

是否是通过Object构造函数直接创建的对象,创建方式

  • {}对象字面量
  • Object.create({})方法
  • Object({})方法
/**
 * Checks if `value` is a plain object, that is, an object created by the
 * `Object` constructor or one with a `[[Prototype]]` of `null`.
 * 直接由`Object`构造函数创建的对象 或者 对象的prototype 为 null 的对象
 *
 * @since 0.8.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
 * @example
 *
 * function Foo() {
 *   this.a = 1
 * }
 *
 * isPlainObject(new Foo)
 * // => false
 *
 * isPlainObject([1, 2, 3])
 * // => false
 *
 * isPlainObject({ 'x': 0, 'y': 0 })
 * // => true
 *
 * isPlainObject(Object.create(null))
 * // => true
 */
function isPlainObject(value) {
  if (!isObjectLike(value) || getTag(value) != '[object Object]') {
    return false
  }
  // 通过Object.create(null)创建的对象
  // console.log( Object.getPrototypeOf(Object.create(null)))
  if (Object.getPrototypeOf(value) === null) {
    return true
  }
  let proto = value
  // 按对象原型链向上查找
  while (Object.getPrototypeOf(proto) !== null) {
    proto = Object.getPrototypeOf(proto)
  }
  return Object.getPrototypeOf(value) === proto
}

isError

原生Error对象,主要包含namemessagetoString这几个属性。详见mdn

/**
 * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
 * `SyntaxError`, `TypeError`, or `URIError` object.
 *
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
 * @example
 *
 * isError(new Error)
 * // => true
 *
 * isError(Error)
 * // => false
 */
function isError(value) {
  // 不是一个"类"对象
  if (!isObjectLike(value)) {
    return false
  }
  // 获取对象错误类型
  const tag = getTag(value)
  return tag == '[object Error]' || tag == '[object DOMException]' ||
  // error message 属性为 string 类型 并且 name 属性为 string 类型 并且不是通过直接调用Object构造函数直接创建的对象
    (typeof value.message === 'string' && typeof value.name === 'string' && !isPlainObject(value))
}

attempt

尝试执行传入的函数或捕获相应的错误对象并返回。函数执行的参数由传入attemp函数的参数决定。

/**
 * Attempts to invoke `func`, returning either the result or the caught error
 * object. Any additional arguments are provided to `func` when it's invoked.
 * 尝试执行传入的函数,返回执行函数的值或捕获错误对象。任意传递给attempt函数的额外参数都会传递给func
 * 
 * @since 3.0.0
 * @category Util
 * @param {Function} func The function to attempt.
 * @param {...*} [args] The arguments to invoke `func` with.
 * @returns {*} Returns the `func` result or error object.
 * @example
 *
 * // Avoid throwing errors for invalid selectors.
 * const elements = attempt(selector =>
 *   document.querySelectorAll(selector), '>_>')
 *
 * if (isError(elements)) {
 *   elements = []
 * }
 */
function attempt(func, ...args) {
  try {
    return func(...args)
  } catch (e) {
    return isError(e) ? e : new Error(e)
  }
}