JS中的数据类型判断
一、typeof()
首先看一批操作,我们可以思考下这些操作的结果会是什么:
// 1.
typeof null
typeof undefined
typeof true
typeof 1
// 2.
typeof(new String('123'))
typeof(Number('abc'))
typeof(new Number(123))
// 3.
typeof []
typeof(Array)
typeof(new Array)
// 4.
typeof function() {}
typeof(Function)
typeof(new Function)
// 5.
typeof {}
typeof Object
typeof new Object()
在浏览器中执行上面的操作后,可以得到如下结果:
// 1.
typeof null // "object"
typeof undefined // "undefined"
typeof true // "boolean"
typeof 1 // "number"
// 2.
typeof(new String('123')) // "object"
typeof(Number('abc')) // "object"
typeof(new Number(123)) // "object"
// 3.
typeof [] // "object"
typeof(Array) // "function"
typeof(new Array) // "object"
// 4.
typeof function() {} // "function"
typeof(Function) // "function"
typeof(new Function) // "function"
// 5.
typeof {} // "object"
typeof Object // "function"
typeof new Object() // "object"
typeof操作符返回一个字符串,指示未经计算的操作数的类型。有部分数据类型比较特殊:
-
null
在 JavaScript 最初的实现中,JavaScript 中的值是由一个表示类型的标签和实际数据值表示的。对象的类型标签是 0。由于null
代表的是空指针(大多数平台下值为 0x00),因此,null的类型标签也成为了 0,typeof null
就错误的返回了"object"
。 -
正则表达式
对正则表达式字面量的类型判断在某些浏览器中不符合标准:
typeof /s/ === 'function'; // Chrome 1-12 , 不符合 ECMAScript 5.1
typeof /s/ === 'object'; // Firefox 5+ , 符合 ECMAScript 5.1
- 未声明的变量
在 ECMAScript 2015 之前,typeof
总是保证为任何操作数返回一个字符串。但是,除了非提升,块作用域的let
和const
之外,在声明之前对块中的let
和const
变量使用typeof
会抛出一个ReferenceError
。而未声明的变量,使用typeof
会返回“undefined”。
typeof undeclaredVariable === 'undefined';
typeof newLetVariable; let newLetVariable; // ReferenceError
typeof newConstVariable; const newConstVariable = 'hello'; // ReferenceError
-
document.all
所有当前的浏览器都暴露了一个类型为 undefined 的非标准宿主对象document.all
typeof document.all === 'undefined';
上述typeof
的操作,我们可以发现在很大程度上,typeof
无法获取到准确的类型(譬如typeof null === "object"; typeof [] === "object";
)。因而我们介绍一种更为通用的判断类型的方式:
二、Object.prototype.toString.call()
我们将上一节中的所有操作替换为Object.prototype.toString.call
:
// 1.
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
Object.prototype.toString.call(true) // "[object Boolean]"
Object.prototype.toString.call(1) // "[object Number]"
// 2.
Object.prototype.toString.call(new String('123')) // "[object String]"
Object.prototype.toString.call(Number('abc')) // "[object Number]"
Object.prototype.toString.call(new Number(123)) // "[object Number]"
// 3.
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(Array) // "[object Function]"
Object.prototype.toString.call(new Array) // "[object Array]"
// 4.
Object.prototype.toString.call(function() {}) // "[object Function]"
Object.prototype.toString.call(Function) // "[object Function]"
Object.prototype.toString.call(new Function) // "[object Function]"
// 5.
Object.prototype.toString.call({}) // "[object Object]"
Object.prototype.toString.call(Object) // "[object Function]"
Object.prototype.toString.call(new Object()) // "[object Object]"
从以上结果我们看到,使用Object.prototype.toString.call
方法能够得到更为准确的类型。
三、ES6中Object.prototype.toString.call()的拓展
如果我们定义了一个类,然后实例化这个类,那么这个实例的类型是否应该为这个类呢?如下:
function Person(name) {
this.name = name;
}
var p = new Person('Hal');
typeof p; // "object"
Object.prototype.toString.call(p); // "[object Object]"
这个与我们想要的结果不大一样,我们希望执行Object.prototype.toString.call(p)
时能够获得实际的类型:"[object Person]"
。ES6中,提供了一种方式:
Person.prototype[Symbol.toStringTag] = 'Person';
再次执行Object.prototype.toString.call(p)
时我们能够获得想要的结果:"[object Person]"
。这是因为在该对象上面调用Object.prototype.toString方法时,如果[Symbol.toStringTag]
属性存在,它的返回值会出现在toString方法返回的字符串之中,表示对象的类型。
综上所述,我们判断数据类型时,推荐使用Object.prototype.toString.call()
这种方式。