# 1-1.分类

JS 中的数据类型分为简单数据类型复杂数据类型

  • 简单数据类型,也可以看做是值类型,以栈形式进行存储。
  • 复杂数据类型,也可以看做是引用类型,以堆形式进行存储。

关于所有类型项,我创建了一个比较特殊(如果冒犯,深感抱歉)的口诀来记忆:nssb uno?

他们分别是:

  1. number 数字
  2. string 字符串
  3. symbol 唯一值,Es6 新增
  4. boolean 布尔值
  5. undefined 未定义
  6. null 空值
  7. object 对象

其中,前6项都是基本数据类型

object复杂数据类型,它是一种比较宽泛的定义,它其实包含 object array function date regexp等等。

下面来记录下数据类型的区分方法:

# 1-2.typeof

console.log(typeof 1019)            // 'number'
console.log(typeof '1019')          // 'string'
console.log(typeof Symbol())        // 'symbol'
console.log(typeof false)           // 'boolean'
console.log(typeof undefined)       // 'undefind'
console.log(typeof null)            // 'object'
console.log(typeof new Object())    // 'object'
console.log(typeof new Array())     // 'object'
console.log(typeof new Function())  // 'function'
console.log(typeof new Date())      // 'object'
1
2
3
4
5
6
7
8
9
10

可以看出:

  • typeof 对于处理基本数据类型(number string symbol boolean undefined)有明显效果,要注意 null 的检测是 object,而不是 null
  • 处理复杂数据类型(object array date)的话,检测结果统一都是 object。而对于 function 的检测结果,则是 function

由此我们总结下 typeof 的缺点:

  1. typeof 不能区分 nullobject
  2. typeof 不能区分 Object Array Date Regexp等标准内置对象。

# 1-3.instanceof

a instanceof A,该语法用来检测 a 是否是 A 的实例。

a 是否是通过 new A() 来创建的。

① 利用字面量方式创建的值类型变量 可以利用 instanceof 来检测 但检测结果将是 false

因为这些字面量并不是利用 new 运算符生成的实例:

console.log(1019 instanceof Number)   // false   
console.log('1019' instanceof String) // false
console.log(false instanceof Boolean) // false

console.log(new Number(1019) instanceof Number)     // true   
console.log(new String('1019') instanceof String)   // true
console.log(new Boolean(false) instanceof Boolean)  // true
1
2
3
4
5
6
7

② 虽然 symbol 有对应的函数 Symbol,但是 Symbol 并不能作为构造函数使用。

即创建 symbol 并不能使用 new Symbol() 而是 Symbol()

console.log(Symbol() instanceof Symbol) // false
1

③ 由于 undefinednull 并没有对应的内置对象,所以不能使用 instanceof 检测方法。

console.log(undefined instanceof new Undefined())   // error
console.log(null instanceof new Null())             // error
1
2

instanceof 更多的是用来检测复杂数据类型

console.log(new Object() instanceof Object)       // true
console.log(new Array() instanceof Array)         // true
console.log(new Function() instanceof Function)   // true
console.log(new Date() instanceof Date)           // true

// 另外部分的复杂数据类型即使是用字面量的方式创建 也可以检测
var obj = {}
var arr = []
var fn = function () {}
console.log(obj instanceof Object)    // true
console.log(arr instanceof Array)     // true
console.log(fn instanceof Function)   // true
1
2
3
4
5
6
7
8
9
10
11
12

可以看出:

  • instanceof 可以用来检测目标是否是某一构造器的实例。
  • 只能用来检测有构造器并且是由构造器创建的变量。

# 1-4.constructor

TIP

constructorinstanceof 的道理是类似的,两者都是利用原型机制来进行检测。

不同之处在于:

  • instanceof 检测的是实例。
  • constructor 检测的是构造器。

基本数据类型的字面量方式不能利用 instanceof,但能利用 constructor

var number = 1019
var string = '1019'
var symbol = Symbol()
var boolean = false
console.log(number.constructor === Number)    // true
console.log(string.constructor === String)    // true
console.log(symbol.constructor === Symbol)    // true
console.log(boolean.constructor === Boolean)  // true      
1
2
3
4
5
6
7
8

undefinednull 没有构造器。

console.log(undefined.constructor === Undefined) // error 
console.log(null.constructor === Null)           // error
1
2

复杂数据类型也能利用 constructor。(无论是利用构造器创建的还是字面量形式创建的)

console.log((new Object()).constructor === Object)      // true
console.log((new Array()).constructor === Array)        // true
console.log((new Function()).constructor === Function)  // true
console.log((new Date()).constructor === Date)          // true
1
2
3
4

可以看出,相对 instanceof 的检测,constructor 的优点有:

  • 能检测字面量形式的基本类型变量。

constructor 的检测缺点也很明显:

  • constructor 的指向可以被更改。也就是说判断不准确。譬如:
    var obj = {}
    obj.__proto__.constructor = Array
    console.log(obj.constructor === Object) // false
    console.log(obj.constructor === Array)  // true
    
    1
    2
    3
    4

