var now = new Date(); console.log(now instanceof Object); // ture console.log(now instanceof Date); // ture instanceof 运算符也可以检测自定义的类型,比如: function Person(name){ this.name = name; } var me = new Person("Nicholas"); console.log(me instanceof Object); // ture console.log(me instanceof Person); // ture
这段示例代码中创建了 Person 类型。变量 me 是 Person 的实例,因此 me instanceof Person 是 true 。上文也提到,所有的对象都被认为是 Object 的实例,因此 me instanceof Object 也是 ture 。
在 JavaScript 中检测 内置类型 和 自定义类型 时,最好的做法就是使用 instanceof 运算符,这也是唯一的方法。
但有一个严重的限制,假设两个浏览器帧(frame)里都有构造函数 Person ,帧A中的 Person 实例 frameAPersonInstance 传入到帧B中,则会有如下结果:
console.log(frameAPersonInstance instanceof frameAPerson) // ture
console.log(frameAPersonInstance instanceof frameBPerson) // false
尽管两个 Person 的定义是完全一样的,但在不同帧(frame)里,他们被认为是不同类型。有两个非常重要的内置类型也有这个问题: Array 和 Function ,所以检测它们一般不使用 instanceof 。
检测函数
从技术上讲,JavaScript 中的函数是引用类型,同样存在 Function 构造函数,每个函数都是其实例,比如:
function myFunc() {} // 不好的写法 console.log(myFunc instanceof Function); // true
然而,这个方法亦不能跨帧(frame)使用,因为每个帧都有各自的 Function 构造函数,好在 typeof 运算符也是可以用于函数的,返回 "function" 。
function myFunc() {} // 好的写法 console.log(typeof myFunc === "function"); // true
检测函数最好的方法是使用 typeof ,因为他可以跨帧(frame)使用。
用 typeof 来检测函数有一个限制。在 IE 8 和更早版本的 IE 浏览器中,使用 typeof 来检测 DOM 节点中的函数都返回 "object" 而不是 "function" 。比如:
// IE8 及更早版本的IE console.log(typeof document.createElement); // "object" console.log(typeof document.getElementById); // "object" console.log(typeof document.getElementByTagName); // "object"
之所以出现这种怪异的现象是因为浏览器对 DOM 的实现有差异。简言之,这些早版本的 IE 并没有将 DOM 实现为内置的 JavaScript 方法,导致内置 typeof 运算符将这些函数识别为对象。因为 DOM 是有明确定义的,了解到对象成员如果存在则意味着它是一个方法,开发者往往通过 in 运算符来检测 DOM 的方法,比如:
// 检测 DOM 方法 if ("querySelectorAll" in document) { var images = document.querySelectorAll("img"); }
这段代码检查 querySelectorAll 是否定义在 document 中,如果是,则使用这个方法。尽管不是最理想的方法,如果想在 IE 8 及更早浏览器中检测 DOM 方法是否存在,这是最安全的做法。在其他所有的情形中, typeof 运算符是检测 JavaScript 函数的最佳选择。
检测数组
JavaScript 中最古老的跨域问题之一就是在帧(frame)之间来回传递数组。开发者很快发现 instanceof Array 在此场景中不能返回正确的结果。正如上文提到的,每个帧都有各自的 Array 构造函数,因此一个帧中的实例在另外一个帧里不会被识别。
关于如何在 JavaScript 中检测数组类型已经有狠多研究了,最终 Kangax 给出了一种优雅的解决方案:
function isArray(value) { return Object.prototype.toString.call(value) === "[object Array]"; }
Kangax 发现调用某个值的内置 toString() 方法在所有浏览器中都会返回标准的字符串结果。对于数组来说,返回的字符串为 "[object Array]" ,也不用考虑数组实例实在哪个帧(frame)中被构造出来的。这种方法在识别内置对象时往往十分有用,但对于自定义对象请不要用这种方法。
ECMAScript5 将 Array.isArray() 正式引入 JavaScript。唯一的目的就是准确地检测一个值是否为数组。同 Kangax 的函数一样, Array.isArray() 也可以检测跨帧(frame)传递的值,因此很多 JavaScript 类库目前都类似地实现了这个方法。
function isArray(value) { if (typeof Array.isArray === "function") { return Array.isArray(value); } else { return Object.prototype.toString.call(value) === "[object Array]"; } }
IE 9+、FireFox 4+、Safari 5+、Opera 10.5+、Chrome 都实现了 Array.isArray() 方法。
检测属性
另外一种用到 null (以及 undefined )的场景是当检测一个属性是否在对象中存在时,比如:
// 不好的写法:检测假值 if (object[propertyName]) { // 一些代码 } // 不好的写法:和null相比较 if (object[propertyName] != null) { // 一些代码 } // 不好的写法:和undefined相比较 if (object[propertyName] != undefined) { // 一些代码 }