- JavaScript重难点实例精讲
- 周雄
- 2345字
- 2021-04-03 20:56:08
1.4.2 typeof运算符
typeof运算符用于返回操作数的数据类型,有以下两种使用形式。
typeof operand typeof (operand)
其中operand表示需要返回数据类型的操作数,可以是引用类型,也可以是基本数据类型。
括号有的时候是必须的,如果不加上括号将会因为优先级的问题得不到我们想要的结果。
typeof运算符在处理不同数据类型时会得到不同的结果,图1-2总结出了可能的返回值。

图1-2
针对图1-2中不同的数据类型,下面总结了一些使用场景。
1. 处理Undefined类型的值
虽然Undefined类型的值只有一个undefined,但是typeof运算符在处理以下3种值时都会返回“undefined”。
· undefined本身。
· 未声明的变量。
· 已声明未初始化的变量。
var declaredButUndefinedVariable; typeof undefined === 'undefined'; // true typeof declaredButUndefinedVariable === 'undefined'; // true,已声明未初始化的变量 typeof undeclaredVariable === 'undefined'; // true,未声明的变量
2. 处理Boolean类型的值
Boolean类型的值只有两个,分别是true和false。typeof运算符在处理这两个值以及它们的包装类型时都会返回“boolean”,但是不推荐使用包装类型的写法。
typeof true === 'boolean'; // true typeof false === 'boolean'; // true typeof Boolean(true) === 'boolean'; // true,不推荐这么写
3. 处理Number类型的值
对于Number类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回“number”。
· 数字,如1、123、145。
· Number类型的静态变量,如Number.MAX_VALUE、Number.EPSILON等。
· Math对象的静态变量值,如Math.PI、Math.LN2(以e为底,2的对数)。
· NaN,虽然NaN是Not a Number的缩写,但它是Number类型的值。
· Infinity和-Infinity,表示的是无穷大和无穷小的数。
· 数值类型的包装类型,如Number(1)、Number(123),虽然它们也会返回“number”,但是并不推荐这么写。
通过上述的总结,我们可以快速完成以下这些测试。
typeof 37 === 'number'; // true typeof 3.14 === 'number'; // true typeof Math.LN2 === 'number'; // true typeof Infinity === 'number'; // true typeof NaN === 'number'; // true typeof Number(1) === 'number'; // true,不推荐这么写
4. 处理String类型的值
对于String类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回“string”。
· 任何类型的字符串,包括空字符串和非空字符串。
· 返回值为字符串类型的表达式。
· 字符串类型的包装类型,例如String('hello')、String('hello' + 'world'),虽然它们也会返回“String”,但是并不推荐这么写。
通过上述的总结,我们可以快速完成以下这些测试。
typeof "" === 'string'; // true typeof "bla" === 'string'; // true typeof (typeof 1) === 'string'; // true,因为typeof会返回一个字符串 typeof String("abc") === 'string'; // true,不推荐这么写
5. 处理Symbol类型的值
Symbol类型是在ES6中新增的原生数据类型,表示一个独一无二的值,typeof运算符处理后得到的返回值为“symbol”。
typeof Symbol() === 'symbol'; // true typeof Symbol('foo') === 'symbol'; // true
6. 处理Function类型的值
对于Function类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回“function”。
· 函数的定义,包括函数声明或者函数表达式两种形式。
· 使用class关键字定义的类,class是在ES6中新增的关键字,它不是一个全新的概念,原理依旧是原型继承,本质上仍然是一个Function。
· 某些内置对象的特定函数,例如Math.sin()函数、Number.isNaN()函数等。
· Function类型对象的实例,一般通过new关键字得到。
通过上述的总结,我们可以快速完成以下这些测试。
var foo = function () {}; function foo2() {} typeof foo === 'function'; // true,函数表达式 typeof foo2 === 'function'; // true,函数声明 typeof class C{} === 'function'; // true typeof Math.sin === 'function'; // true typeof new Function() === 'function'; // true,new操作符得到Function类型的实例
7. 处理Object类型的值
对于Object类型的数据,可以概括为以下这些值,typeof运算符在处理时会返回“object”。
· 对象字面量形式,例如{name: 'kingx'}。
· 数组,例如[1, 2, 3]和Array(1, 2, 3)。
· 所有构造函数通过new操作符实例化后得到的对象,例如new Date()、new function(){},但是new Function(){}除外。
· 通过new操作符得到的基本数据类型的包装类型对象,如new Boolean(true)、newNumber(1),但不推荐这么写。
细心的读者可能发现了,与基本数据类型的包装类型相关的部分,我们都有写“不推荐这么写”,这是为什么呢?
因为涉及包装类型时,使用了new操作符与没有使用new操作符得到的值在通过typeof运算符处理后得到的结果是不一样的,很容易让人混淆。
通过上述的总结,我们可以快速完成以下这些测试。
typeof {a:1} === 'object'; // true,对象字面量 typeof [1, 2, 4] === 'object'; // true,数组 typeof new Date() === 'object'; // true,Date对象的实例 // 下面的代码容易令人迷惑,不要使用! typeof new Boolean(true) === 'object'; // true typeof new Number(1) === 'object'; // true typeof new String("abc") === 'object'; // true
typeof运算符的使用在绝大部分情况下都是安全的,但是在ES6以后情况就不一样了。这里总结了使用typeof运算符时需要考虑的问题。
1. typeof运算符区分对待Object类型和Function类型
在Nicholas C.Zakas所著的《JavaScript高级程序设计》一书中讲到,从技术角度讲,函数在ECMAScript中是对象,不是一种数据类型。然而,函数也确实有一些特殊的属性,因此通过typeof运算符来区分函数和其他对象是有必要的。
另外,在实际使用过程中,有必要区分Object类型和Function类型,而typeof运算符就能帮我们实现。
2. typeof运算符对null的处理
使用typeof运算符对null进行处理,返回的是“object”,这是一个让大家都感到惊讶的结果。因为null是一个原生类型的数据,为什么typeof运算符会返回“object”呢?
这是一个在JavaScript设计之初就存在的问题,这里简单介绍下。
在JavaScript中,每种数据类型都会使用3bit表示。
· 000表示Object类型的数据。
· 001表示Int类型的数据。
· 010表示Double类型的数据。
· 100表示String类型的数据。
· 110表示Boolean类型的数据。
由于null代表的是空指针,大多数平台中值为0x00,因此null的类型标签就成了0,所以使用typeof运算符时会判断为object类型,返回“object”。
虽然在后面的提案中有提出修复方案,但是因为影响面太大,所以并没有被采纳,从而导致这个问题一直存在。
3. typeof运算符相关语法的括号
在前文中有讲到,括号有时是必须存在的,如果不加上括号则会因为优先级的问题得不到我们想要的结果。
我们可以通过以下代码看看加不加括号在结果上的差异。
var number = 123; typeof (number + ' hello'); // "string" typeof number + ' hello'; // "number hello"
因为typeof运算符的优先级会高于字符串拼接运算符(+),但是优先级低于小括号(),所以在未使用括号时,会优先处理typeof number,返回的是"number",然后与"hello"字符串进行拼接,得到结果"number hello"。
下面是更能体现括号重要性的例子。
typeof 1 / 0; // "NaN" typeof (1 / 0); // "number"
第一行代码中,因为没有小括号,实际会先运行typeof 1,返回的是"number",然后除以0,一个字符串除以0,得到的是"NaN"。
第二行代码中,因为使用了小括号,实际会先运行1/0,得到的是Infinity,而Infinity实际上为Number类型的值,通过typeof运算符处理后,得到的是"number"。
因此在处理某些表达式时,需要将这些表达式用括号括起来以保证先运算表达式,再使用typeof运算符进行运算。