JS数据类型及检测方法总结

JS 中有 7 种内置数据类型:

  1. Undefined(未定义)
  2. Null(空值)
  3. Number(数字)
  4. String(字符串)
  5. Boolean(布尔值)
  6. Symbol(符号) [ ES6 新增,详情见 ES6 入门 ]
  7. Object(对象)

上述 7 种类型,除了 Object 外都是基本数据类型。

检测数据类型的方法

1. typeof(返回类型的字符串)

  • typeof undefined // undefined
  • typeof true // boolean
  • typeof 42 // number
  • typeof ‘42’ // string
  • typeof { life: 42} // object
  • typeof Symbol() // symbol

以上 6 种类型均有同名的字符串与之对应,接下来我们看 null:

  • typeof null // object

之所以对 null 调用 typeof 返回 object,是因为特殊值 null 被认为是一个空的对象的引用。

还有以下几种情况:

  • typeof function a() { // } // function
  • typeof [1, 2, 3] // object
  • typeof /<\w+?>/ // function || object

这样看来的话

  • function(函数)也是 JS 的一个内置类型?它实际上是 object 的一个子类型。具体来说,函数是“可调用对象”,它内部有一个 [[Call]] 属性,可以使其被调用。所以用 typeof 区分函数和其他对象是有必要的。

  • array(数组)也是对象。确切地说,它也是 object 的一个“子类型”,数组的元素按数字顺序来进行索引(而非普通像对象那样通过字符串键值),其 length 属性是元素个数。

  • regexp(正则表达式)也是对象。准确描述,Chrome 7 及之前版本在此返回 function,而其他浏览器返回 object

JS 中,对于未赋值或者未定义的变量,使用 typeof 操作符取得的结果都是 undefined,但是实际上,技术上这两种情况完全不同。未赋值指的是变量已经声明但是未赋值,所以返回 undefined 指明未定义;未定义指的是变量未声明直接调用 typeof,所以这种情况我们期望返回 undeclared。

2. instanceof(返回布尔值)

在使用 typeof 运算符时采用引用类型存储值会出现一个问题,无论引用的是什么类型的对象,它都返回 “object”。ECMAScript 引入了另一个运算符 instanceof 来解决这个问题。instanceof 运算符与 typeof 运算符相似,用于识别正在处理的对象的类型。与 typeof 方法不同的是,instanceof 方法要求开发者明确地确认对象为某特定类型。检测一个引用类型值和 Object 构造函数时,instanceof 始终返回 true。如果使用 instanceof 检测基本类型值,则返回 false,因为基本类型不是对象。

instanceof 用法示例:

  • 示例一:

    1
    2
    var oStringObject = new String("hello world"); 
    console.log(oStringObject instanceof String); // 输出 "true"
  • 示例二:

    1
    2
    3
    4
    // 判断 foo 是否是 Foo 类的实例
    function Foo(){}
    var foo = new Foo();
    console.log(foo instanceof Foo) // true
  • 示例三:

    1
    2
    3
    4
    5
    6
    7
    8
    // 判断 foo 是否是 Foo 类的实例 , 并且是否是其父类型的实例
    function Aoo(){}
    function Foo(){}
    Foo.prototype = new Aoo(); // JavaScript 原型继承

    var foo = new Foo();
    console.log(foo instanceof Foo) // true
    console.log(foo instanceof Aoo) // true
  • 示例四:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    console.log(Object instanceof Object); //true 
    console.log(Function instanceof Function); // true
    console.log(Number instanceof Number); // false
    console.log(String instanceof String); // false

    console.log(Function instanceof Object); // true

    console.log(Foo instanceof Function); // true
    console.log(Foo instanceof Foo); // false

剖析 instanceof 运算符定义

1
2
3
4
5
6
7
8
9
10
11
function instance_of(L, R) {//L 表示左表达式,R 表示右表达式
var O = R.prototype;// 取 R 的显示原型
L = L.__proto__;// 取 L 的隐式原型
while (true) {
if (L === null)
return false;
if (O === L)// 这里重点:当 O 严格等于 L 时,返回 true
return true;
L = L.__proto__;
}
}

instanceof 复杂用法(上述的最后三个例子)

  1. Object instanceof Object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 为了方便表述,首先区分左侧表达式和右侧表达式
    ObjectL = Object, ObjectR = Object;
    // 下面根据规范逐步推演
    O = ObjectR.prototype = Object.prototype
    L = ObjectL.__proto__ = Function.prototype
    // 第一次判断
    O != L
    // 循环查找 L 是否还有 __proto__
    L = Function.prototype.__proto__ = Object.prototype
    // 第二次判断
    O == L
    // 返回 true
  2. Function instanceof Function

    1
    2
    3
    4
    5
    6
    7
    8
    // 为了方便表述,首先区分左侧表达式和右侧表达式
    FunctionL = Function, FunctionR = Function;
    // 下面根据规范逐步推演
    O = FunctionR.prototype = Function.prototype
    L = FunctionL.__proto__ = Function.prototype
    // 第一次判断
    O == L
    // 返回 true
  3. Foo instanceof Foo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 为了方便表述,首先区分左侧表达式和右侧表达式
    FooL = Foo, FooR = Foo;
    // 下面根据规范逐步推演
    O = FooR.prototype = Foo.prototype
    L = FooL.__proto__ = Function.prototype
    // 第一次判断
    O != L
    // 循环再次查找 L 是否还有 __proto__
    L = Function.prototype.__proto__ = Object.prototype
    // 第二次判断
    O != L
    // 再次循环查找 L 是否还有 __proto__
    L = Object.prototype.__proto__ = null
    // 第三次判断
    L == null
    // 返回 false

3. Object.prototype.toString

当 toString 方法被调用的时候,下面的步骤会被执行(ES5 的规范):

  1. If the this value is undefined, return “[object Undefined]” => 如果 this 值是 undefined,就返回 [object Undefined]
  2. If the this value is null, return “[object Null]” => 如果 this 值是 null,就返回 [object Null]
  3. Let O be the result of calling ToObject passing the this value as the argument => 让 O 成为 ToObject(this) 的结果
  4. Let class be the value of the [[Class]] internal property of O => 让 class 成为 O 的内部属性 [[Class]] 的值
  5. Return the String value that is the result of concatenating the three Strings “[object “, class, and “]” => 最后返回由 “[object “ 和 class 和 “]” 三个部分组成的字符串

通过规范,我们至少知道了调用 Object.prototype.toString 会返回一个由 “[object “ 和 class 和 “]” 组成的字符串,而 class 是要判断的对象的内部属性。正是因为这种特性,我们可以用 Object.prototype.toString 方法识别出更多类型。至于到底能识别多少种,emmmm,我告诉你 14 种你信吗?不信的话,那数数——

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log(Object.prototype.toString.call(1)); // [object Number]
console.log(Object.prototype.toString.call('123')); // [object String]
console.log(Object.prototype.toString.call(true)); // [object Boolean]
console.log(Object.prototype.toString.call(undefined)); // [object Undefined]
console.log(Object.prototype.toString.call(null)); // [object Null]
console.log(Object.prototype.toString.call({a: 1})); // [object Object]
console.log(Object.prototype.toString.call([1, 2, 3])); // [object Array]
console.log(Object.prototype.toString.call(new Date())); // [object Date]
console.log(Object.prototype.toString.call(new Error())); // [object Error]
console.log(Object.prototype.toString.call(/a/g)); // [object RegExp]
console.log(Object.prototype.toString.call(function a(){})); // [object Function]
console.log(Object.prototype.toString.call(Math)); // [object Math]
console.log(Object.prototype.toString.call(JSON)); // [object JSON]
console.log(Object.prototype.toString.call(arguments)); // [object Arguments]

所以我数了数至少识别 14 种,哈哈哈哈~

之前也有针对常用的数据类型,写了一个比较完备的检测函数——

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 实现一个function 可以兼容所有类型的数据判断
* 对于基本数据类型使用 typeof 检测,引用类型使用
* Object.prototype.toString.call(obj)检测
* 注意:IE6中,null 和 undefined 会被 Object.prototype.toString
* 识别为[object Object]类型
*/

const typeClass = {};

const types = "Boolean Number String Function Array Date RegExp Object Error".split(" ");

// console.log(types);
// [ 'Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp', 'Object', 'Error' ]

// 将其用 map 映射为 [object Number] 类似的形式
types.map(function(item, index) {
// console.log(item);
typeClass["[object " + item + "]"] = item.toLowerCase();
});

// console.log(typeClass);
// { '[object Boolean]': 'boolean',
// '[object Number]': 'number',
// '[object String]': 'string',
// '[object Function]': 'function',
// '[object Array]': 'array',
// '[object Date]': 'date',
// '[object RegExp]': 'regExp',
// '[object Object]': 'object',
// '[object Error]': 'error' }

function checkType(obj) {
var typeRes; // 检测结果

if(obj === null) {
typeRes = obj + '';
} else {
// console.log(Object.prototype.toString.call(obj));
typeRes = typeof obj === 'object' || typeof obj === 'function' ?
typeClass[Object.prototype.toString.call(obj)] || "object" :
typeof obj;
}

console.log(typeRes);
return typeRes;
}