先扣题~JS检查数据类型的方法如下:
typeof xxx
xxx instanceof 对象子类型
(xxx).constructor === 对象子类型
Object.prototype.toString.call(xxx)
具体猫腻见下文:
1.typeof
JS简单基本类型(语言类型)/文字形式 |JS复杂基本类型/构造形式
- number | - Number
- string | - String
- boolean | - Boolean
- symbol | - Array
- null | - Date
- undefined | - RegExp
- object | - Object
| - Error
| - 等等,详见MDN
复杂基本类型是对象的子类型,也被叫做内置对象、内置函数(也叫构造函数,用new产生的函数调用)。和简单基本类型(官方称语言类型)要区分开。
我们用typeof方法打印出的结果,都是小写字母,也就是简单基本类型。
typeof 1 // "number"
typeof '1' // "string"
typeof true // "boolean"
typeof Symbol() // "symbol"
typeof null // "object"
typeof undefined // "undefined"
typeof fn(){} // "function"
typeof {} // "object"
以上有3处值得注意!
- "symbol"类型的值,是通过Symbol()这种类似于构造函数的方法创建出来的,但是其不需要new,因为Symbol作为构造函数来说它并不完整,故它不支持语法:“new Symbol()”。
- null 的type不是"null",而是"object"。原理是这样的, 不同的对象在底层都表示为二进制, 在 JavaScript 中二进制前三位都为 0 的话会被判断为 object 类型, null 的二进制表示是全 0, 自然前三位也是 0, 所以执行 typeof 时会返回“object”。实际上是JS设计时的bug。
- 函数的类型是"function",不是"object"。
2. instanceof
借用《你不知道的事JS(上)》中的例子来 instanceof 和 typeof 的区别:
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
strPrimitive instanceof String; // false
var strObject = new String( "I am a string" );
typeof strObject; // "object"
strObject instanceof String; // true
// 检查 sub-type 对象
Object.prototype.toString.call( strObject ); // [object String]
下面这段话建议左右对照上文中的简单基本类型、复杂基本类型(看看对面是否有相同的单词,如number和Number)来理解。(引文里的构造形式,对应大写字母开头的对象子类型;文字形式,对应字面量,类似于var a = 1
的表达方式。)
原始值 “I am a string” 并不是一个对象, 它只是一个字面量,并且是一个不可变的值。如果要在这个字面量上执行一些操作, 比如获取长度、访问其中某个字符等,则需要将其转换为 String 对象。
之所以可以在字面量上直接访问属性或者方法,是因为引擎自动把字面量转换成相应的对象。对于number字面量上,boolean字面量来说也是如此。
null 和undefined 没有对应的构造形式, 它们只有文字形式。 相反, Date 只有构造, 没有文字形式。
对于 Object、 Array、 Function 和 RegExp来说, 无论使用文字形式还是构造形式, 它们都是对象, 不是字面量。 在某些情况下, 相比用文字形式创建对象, 构造形式可以提供一些额外选项。 由于这两种形式都可以创建对象, 所以我们首选更简单的文字形式。 建议只在需要那些额外选项时使用构造形式。
Error 对象很少在代码中显式创建, 一般是在抛出异常时被自动创建。 也可以使用 new Error(…) 这种构造形式来创建,不过一般来说用不着。
总结:
- 字面量方式创建的"string",“number”,"boolean"数据使用 instanceof 会返回false
- 引用数据类型(“object”)无论是字面量方式创建还是构造方式创建,使用 instanceof 只要后面对象子类型没跟错,就会返回true
- Object是一切复杂基础类型的爸爸
注:是不是和浅拷贝、深拷贝对上了号了!
3. constructor
constructor貌似能弥补instanceof中的一些不便,还是上面的例子
var strPrimitive = "I am a string";
typeof strPrimitive; // "string"
strPrimitive instanceof String; // false
strPrimitive.constructor === String; // true
正常情况下,constructor还是能正常显示类型的,但如果你测试的是一个对象(引用数据类型),那只要你把构造函数的prototype指向改一下,实例化对象的constructor立马翻脸不认人,详见实例、构造函数、函数对象的关系,是的!构造函数的prototype才是实例construtor的金主爸爸,随时可以换的~
4.Object.prototype.toString
此方法如果在自定义对象中未被覆盖,则返回 “[object type]”,其中 type 是对象的类型。这个类型指的很暧昧,从大多数结果类型名(有Date、Function等)以及打印形式(首字母大写)来看,这个类型是指复杂数据类型,但下面两个例子就很迷,不过不影响类型检测就是了。JS真的是一门很随便的语言啊~