    /** Used as references for various `Number` constants. */
    const MAX_SAFE_INTEGER = 9007199254740991
    /**
     * Used to match `RegExp`
     * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
     */
    const reRegExpChar = /[\\^$.*+?()[\]{}|]/g
    /** Used to detect if a method is native. */
    const reIsNative = RegExp(
      `^${Function.prototype.toString
        .call(Object.prototype.hasOwnProperty)
        .replace(reRegExpChar, '\\$&')
        .replace(
          /hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,
          '$1.*?'
        )}$`
    )

    /**
     * 类型验证工具
     * @classdesc 类型验证工具
     * @class CheckUtil
     * @link https://www.lodashjs.com/docs/lodash.isArrayLike
     */
    class CheckUtil {
      /**
       * Gets the `toStringTag` of `value`.
       *
       * @private
       * @param {*} value The value to query.
       * @returns {string} Returns the `toStringTag`.
       */
      static getTag(value) {
        if (value === null) {
          return value === undefined ? '[object Undefined]' : '[object Null]'
        }
        return Object.prototype.toString.call(value)
      }

      /**
       * Checks if `value` is classified as a boolean primitive or object.
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
       * @example
       *
       * isBoolean(false)
       * // => true
       *
       * isBoolean(null)
       * // => false
       */
      static isBoolean(value) {
        return (
          value === true ||
          value === false ||
          (CheckUtil.isObjectLike(value) &&
            CheckUtil.getTag(value) === '[object Boolean]')
        )
      }

      /**
       * Checks if `value` is the
       * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
       * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an object, else `false`.
       * @example
       *
       * isObject({})
       * // => true
       *
       * isObject([1, 2, 3])
       * // => true
       *
       * isObject(Function)
       * // => true
       *
       * isObject(null)
       * // => false
       */
      static isObject(value) {
        const type = typeof value
        return value !== null && (type === 'object' || type === 'function')
      }

      /**
       * Checks if `value` is object-like. A value is object-like if it's not `null`
       * and has a `typeof` result of "object".
       * @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
       */
      static isObjectLike(value) {
        return typeof value === 'object' && value !== null
      }

      /**
       * Checks if `value` is classified as a `Function` object.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a function, else `false`.
       * @example
       *
       * isFunction(class Any{})
       * // => true
       *
       * isFunction(() => {})
       * // => true
       *
       * isFunction(async () => {})
       * // => true
       *
       * isFunction(function * Any() {})
       * // => true
       *
       * isFunction(Math.round)
       * // => true
       *
       * isFunction(/abc/)
       * // => false
       */
      static isFunction(value) {
        return typeof value === 'function'
      }

      /**
       * Checks if `value` is a valid array-like length.
       *
       * **Note:** This method is loosely based on
       * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
       *
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
       * @example
       *
       * isLength(3)
       * // => true
       *
       * isLength(Number.MIN_VALUE)
       * // => false
       *
       * isLength(Infinity)
       * // => false
       *
       * isLength('3')
       * // => false
       */
      static isLength(value) {
        return (
          typeof value === 'number' &&
          value > -1 &&
          value % 1 === 0 &&
          value <= MAX_SAFE_INTEGER
        )
      }

      /**
       * Checks if `value` is classified as a `String` primitive or object.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a string, else `false`.
       * @example
       *
       * isString('abc')
       * // => true
       *
       * isString(1)
       * // => false
       */
      static isString(value) {
        const type = typeof value
        return (
          type === 'string' ||
          (type === 'object' &&
            value !== null &&
            !Array.isArray(value) &&
            CheckUtil.getTag(value) === '[object String]')
        )
      }

      /**
       * Checks if `value` is classified as a `Symbol` primitive or object.
       *
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
       * @example
       *
       * isSymbol(Symbol.iterator)
       * // => true
       *
       * isSymbol('abc')
       * // => false
       */
      static isSymbol(value) {
        const type = typeof value
        return (
          type === 'symbol' ||
          (type === 'object' &&
            value !== null &&
            CheckUtil.getTag(value) === '[object Symbol]')
        )
      }

      /**
       * Checks if `value` is `undefined`.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
       * @example
       *
       * isUndefined(void 0)
       * // => true
       *
       * isUndefined(null)
       * // => false
       */
      static isUndefined(value) {
        return value === undefined
      }

      /**
       * Checks if `value` is `null`.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
       * @example
       *
       * isNull(null)
       * // => true
       *
       * isNull(void 0)
       * // => false
       */
      static isNull(value) {
        return value === null
      }

      /**
       * Checks if `value` is `Array`.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is `Array`, else `false`.
       * @example
       *
       * isArray([1,2,3])
       * // => true
       */
      static isArray(value) {
        return Array.isArray(value)
      }

      /**
       * Checks if `value` is array-like. A value is considered array-like if it's
       * not a function and has a `value.length` that's an integer greater than or
       * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
       *
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
       * @example
       *
       * isArrayLike([1, 2, 3])
       * // => true
       *
       * isArrayLike(document.body.children)
       * // => true
       *
       * isArrayLike('abc')
       * // => true
       *
       * isArrayLike(Function)
       * // => false
       */
      static isArrayLike(value) {
        return (
          value !== null &&
          typeof value !== 'function' &&
          CheckUtil.isLength(value.length)
        )
      }

      /**
       * This method is like `isArrayLike` except that it also checks if `value`
       * is an object.
       *
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an array-like object,
       *  else `false`.
       * @example
       *
       * isArrayLikeObject([1, 2, 3])
       * // => true
       *
       * isArrayLikeObject(document.body.children)
       * // => true
       *
       * isArrayLikeObject('abc')
       * // => false
       *
       * isArrayLikeObject(Function)
       * // => false
       */
      static isArrayLikeObject(value) {
        return CheckUtil.isObjectLike(value) && CheckUtil.isArrayLike(value)
      }

      /**
       * Checks if `value` is `null` or `undefined`.
       *
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
       * @example
       *
       * isNil(null)
       * // => true
       *
       * isNil(void 0)
       * // => true
       *
       * isNil(NaN)
       * // => false
       */
      static isNil(value) {
        return value === null
      }

      /**
       * Checks if `value` is classified as a `Number` primitive or object.
       *
       * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
       * classified as numbers, use the `Number.isFinite` method.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a number, else `false`.
       * @see isInteger, toInteger, toNumber
       * @example
       *
       * isNumber(3)
       * // => true
       *
       * isNumber(Number.MIN_VALUE)
       * // => true
       *
       * isNumber(Infinity)
       * // => true
       *
       * isNumber('3')
       * // => false
       */
      static isNumber(value) {
        return (
          typeof value === 'number' ||
          (CheckUtil.isObjectLike(value) &&
            CheckUtil.getTag(value) === '[object Number]')
        )
      }

      /**
       * Checks if `value` is a plain object, that is, an object created by the
       * `Object` constructor or one with a `[[Prototype]]` of `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
       */
      static isPlainObject(value) {
        if (
          !CheckUtil.isObjectLike(value) ||
          CheckUtil.getTag(value) !== '[object Object]'
        ) {
          return false
        }
        if (Object.getPrototypeOf(value) === null) {
          return true
        }
        let proto = value
        while (Object.getPrototypeOf(proto) !== null) {
          proto = Object.getPrototypeOf(proto)
        }
        return Object.getPrototypeOf(value) === proto
      }

      /**
       * Checks if `value` is likely a DOM element.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
       * @example
       *
       * isElement(document.body)
       * // => true
       *
       * isElement('<body>')
       * // => false
       */
      static isElement(value) {
        return (
          CheckUtil.isObjectLike(value) &&
          value.nodeType === 1 &&
          !CheckUtil.isPlainObject(value)
        )
      }

      /**
       * 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
       */
      static isError(value) {
        if (!CheckUtil.isObjectLike(value)) {
          return false
        }
        const tag = CheckUtil.getTag(value)
        return (
          tag === '[object Error]' ||
          tag === '[object DOMException]' ||
          (typeof value.message === 'string' &&
            typeof value.name === 'string' &&
            !CheckUtil.isPlainObject(value))
        )
      }

      /**
       * Checks if `value` is likely an `arguments` object.
       *
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
       * @example
       *
       * isArguments(function() { return arguments }())
       * // => true
       *
       * isArguments([1, 2, 3])
       * // => false
       */
      static isArguments(value) {
        return (
          CheckUtil.isObjectLike(value) &&
          CheckUtil.getTag(value) === '[object Arguments]'
        )
      }

      /**
       * Checks if `value` is a pristine native function.
       *
       * @since 3.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a native function,
       *  else `false`.
       * @example
       *
       * isNative(Array.prototype.push)
       * // => true
       *
       * isNative(isDate)
       * // => false
       */
      static isNative(value) {
        return CheckUtil.isObject(value) && reIsNative.test(value)
      }
    }

    export default CheckUtil