【问题标题】:Better way to get type of a Javascript variable?获取 Javascript 变量类型的更好方法?
【发布时间】:2011-11-15 11:13:00
【问题描述】:

有没有比typeof更好的方法在JS中获取变量的类型?这样做时效果很好:

> typeof 1
"number"
> typeof "hello"
"string"

但是尝试了也没用:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

我知道instanceof,但这需要你事先知道类型。

> [1,2] instanceof Array
true
> r instanceof RegExp
true

有没有更好的办法?

【问题讨论】:

  • 仅供参考,Chrome 中的 typeof new RegExp(/./); // "function" 问题似乎已在 Chrome 14 中得到修复。
  • 警告:如果您正在缩小您的 JS 并且您正在使用诸如打字稿类之类的“自定义对象”,那么下面的一些答案最终会为您提供混淆的函数名称,例如 n而不是预期的原始名称。例如constructor.name 可能会给你n 而不是预期的全名

标签: javascript types typeof


【解决方案1】:

我知道我很晚才回答这个问题,但对于任何可能偶然发现这个问题的人,我个人还创建了一个类似于公认的函数:

const toType = (param) => {
    if (param === NaN) return 'NaN';
    else return Object.prototype.toString.call(param).replace('[object ', '').slice(0, -1).toLowerCase();
}

除了NaN之外,结果完全相同。

