美文网首页JavaScript小结
js 类型检测(红宝书)

js 类型检测(红宝书)

作者: 别过经年 | 来源:发表于2017-05-08 10:24 被阅读69次

    4.1.4 检测类型

    要检测一个变量是不是基本数据类型?第 3 章介绍的 typeof 操作符是最佳的工具。说得更具体一
    点, typeof 操作符是确定一个变量是字符串、数值、布尔值,还是 undefined 的最佳工具。如果变
    量的值是一个对象或 null,则 typeof 操作符会像下面例子中所示的那样返回"object":

    var s = "Nicholas";
    var b = true;
    var i = 22;
    var u;
    var n = null;
    var o = new Object();
    alert(typeof s); //string
    alert(typeof i); //number
    alert(typeof b); //boolean
    alert(typeof u); //undefined
    alert(typeof n); //object
    alert(typeof o); //object
    DeterminingTypeExample01.htm
    

    虽然在检测基本数据类型时 typeof 是非常得力的助手,但在检测引用类型的值时,这个操作符的
    用处不大。 通常, 我们并不是想知道某个值是对象,而是想知道它是什么类型的对象。为此, ECMAScript
    提供了 instanceof 操作符,其语法如下所示:
    result = variable instanceof constructor
    如果变量是给定引用类型(根据它的原型链来识别;第 6 章将介绍原型链)的实例,那么
    instanceof 操作符就会返回 true。请看下面的例子:
    alert(person instanceof Object); // 变量 person 是 Object 吗?
    alert(colors instanceof Array); // 变量 colors 是 Array 吗?
    alert(pattern instanceof RegExp); // 变量 pattern 是 RegExp 吗?
    根据规定,所有引用类型的值都是 Object 的实例。因此,在检测一个引用类型值和 Object 构造
    函数时, instanceof 操作符始终会返回 true。当然,如果使用 instanceof 操作符检测基本类型的
    值,则该操作符始终会返回 false,因为基本类型不是对象。
    使用 typeof 操作符检测函数时,该操作符会返回"function"。在 Safari 5 及
    之前版本和 Chrome 7 及之前版本中使用 typeof 检测正则表达式时,由于规范的原
    因,这个操作符也返回"function"。 ECMA-262 规定任何在内部实现[[Call]]方法
    的对象都应该在应用 typeof 操作符时返回"function"。由于上述浏览器中的正则
    表达式也实现了这个方法,因此对正则表达式应用 typeof 会返回"function"。在
    IE 和 Firefox 中,对正则表达式应用 typeof 会返回"object"。

    22.1.1 安全的类型检测

    JavaScript 内置的类型检测机制并非完全可靠。事实上,发生错误否定及错误肯定的情况也不在少
    数。比如说 typeof 操作符吧,由于它有一些无法预知的行为,经常会导致检测数据类型时得到不靠谱
    的结果。 Safari(直至第 4 版)在对正则表达式应用 typeof 操作符时会返回"function",因此很难确
    定某个值到底是不是函数。
    再比如, instanceof 操作符在存在多个全局作用域(像一个页面包含多个 frame)的情况下,也
    是问题多多。一个经典的例子(第 5 章也提到过)就是像下面这样将对象标识为数组。
    var isArray = value instanceof Array;
    以上代码要返回 true, value 必须是一个数组,而且还必须与 Array 构造函数在同个全局作用域
    中。(别忘了, Array 是 window 的属性。)如果 value 是在另个 frame 中定义的数组,那么以上代码
    就会返回 false。
    在检测某个对象到底是原生对象还是开发人员自定义的对象的时候,也会有问题。出现这个问题的
    原因是浏览器开始原生支持 JSON 对象了。因为很多人一直在使用 Douglas Crockford 的 JSON 库,而该
    库定义了一个全局 JSON 对象。于是开发人员很难确定页面中的 JSON 对象到底是不是原生的。
    解决上述问题的办法都一样。大家知道,在任何值上调用 Object 原生的 toString()方法,都会
    返回一个[object NativeConstructorName]格式的字符串。每个类在内部都有一个[[Class]]属
    性,这个属性中就指定了上述字符串中的构造函数名。举个例子吧。
    alert(Object.prototype.toString.call(value)); //"[object Array]"
    由于原生数组的构造函数名与全局作用域无关,因此使用 toString()就能保证返回一致的值。利
    用这一点,可以创建如下函数:

    function isArray(value){
    return Object.prototype.toString.call(value) == "[object Array]";
    }
    

    同样,也可以基于这一思路来测试某个值是不是原生函数或正则表达式:

    function isFunction(value){
    return Object.prototype.toString.call(value) == "[object Function]";
    }
    function isRegExp(value){
    return Object.prototype.toString.call(value) == "[object RegExp]";
    }
    

    不过要注意,对于在 IE 中以 COM 对象形式实现的任何函数, isFunction()都将返回 false(因
    为它们并非原生的 JavaScript 函数,请参考第 10 章中更详细的介绍)。
    这一技巧也广泛应用于检测原生 JSON 对象。 Object 的 toString()方法不能检测非原生构造函
    数的构造函数名。因此,开发人员定义的任何构造函数都将返回[object Object]。有些 JavaScript 库会包
    含与下面类似的代码。
    var isNativeJSON = window.JSON && Object.prototype.toString.call(JSON) ==
    "[object JSON]";
    在 Web 开发中能够区分原生与非原生 JavaScript 对象非常重要。只有这样才能确切知道某个对象到
    底有哪些功能。这个技巧可以对任何对象给出正确的结论。
    请注意, Object.prototpye.toString()本身也可能会被修改。本节讨论的
    技巧假设 Object.prototpye.toString()是未被修改过的原生版本。

    jQuery和lodash都是采用安全的类型检测的,下面是lodash代码:

    lodash/.internal/baseGetTag.js

    const objectProto = Object.prototype
    const hasOwnProperty = objectProto.hasOwnProperty
    const toString = objectProto.toString
    const symToStringTag = typeof Symbol != 'undefined' ? Symbol.toStringTag : undefined
    
    /**
     * The base implementation of `getTag` without fallbacks for buggy environments.
     *
     * @private
     * @param {*} value The value to query.
     * @returns {string} Returns the `toStringTag`.
     */
    function baseGetTag(value) {
      if (value == null) {
        return value === undefined ? '[object Undefined]' : '[object Null]'
      }
      if (!(symToStringTag && symToStringTag in Object(value))) {
        return toString.call(value)
      }
      const isOwn = hasOwnProperty.call(value, symToStringTag)
      const tag = value[symToStringTag]
      let unmasked = false
      try {
        value[symToStringTag] = undefined
        unmasked = true
      } catch (e) {}
    
      const result = toString.call(value)
      if (unmasked) {
        if (isOwn) {
          value[symToStringTag] = tag
        } else {
          delete value[symToStringTag]
        }
      }
      return result
    }
    
    export default baseGetTag
    

    lodash/isFunction.js

    import baseGetTag from './.internal/baseGetTag.js'
    import isObject from './isObject.js'
    
    /**
     * 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(_)
     * // => true
     *
     * isFunction(/abc/)
     * // => false
     */
    function isFunction(value) {
      if (!isObject(value)) {
        return false
      }
      // The use of `Object#toString` avoids issues with the `typeof` operator
      // in Safari 9 which returns 'object' for typed arrays and other constructors.
      const tag = baseGetTag(value)
      return tag == '[object Function]' || tag == '[object AsyncFunction]' ||
        tag == '[object GeneratorFunction]' || tag == '[object Proxy]'
    }
    
    export default isFunction
    

    javascript中对变量类型的判断

    相关文章

      网友评论

        本文标题:js 类型检测(红宝书)

        本文链接:https://www.haomeiwen.com/subject/ublmtxtx.html