JavaScript对类型错误出奇宽容
在JavaScript中有一些极少数的情况,提供错误的类型会产生一个及时错误;但是在大多数情况下,JavaScript不会抛出错误,而是按照多种多样的自动转换协议将值强制转换为期望的类型。
2 + 3; // 5
"hello" + "world"; // "hello world"
"2" + 3; // "23"
2 + "3"; // "23"
1 + 2 + "3"; // "33" === (1 + 2) + "3"; // "33"
1 + "2" + 3; // "123" === (1 + "2") + 3; // "123"
位运算符不仅会将操作数转换为数字,而且还会将操作数转换为32位整数。
"17" * 3; // 51
"8" | "1"; // 9
强制转换也会隐藏错误。结果为null的变量在算术运算中不会导致失败,而是被隐式地转换为0;一个未定义的变量将被转换为特殊的浮点数值NaN(not a number)。
NaN
- NaN不等于其本身,测试一个值是否等于NaN根本行不通。
var x = NaN;
x === NaN; // false
- 标准的库函数isNaN只有在你已经知道一个值是数字时才能检测它是否是NaN。
isNaN(NaN); // true
isNaN("foo"); // true
isNaN(undefined); // true
isNaN({}); // true
isNaN({ valueOf: "foo" }); // true
- NaN是JavaScript中唯一一个不等于其本身的值,可以随时通过检查一个值是否等于其自身的方式来测试该值是否是NaN。
var a = NaN;
a !== a; // true
var b = "foo";
b !== b; // false
var c = undefined;
c !== c; // false
var d = {};
d !== d; // false
var e ={ valueOf: "foo" };
e !== e; // false
//抽象函数工具
function isReallyNaN(x) {
return x !== x;
}
对象也可以被强制转换为原始值。
- 通过toString方法转换为字符串。
"J" + { toString: function() { return "S"; } }; // "JS"
- 通过valueOf方法转换为数字。
2 * { valueOf: function() { return 3; } }; // 6
当一个对象同时包含toString
和valueOf
方法时,运算符+应该调用哪个方法并不明显——做字符串连接还是加法应该根据参数的类型,但是存在隐式的强制转换,因此类型并不是显而易见,JavaScript通过盲目地选择valueOf
方法而不是toString
方法来解决这种含糊的情况。
var obj = {
toString: function() {
return "[object MyObject]";
},
valueOf: function() {
return 17;
}
};
"object: " + obj; // "object: 17"
// valueOf方法才真正是为那些代表数值的对象而设计的,对于这些对象,toString和valueOf方法应该返回一致的结果,最好避免使用valueOf方法,除非对象的确是一个数字的抽象。
真值运算
if
、||
、&&
等运算符逻辑上需要布尔值作为操作参数,但实际上可以接受任何值,JavaScript按照简单的隐式强制转换规则将值解释为布尔值,JavaScript中有7个假值:false
、0
、-0
、NaN
、null
、""
、undefined
,除此以外所有的值都为真值。使用真值运算检查函数参数或者对象属性是否已定义不是绝对安全的。
function point(x, y){
if (!x){
x = 320;
}
if (!y){
y = 240;
}
return { x: x, y: y };
}
point(0, 0); // { x: 320, y: 240 }
检查参数是否为undefined
更为严格的方法是使用typeof
。
function point(x, y) {
if (typeof x === "undefined") {
x = 320;
}
if (typeof y === "undefined") {
y = 240;
}
return { x: x, y: y };
}
// 此版本的point函数可以正确地识别0和undefined。
point(); // { x: 320, y: 240 }
point(0, 0); // { x: 0, y: 0 }
// 另一种方式是与undefined进行比较。
if (x === undefined) { ... }
提示
- 类型错误可能被隐式的强制转换所隐藏。
- 重载的运算符
+
是进行加法运算还是字符串连接操作取决于其参数类型。 - 对象通过
valueOf
方法强制转换为数字,通过toString方法强制转换为字符串。 - 具有
valueOf
方法的对象应该实现toString
方法,返回一个valueOf
方法产生的数字的字符串表示。 - 测试一个值是否为未定义的值,应该使用
typeof
或者与undefined
进行比较而不是使用真值运算。