详解typeof
typeof语法
typeof是一个运算符
typeof 运算符后接操作数:
typeof operand typeof(operand) 复制代码
参数operand是一个表示对象或原始值的表达式,其类型将被返回。
typeof返回值表
类型 | 结果 |
Undefined | "undefined" |
Null | "object" (见下文) |
Boolean | "boolean" |
Number | "number" |
BigInt(ECMAScript 2020 新增) | "bigint" |
String | "string" |
Symbol (ECMAScript 2015 新增) | "symbol" |
宿主对象(由 JS 环境提供) | 取决于具体实现 |
Function 对象 (按照 ECMA-262 规范实现 [[Call]]) | "function" |
其他任何对象 | "object" |
检测示例
// 数值 typeof 37 === 'number'; typeof 3.14 === 'number'; typeof(42) === 'number'; typeof Math.LN2 === 'number'; typeof Infinity === 'number'; typeof NaN === 'number'; // 尽管它是 "Not-A-Number" (非数值) 的缩写 typeof Number(1) === 'number'; // Number 会尝试把参数解析成数值 typeof 42n === 'bigint'; // 字符串 typeof '' === 'string'; typeof 'bla' === 'string'; typeof `template literal` === 'string'; typeof '1' === 'string'; // 注意内容为数字的字符串仍是字符串 typeof (typeof 1) === 'string'; // typeof 总是返回一个字符串 typeof String(1) === 'string'; // String 将任意值转换为字符串,比 toString 更安全 // 布尔值 typeof true === 'boolean'; typeof false === 'boolean'; typeof Boolean(1) === 'boolean'; // Boolean() 会基于参数是真值还是虚值进行转换 typeof !!(1) === 'boolean'; // 两次调用 ! (逻辑非) 操作符相当于 Boolean() // Symbols typeof Symbol() === 'symbol'; typeof Symbol('foo') === 'symbol'; typeof Symbol.iterator === 'symbol'; // Undefined typeof undefined === 'undefined'; typeof declaredButUndefinedVariable === 'undefined'; typeof undeclaredVariable === 'undefined'; // 对象 typeof {a: 1} === 'object'; // 使用 Array.isArray 或者 Object.prototype.toString.call // 区分数组和普通对象 typeof [1, 2, 4] === 'object'; typeof new Date() === 'object'; typeof /regex/ === 'object'; // 历史结果请参阅正则表达式部分 // 下面的例子令人迷惑,非常危险,没有用处。避免使用它们。 typeof new Boolean(true) === 'object'; typeof new Number(1) === 'object'; typeof new String('abc') === 'object'; // 函数 typeof function() {} === 'function'; typeof class C {} === 'function' typeof Math.sin === 'function'; // Null // JavaScript 诞生以来便如此 typeof null === 'object'; // 除 Function 外的所有构造函数的类型都是 'object' var str = new String('String'); var num = new Number(100); typeof str; // 返回 'object' typeof num; // 返回 'object' var func = new Function(); typeof func; // 返回 'function' 复制代码
语法中的括号
// 括号有无将决定表达式的类型。 var iData = 99; typeof iData + ' Wisen'; // 'number Wisen' 检测的变量是iDaya typeof (iData + ' Wisen'); // 'string' 检测的变量是(iData + ' Wisen') 复制代码
正则表达式
对正则表达式字面量的类型判断在某些浏览器中不符合标准:
typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1 typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1 复制代码
为什么 typeof null === 'object';?
这里其实是一个JavaScript历史遗留问题,需要从JavaScript底层是如何存储数据的类型信息讲起。
简单来说就是
在 JS 的最初版本中使用的是 32 位系统,为了性能考虑使用低位存储变量的类型信息,000 开头代表是对象然而 null 表示为全零,所以将它错误的判断为 object
typeof原理: 不同的对象在底层都表示为二进制,在Javascript中二进制前(低)三位存储其类型信息。
000: 对象 010: 浮点数 100:字符串 110: 布尔 1: 整数
null:所有机器码均为0
Array: 1000100010001000 null: 0000000000000000 typeof [] // "object" typeof null // "object" 复制代码
二进制中的“前”一般代表低位, 比如二进制00000011对应十进制数是3,它的前三位是011。这里null、Array的二进制前三位都是000 都被视为对象标签。
很多博主写的文章都是讲到这里就浅尝而止了,后面推荐的文章讲得更深。
推荐文章
typeof也会报错
红宝书上讲过对未定义的变量使用typeof检测也会返回一个“undefined”。不会报错。
下面是详解
在 ECMAScript 2015 之前,typeof 总能保证对任何所给的操作数返回一个字符串。即便是没有声明的标识符,typeof 也能返回 'undefined'。使用 typeof 永远不会抛出错误。
但在加入了块级作用域的 let 和 const 之后,在其被声明之前对块中的 let 和 const 变量使用 typeof 会抛出一个 ReferenceError。块作用域变量在块的头部处于“暂存死区”,直至其被初始化,在这期间,访问变量将会引发错误。
红宝书p26对暂时性死区的概述
暂时性死区:在解析代码时,JavaScript 引擎也会注意出现在块后面的 let 声明,只不过在此之前不能以任何方式来引用未声明的变量。在 let 声明之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用任何后面才声明的变量都会抛出 ReferenceError。
简单来说就是在使用let、const声明变量之前访问这个变量就会报错。
如果检查不存在的变量会抛出ReferenceError,请使用typeof nonExistentVar === 'undefined'。
typeof undeclaredVariable === 'undefined'; // 不会抛出错误 typeof newLetVariable; // ReferenceError typeof newConstVariable; // ReferenceError typeof newClass; // ReferenceError let newLetVariable; const newConstVariable = 'hello'; class newClass{}; 复制代码
document.all类型是Undefined
对于document.all该特性已经从 Web 标准中删除,虽然一些浏览器目前仍然支持它,但也许会在未来的某个时间停止支持,请尽量不要使用该特性。
当前所有的浏览器都暴露了一个类型为 undefined 的非标准宿主对象 document.all
typeof document.all === 'undefined'; 复制代码
尽管规范允许为非标准的外来对象自定义类型标签,但它要求这些类型标签与已有的不同。document.all 的类型标签为 'undefined' 的例子在 Web 领域中被归类为对原 ECMA JavaScript 标准的“故意侵犯”。
typeof为什么要区分object和function?
《JavaScript高级程序设计》:从技术角度讲,函数在ECMAScript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过typeof操作符来区分函数和其他对象是有必要的。
typeof wrapper 检测类型封装函数
function type(obj, fullClass) { // get toPrototypeString() of obj (handles all types) // Early JS environments return '[object Object]' for null, so it's best to directly check for it. // 早期JS环境 检测 null 返回'[object object]',所以最好直接检查它。 if (fullClass) { return (obj === null) ? '[object Null]' : Object.prototype.toString.call(obj); // 输出[object,xxxx] } if (obj == null) { return (obj + '').toLowerCase(); } // implicit toString() conversion 隐式toString()转换 var deepType = Object.prototype.toString.call(obj).slice(8,-1).toLowerCase(); if (deepType === 'generatorfunction') { return 'function' } // Prevent overspecificity (for example, [object HTMLDivElement], etc). // 防止过度特异性(例如,[object HTMLDivElement]等)。 // Account for functionish Regexp (Android <=2.3), functionish <object> element (Chrome <=57, Firefox <=52), etc. // 解释功能化Regexp (Android <=2.3),功能化<对象>元素(Chrome <=57, Firefox <=52),等等。 // String.prototype.match is universally supported. // 普遍支持String.prototype.match。 return deepType.match(/^(array|bigint|date|error|function|generator|regexp|symbol)$/) ? deepType : (typeof obj === 'object' || typeof obj === 'function') ? 'object' : typeof obj; } let user = { [Symbol.toStringTag]: "User" }; console.log(type(user,false)); // object 有效的防止了过度特异性 console.log(type(user,true)); // [object User]