对象类型判断

类型判断的方法 - Object.prototype.toString是最优解

Array.isArray()

ES5,低版本浏览器不支持。只有Array有这个方法。

typeof

无法判断出array与null,两个返回结果都是object。

instanceof

问题1:

1
2
3
4
var num = 1
num instanceof Number // false
var num = new Number()
num instanceof Number // true

如果是直接创建的原始类型,不instanceof其构造函数。当然,array、object等引用类型不受影响。

问题2:

1
2
3
4
5
6
7
8
9
10
11
// index.html

<iframe src="a.html"></iframe>

<script>
window.onload = function () {
var a = window.frames[0].a
console.log(a instanceof Array) // false
console.log(a.constructor === Array) // false
}
</script>
1
2
3
4
5
a.html

<script>
window.a = [1, 2, 3]
</script>

跨iframe对象无法正常判断。这是因为跨iframe实例化的对象彼此不共享原型链。如果是a instanceof window.frames[0].Arraya.constructor === window.frames[0].Array,那么结果就是true了。

constructor

与instanceof的问题2相同。

[正确]Object.prototype.toString()

1
2
3
4
5
6
7
8
9
10
Object.prototype.toString.call(10) // [object Number]
Object.prototype.toString.call('Hello') // [object String]
Object.prototype.toString.call(true) // [object Boolean]
Object.prototype.toString.call([]) // [object Array]
Object.prototype.toString.call({}) // [object Object]
Object.prototype.toString.call(function () {}) // [object Function]
Object.prototype.toString.call(/a/g) // [object RegExp]
Object.prototype.toString.call(null) // [object Null]
Object.prototype.toString.call(undefined) // [object Undefined]
Object.prototype.toString.call(new Date()) // [object Date]

Object.prototype.toString.call()为什么能返回这样类型的字符串?

ECMA-262:

Object.prototype.toString.call() When toString method is called, the following steps are taken:

  1. Get the [[Class]] prototype of the object.
  2. Computed a string value by concatenating the three things “[object”, Result (1), and “]”.
  3. Return result (2).

即取得一个对象的内部属性[[Class]],然后依据这个属性返回一个类似“[object String]”的字符串作为结果([[]]用来表示语言内部用到的、外部不可直接访问的属性,称为“内部属性”)。利用这个方法,再配合call,我们可以取到任何对象的内部属性[[Class]]。

ECMA标准中对Array的描述:
new Array([ item 0[, item 1, [,…]] ])
The [[Class]] property of the newly constructed object is set to “Array”.

原文与参考

常用类型的判断

object

1
2
3
4
_.isObject = function (obj) {
var type = typeof obj
return typeof obj === 'function' || typeof obj === 'object' && !!obj
}

underscore将function和object都算作对象,!!obj为了除去null。
这里需要注意顺序,先判断function,再判断Object。否则,在obj为null的情况下,typeof obj !== ‘function’,判断为false直接返回,不再继续执行,因此执行不到!!obj步骤。

可以用Object.prototype.toString()直接判断的类型

1
2
3
4
5
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function (name) {
_['is' + name] = function (obj) {
return Object.prototype.toString.call(obj) === '[object ' + name + ']'
}
})

arguments

IE < 9下对arguments调用Object.prototype.toString()返回结果为[object Object],而非[object Arguments],我们可以用该元素是否含有callee属性来判断,arguments.callee能返回当前arguments所在的函数。

1
2
3
4
5
if (!_.isArguments(arguments)) {
_.isArguments = function (obj) {
return _.has(obj, 'callee') // 但我觉得这种情况会有bug,{ callee: true }也会被判断为arguments
}
}

DOM元素

不为空且nodeType属性为1。

1
2
3
_.isElement = function (obj) {
return !!(obj && obj.nodeType === 1)
}

NaN

Object.prototype.isString.call(NaN)返回[object Number]。

1
2
3
_.isNaN = function (obj) {
return _.isNumber(obj) && isNaN(obj)
}

原生isNaN会做一个强制类型转换,先将非Number类型的值转换toNumber,然后看返回值是不是NaN。而underscore只希望判断数值的NaN,因此加上_.isNumber判断。