# 1-5.Object.prototype.toString.call()

其实 toString 这个方法,很多原型上都挂载了,不仅仅只有 Object.prototype.toString

还有 Array.prototype.toString Number.prototype.toString String.prototype.toString等等。

但是每种方法的调用方式和结果有些许差别

  1. 在判断数据类型方面,我们采用 Object.prototype.toString
  2. 之所以调用 call 方法,是因为参数并不单单只是 object 类型。
console.log(Object.prototype.toString.call(undefined))       // '[object Undefined]'
console.log(Object.prototype.toString.call(null))            // '[object Null]'
console.log(Object.prototype.toString.call(new Object()))    // '[object Object]'
console.log(Object.prototype.toString.call(new Array()))     // '[object Array]'
console.log(Object.prototype.toString.call(new Function()))  // '[object Function]'
console.log(Object.prototype.toString.call(new Date()))      // '[object Date]'
console.log(Object.prototype.toString.call(1019))            // '[object Number]'
console.log(Object.prototype.toString.call('1019'))          // '[object String]'
console.log(Object.prototype.toString.call(false))           // '[object Boolean]'
console.log(Object.prototype.toString.call(Symbol()))        // '[object Symbol]'
1
2
3
4
5
6
7
8
9
10

可以看出:

无论数据类型是什么,Object.prototype.toString.call() 都能给出唯一的标识。

当之无愧的最优方式。

# 1-6.番外

# 1-6-1.检测方式汇总

  1. 检测 undefined
  2. 检测 null
  3. 检测 string
  4. 检测 number
  5. 检测 boolean
  6. 检测 object
  7. 检测 array
function getDataType (target) {
  if (typeof target === 'undefined') {
    console.log('target 是 undefined')
    return
  }
  if (target === null) {
    console.log('target 是 null')
    return
  }
  if (typeof target === 'string') {
    console.log('target 是 string')
    return
  }
  if (typeof target === 'number') {
    console.log('target 是 number')
    return
  }
  if (typeof target === 'boolean') {
    console.log('target 是 boolean')
    return
  }
  if (Object.prototype.toString.call(target) === '[object Object]') {
    console.log('target 是 object')
    return
  }
  // Array的静态方法 Array.isArray的原理也是利用的 Object.prototype.toString.call()
  if (Object.prototype.toString.call(target) === '[object Array]' || Array.isArray(target)) {
    console.log('target 是 array')
    return
  }
}
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

Array.isArraypolyfill 如下:

if (!Array.isArray) {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]'
  }
}
1
2
3
4
5

# 1-6-2.检测空对象

  1. 利用 for..in
function judgeObjIsEmpty (target) {
  if (Object.prototype.toString.call(target) !== '[object Object]') return false
  for (var name in target) {
    return false
  }
  return true
  // 缺点:不能正确检测原型上有属性的空对象
}
1
2
3
4
5
6
7
8
  1. 利用 JSON.stringify()
function judgeObjIsEmpty (target) {
  if (Object.prototype.toString.call(target) !== '[object Object]') return false
  // 如果target是一个空对象,即使有不能枚举的属性,经过JSON.stringify转译后,也会是'{}'
  if (JSON.stringify(target) === '{}') {
    return true
  }
  return false
}

var obj = {} 
Object.defineProperty(obj, 'id', {
  value: 19,
  enumerable: false,
  writable: true,
  configable: true
})
judgeObjIsEmpty(obj) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. 利用 Object.keys。推荐这种方式 比 JSON.stringify 更优雅一些:
function judgeObjIsEmpty (target) {
  if (Object.prototype.toString.call(target) !== '[object Object]') return false
  // Object.keys 也不会管属性描述符 enumerable 为 false 的属性
  if (Object.keys(target).length > 0) {
    return false
  }
  return true
}
1
2
3
4
5
6
7
8

# 1-6-3.Jquery中的对象检测方式

Jquery中判断数据类型是不是一个对象:

function isPlainObject ( obj ) {
  var proto, Ctor;
  // Detect obvious negatives
  // Use toString instead of jQuery.type to catch host objects
  if ( !obj || Object.prototype.toString.call( obj ) !== "[object Object]" ) {
    return false;
  }
  proto = Object.getPrototypeOf( obj );
  // Objects with no prototype (e.g., `Object.create( null )`) are plain
  if ( !proto ) {
    return true;
  }
  // Objects with prototype are plain iff they were constructed by a global Object function
  Ctor = Object.prototype.hasOwnProperty.call( proto, "constructor" ) && proto.constructor;
  return typeof Ctor === "function" && Function.prototype.toString.call( Ctor ) === 'function Object() { [native code] }';
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

它对于 proto 的判断,刨除了自定义构造器的实例对象,譬如:

function Factory() {
  this.name = 'yxp'
}

var f = new Factory()

isPlainObject(f) // false
1
2
3
4
5
6
7