在Javascript中,常常看到这种代码:变量和null的比较,如下:
var Controller = {
process: function(items) {
if (items !== null) { // bad
items.sort();
item.forEach(function(item) {
// 执行代码逻辑
});
}
}
};
这段代码中,process() 方法显然是希望items是一个数组,如果参数不是一个数组,则停止接下来的操作。
问题在于,仅仅和null比较并不能提供足够的信息来判断变量是否为一个数组。好在可以Javascript提供了多种方法检测变量的真实值。
检测原始值
Javascript的数据类型有:字符串、数字、布尔值、Null、Undefined、对象、Symbol(ES6新增数据类型);
Javascript有6中原始数据类型:字符串、数字、布尔值、Null、Undefined、Symbol。
typeof 运算符会返回一个表示值得类型的字符串(typeof是一个运算符不是函数)
- 对于字符串,typeof返回“string”
- 对于数字,typeof返回“number”
- 对于布尔值,typeof返回“boolean”
- 对于undefined,typeof 返回“undefined”
看下面一些例子:
// 检测字符串
if (typeof name === "string") {
anotherName = name.substring(3);
}
// 检测数字
if (typeof count === "number") {
updateCount(count);
}
// 检测布尔值
if (typeof found === "boolean" && found) {
message("Found!");
}
// 检测undefined
if (typeof MyApp === "undefined") {
MyApp = {
// 其他代码
};
}
typeof运算符的独特之处是,将其用于一个未声明的变量也不会报错。未定义的变量和值为undefined的变量通过typeof都将返回”undefined”。
当然如果你所期望的值是null,则可以直接和null进行比较。这时应当使用===或者!==来和null进行比较。比如:
// 如果你需要检测null,则使用这种方法
var element = document.getElementById('my-div');
if (element !== null) {
element.className = "found";
}
运行typeof null则返回“object”,这是一种低效的判断null的方法。如果你需要检测null,则直接使用恒等运算符(===)或非恒等运算符(!==)。
检测引用值
引用值也称作对象(object)。
在Javascript中除了原始值之外的都是引用值。有这几种内置引用类型:Object、Array、Date和Error等。typeof运算符在判断这些引用类型的时则显得力不从心,因为所有对象都会返回“object”。
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof new RegExp()); // object
console.log(typeof new Date()); // object
注:
console.log(typeof new Function()); // function (ECMA-262条款中实现了,称为函数对象)
typeof另外一种不推荐的用法就是当检测null的类型时,typeof运算符用于null时将返回“object”。
console.log(typeof null); // object
这很怪异(null明明是原始类型值),被认为是标准规范的bug,因此在编程时要杜绝使用typeof来检测null的类型。检测某个引用值的最好方法是使用instanceof运算符。如下例子:
// 检测日期
if (value instanceof Date) {
console.log(value.getFullYear());
}
// 检测正则
if (value instanceof RegExp) {
// 代码
}
// 检测Error
if (value instanceof Error) {
throw value;
}
instanceof 的一个特性是它不仅检测构造这个对象的构造器,而且检测原形链。原型链包含了很多信息,包括定义对象所采用的继承模式。比如:默认情况下,每个对象都继承Object,因此每个对象value instanceof Object 都会返回true。
var now = new Date();
console.log(now instanceof Object); // true
console.log(now instanceof Date); // true
instanceof运算符也可以检测自定义的类型,如下:
function Person(name) {
this.name = name;
}
var me = new Person("Mangoyi");
console.log(me instanceof Person); // true
console.log(me instanceof Object); // true
代码中创建了Person类型,变量me是Person的实例,因此me instanceof Person是true。
当然所有对象都被认为是Object的实例,因此me instanceof Object也是true。
函数和数组这两个类型来说,一般也用不着使用instanceof。
检测函数
从技术上讲,Javascript中的函数都是引用类型,同样存在Function构造函数,每个函数都是其实例。
function myFunc() {}
// bad
console.log(myFunc instanceof Function); // true
然而这个方法不能跨帧(frame)使用,因为每个帧都有各自的Function构造函数。好在typeof运算符可以用于函数,返回“function”。
function myFunc() {}
// good
console.log(typeof myFunc === "function"); // true
检测函数最好的方法是使用typeof,因为它可以跨帧使用。
检测数组
在ECMAScript5中将Array.isArray() 正式引入到Javascript。唯一的目的就是准确的检测一个值是否为数组。底层的实现如下:
function isArray(value) {
if (typeof Array.isArray === "function") {
return Array.isArray(value);
} else {
return Object.prototype.toString.call(value) === "[object Array]";
}
}
调用内置的toString() 方法在所有浏览器中都会返回标准的字符串结果。对于数组来说,返回的字符串是“[object Array]”。
检测属性
判断属性是否存在最好的方法就是使用in运算符。
in运算符仅仅会简单地判断属性是否存在,而不会去读属性的值。
如果实例对象的属性存在、或者继承自对象的原型,in运算符都会返回true。如下:
var object = {
count: 0,
related: null
};
// good
if ("count" in object) {
// 这里的代码会执行
}
// bad(检测假值)
if (object["count"]) {
// 这里的代码不会执行
}
// good
if ("related" in object) {
// 这里的代码会执行
}
// bad(检测是否为null)
if (object["related"] !== null) {
// 这里的代码不会执行
}
如果你想检查 实例对象 的某个属性是否存在,则使用hasOwnProperty() 方法。
所有继承自Object的JavaScript对象都有这个方法,如果实例中存在这个属性则返回true(如果这个属性只存在原型里而实例中不存在,则返回false)。
if (object.hasOwnProperty("related")) {
// 代码
}
文章内容从《编写可维护的Javascript》[美] Nicholas C. Zakas著 一书中总结写出。