# 1-1.分类
JS
中的数据类型分为简单数据类型与复杂数据类型。
- 简单数据类型,也可以看做是值类型,以栈形式进行存储。
- 复杂数据类型,也可以看做是引用类型,以堆形式进行存储。
关于所有类型项,我创建了一个比较特殊(如果冒犯,深感抱歉)的口诀来记忆:nssb uno?
。
他们分别是:
number
数字string
字符串symbol
唯一值,Es6
新增boolean
布尔值undefined
未定义null
空值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'
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
的缺点:
typeof
不能区分null
与object
。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
2
3
4
5
6
7
② 虽然 symbol
有对应的函数 Symbol
,但是 Symbol
并不能作为构造函数使用。
即创建 symbol
并不能使用 new Symbol()
而是 Symbol()
:
console.log(Symbol() instanceof Symbol) // false
③ 由于 undefined
与 null
并没有对应的内置对象,所以不能使用 instanceof
检测方法。
console.log(undefined instanceof new Undefined()) // error
console.log(null instanceof new Null()) // error
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
2
3
4
5
6
7
8
9
10
11
12
可以看出:
instanceof
可以用来检测目标是否是某一构造器的实例。- 只能用来检测有构造器并且是由构造器创建的变量。
# 1-4.constructor
TIP
constructor
与 instanceof
的道理是类似的,两者都是利用原型机制来进行检测。
不同之处在于:
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
2
3
4
5
6
7
8
② undefined
与 null
没有构造器。
console.log(undefined.constructor === Undefined) // error
console.log(null.constructor === Null) // error
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
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
等等。
但是每种方法的调用方式和结果有些许差别。
- 在判断数据类型方面,我们采用
Object.prototype.toString
。 - 之所以调用
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]'
2
3
4
5
6
7
8
9
10
可以看出:
无论数据类型是什么,Object.prototype.toString.call()
都能给出唯一的标识。
当之无愧的最优方式。
# 1-6.番外
# 1-6-1.检测方式汇总
- 检测
undefined
- 检测
null
- 检测
string
- 检测
number
- 检测
boolean
- 检测
object
- 检测
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
}
}
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.isArray
的 polyfill
如下:
if (!Array.isArray) {
Array.isArray = function(arg) {
return Object.prototype.toString.call(arg) === '[object Array]'
}
}
2
3
4
5
# 1-6-2.检测空对象
- 利用
for..in
function judgeObjIsEmpty (target) {
if (Object.prototype.toString.call(target) !== '[object Object]') return false
for (var name in target) {
return false
}
return true
// 缺点:不能正确检测原型上有属性的空对象
}
2
3
4
5
6
7
8
- 利用
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 利用
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
}
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] }';
}
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
2
3
4
5
6
7
继承 →