【讨论】:

    【解决方案2】:

    我做了这个函数:

    (您应该将其命名为更独特,以免与其他全局名称冲突。)

    function type(theThing) {
        return Object.prototype.toString.call(theThing).match(/\s([\w]+)/)[1].toLowerCase()
    }
    
    type({})           //-> 'object'
    type([])           //-> 'array'
    type(function(){}) //-> 'function'
        
    type(null)         //-> 'null'
    type(undefined)    //-> 'undefined
    type(true)         //-> 'boolean'
    type('hello')      //-> 'string'
    type(42)           //-> 'number'
    
    type(Symbol())     //-> 'symbol'
    type(/abc/)        //-> 'regexp'
    type(new Set())    //-> 'set'
    // etc ...
    

    PS:上面的 F.NiX 制作了更强大的版本,它还告诉您由 Class 或构造函数创建的自定义对象的名称。

    【讨论】:

      【解决方案3】:

      您可能会发现以下功能很有用:

      function typeOf(obj) {
        return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
      }
      

      或者在 ES7 中(如果有进一步改进请评论)

      const { toString } = Object.prototype;
      
      function typeOf(obj) {
        const stringified = obj::toString();
        const type = stringified.split(' ')[1].slice(0, -1);
            
        return type.toLowerCase();
      }
      

      结果:

      typeOf(); //undefined
      typeOf(null); //null
      typeOf(NaN); //number
      typeOf(5); //number
      typeOf({}); //object
      typeOf([]); //array
      typeOf(''); //string
      typeOf(function () {}); //function
      typeOf(/a/) //regexp
      typeOf(new Date()) //date
      typeOf(new Error) //error
      typeOf(Promise.resolve()) //promise
      typeOf(function *() {}) //generatorfunction
      typeOf(new WeakMap()) //weakmap
      typeOf(new Map()) //map
      typeOf(async function() {}) //asyncfunction
      

      感谢@johnrees 通知我:错误、承诺、生成器函数

      【讨论】:

      • 谢谢。它也适用于日期对象:typeOf(new Date())
      • 啊,我忘记了 - 谢谢,现在已经包含在内了。
      • 还有typeOf(Promise.resolve())//promise typeOf(function *() {})//generatorfunction
      • 我无法在打字稿中得到这个答案。给出错误:[ts] Cannot redeclare block-scoped variable 'toString'
      • 不确定 TypeScript。你试过ES7版本吗?没有明确声明 toString 可能是原因?
      【解决方案4】:

      这个版本比较完整:

      const typeOf = obj => {
        let type = ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1]
        if (type === 'Object') {
          const results = (/^(function|class)\s+(\w+)/).exec(obj.constructor.toString())
          type = (results && results.length > 2) ? results[2] : ''
        }
        return type.toLowerCase()
      }
      

      现在不仅您可以获得这些结果:(正如他们已在此处回答的那样)

      undefined or empty -> undefined
      null -> null
      NaN -> number
      5 -> number
      {} -> object
      [] -> array
      '' -> string
      function () {} -> function
      /a/ -> regexp
      new Date() -> date
      new Error -> error
      Promise.resolve() -> promise
      function *() {} -> generatorfunction
      new WeakMap() -> weakmap
      new Map() -> map
      

      但您也可以从类或函数中获取您构造的每个实例或对象的类型:(这在其他答案之间无效,它们都返回对象)

      class C {
        constructor() {
          this.a = 1
        }
      }
      
      function F() {
        this.b = 'Foad'
      }
      
      typeOf(new C()) // -> c
      typeOf(new F()) // -> f
      

      【讨论】:

      • 嗨,我看到了一种可能的改进(但值得怀疑)-> 如果函数返回自定义的类名/对象名,它应该区分大小写,返回确切的名称。
      【解决方案5】:

      typeof 条件用于检查变量类型,如果你在 if-else 条件下检查变量类型 例如

      if(typeof Varaible_Name "undefined")
      {
      
      }
      

      【讨论】:

        【解决方案6】:

        我想这里最通用的解决方案是先检查undefinednull,然后调用constructor.name.toLowerCase()

        const getType = v =>
          v === undefined
            ? 'undefined'
            : v === null
              ? 'null'
              : v.constructor.name.toLowerCase();
        
        
        
        
        console.log(getType(undefined)); // 'undefined'
        console.log(getType(null)); // 'null'
        console.log(getType('')); // 'string'
        console.log(getType([])); // 'array'
        console.log(getType({})); // 'object'
        console.log(getType(new Set())); // `set'
        console.log(getType(Promise.resolve())); // `promise'
        console.log(getType(new Map())); // `map'
        

        【讨论】:

        • 据我所知 - .constructor.name 始终包含字符串值。对于 undefined/null,我们有函数返回的适当字符串。
        • 谢谢 - 删除我的 cmets...
        • 由于constructor 是一个继承属性,这对于使用Object.create(null) 创建的对象会中断。简单到可以修复,但是怎么称呼它呢? “[对象空]”?
        【解决方案7】:

        单行函数:

        function type(obj) {
            return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
        }
        

        这给出与jQuery.type() 相同的结果

        【讨论】:

          【解决方案8】:

          我的 2 美分! 真的,尽管答案列表很长,但我在这里抛出这个的部分原因是为了提供更多 all in one 输入解决方案,并在未来获得一些反馈,了解如何扩展它以包含更多real types

          使用以下解决方案,如前所述,我结合了这里找到的几个解决方案,并合并了一个 fix 用于在 jQuery 定义的对象上返回 jQuery如果有的话。我还将该方法附加到本机 Object 原型。我知道这通常是禁忌,因为它可能会干扰其他此类扩展,但我将其留给 user beware。如果您不喜欢这种方式,只需将基函数复制到您喜欢的任何位置,并将this 的所有变量替换为要传入的参数参数(例如arguments[0])。

          ;(function() {  //  Object.realType
              function realType(toLower) {
                  var r = typeof this;
                  try {
                      if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
                      else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
                  }
                  catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
                  return !toLower ? r : r.toLowerCase();
              }
              Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
                  ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
          })();
          

          然后简单地使用,像这样:

          obj.realType()  //  would return 'Object'
          obj.realType(true)  //  would return 'object'
          

          注意:有 1 个参数可以通过。如果是true 的布尔值,则返回值将始终为小写

          更多例子:

          true.realType();                            //  "Boolean"
          var a = 4; a.realType();                    //  "Number"
          $('div:first').realType();                   // "jQuery"
          document.createElement('div').realType()    //  "HTMLDivElement"
          

          如果您有任何要添加的内容可能对您有所帮助,例如定义使用另一个库(Moo、Proto、Yui、Dojo 等)创建对象的时间,请随时发表评论或编辑并继续下去更准确和精确。或者转到我为它制作的GitHub 并让我知道。您还可以在此处找到指向 cdn min 文件的快速链接。

          【讨论】:

          • 如果你这样做 (null).realType() 它会给 Uncaught TypeError: Cannot read properties of null (reading 'realType')
          【解决方案9】:
          function getType(obj) {
              if(obj && obj.constructor && obj.constructor.name) {
                  return obj.constructor.name;
              }
              return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
          }
          

          在我的初步测试中,它运行良好。第一种情况将打印使用“new”创建的任何对象的名称,第二种情况应该捕获其他所有内容。

          我使用(8, -1) 是因为我假设结果总是以[object 开头并以] 结尾,但我不确定在每种情况下都是如此。

          【讨论】:

            【解决方案10】:

            我们也可以从 ipr101

            改变一个小例子
            Object.prototype.toType = function() {
              return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
            }
            

            并调用为

            "aaa".toType(); // 'string'
            

            【讨论】:

            • 记住孩子们,操作 JavaScript 基础对象会导致兼容性和其他奇怪的问题。尽量在库和大型项目中谨慎使用它!
            【解决方案11】:

            Angus Croll 最近写了一篇关于此的有趣博客文章 -

            http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

            他分析了各种方法的优缺点,然后定义了一个新方法 'toType' -

            var toType = function(obj) {
              return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
            }
            

            【讨论】:

            • 这里有一个有趣的旁注——这可能会破坏将“主机”对象传递给函数的位置。例如,Internet Explorer ActiveXObject 实例。
            • 如果你创建了自己的对象类型(原型继承),你怎么能让它返回比“对象”更具体的值?这也作为问题 here 提出,但从未得到解决。
            • 如果您的类型命名包含 : 或 _ 您可以将此正则表达式扩展为 match(/\s([a-z,_,:,A-Z]+)/)
            • 正则表达式还需要包含诸如Uint8Array 之类的数字。相反,请考虑更通用的match(/\s([^\]]+)/),它应该可以处理任何事情。
            • 我建议改用\w 字运算符...
            【解决方案12】:

            YUI3 使用的一个相当不错的类型捕获函数:

            var TYPES = {
                'undefined'        : 'undefined',
                'number'           : 'number',
                'boolean'          : 'boolean',
                'string'           : 'string',
                '[object Function]': 'function',
                '[object RegExp]'  : 'regexp',
                '[object Array]'   : 'array',
                '[object Date]'    : 'date',
                '[object Error]'   : 'error'
            },
            TOSTRING = Object.prototype.toString;
            
            function type(o) {
                return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
            };
            

            这捕获了 javascript 提供的许多原语,但您始终可以通过修改 TYPES 对象来添加更多原语。请注意,Safari 中的 typeof HTMLElementCollection 将报告 function,但 type(HTMLElementCollection) 将返回 object

            【讨论】:

              【解决方案13】:

              您可以尝试使用constructor.name

              [].constructor.name
              new RegExp().constructor.name
              

              与所有 JavaScript 一样,最终总会有人指出这在某种程度上是邪恶的,所以 here is a link to an answer 很好地涵盖了这一点。

              另一种方法是使用Object.prototype.toString.call

              Object.prototype.toString.call([])
              Object.prototype.toString.call(/./)
              

              【讨论】:

              • name 是 ECMAScript 的一个扩展,并不适用于所有浏览器。
              • 由于确认怀疑在 javascript 中完成的所有事情都在某种程度上是邪恶的,因此投票赞成。
              • EVIL:如果您正在缩小代码,那么 constructor.name 可能最终会变成 n 之类的东西,而不是您期望的对象名称。所以要注意这一点——尤其是如果你只在生产中缩小。
              • nullundefined 很遗憾没有构造函数。
              • JavaScript 本身就是邪恶的。而且很傻。这个问题没有很好的答案这一事实就是一个活生生的证明。
              【解决方案14】:

              您可以将Object.prototype.toString 应用于任何对象:

              var toString = Object.prototype.toString;
              
              console.log(toString.call([]));
              //-> [object Array]
              
              console.log(toString.call(/reg/g));
              //-> [object RegExp]
              
              console.log(toString.call({}));
              //-> [object Object]
              

              这适用于所有浏览器,除了 IE - 当在从另一个窗口获得的变量上调用它时,它只会吐出[object Object]

              【讨论】:

              • @Xeon 我觉得对IE的仇恨有点过分了。 IE9 是一个比它的前辈更好的浏览器。
              • @pessimopoppotamus 但没有人愿意将浏览器更新到 IE9 使用 IE。
              • IE 9 是朝着正确方向迈出的一步,IE 10 看起来不错。顺便说一句,我提到的错误是 IE 9 中的回归 - 他们在 IE 8 中修复了它。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2014-04-01
              • 1970-01-01
              • 2012-01-08
              • 2021-08-20
              • 1970-01-01
              • 1970-01-01
              • 2019-03-20
              相关资源
              最近更新 更多