如何编写高效的js代码(2)
多使用基础类型,少使用包装类
js有5中基础类型:null,undefined,string,number,bool(尽管typeof null"object"),js提供了这几种基础类型的包装类,例如你可以构造一个字符串
var s = new String("hello");
s + " world";// "hello world"
s[4];// "o"
但是不同于基础类型,包装类是object对象
typeof "hello";// "string"
typeof s; // "object"
因此任何用包装类构造函数生成的对象都是不同的对象
var s1 = new String("hello");
var s2 = new String("hello");
s1 === s2; // false
包装类存在的理由是,你可以在基础类型上使用包装类的方法
"hello".toUpperCase();// "HELLO"
实际上是因为这里产生了一个隐式的包装,当基础类型只要使用包装的方法时会隐式的产生一个包装,因此会产生一个不容易理解的后果
"hello".someProperty = 17;
"hello".someProperty;// undefined
隐式的包装在基础类型每次调用包装类的方法或者设置和读取属性时发生,而且是每次动作都会发生
因此上面的hello的每次读取和设置属性都会产生不一样的包装类实例
避免在混合类型上使用运算符
"1.0e0"== { valueOf: function() { returntrue; } };
上述运算符的操作数看上去好像毫不相关,但是由于隐式转换,两边的操作数在比较之前会尝试转换为数值类型,所以左边的1.0e0会被转为1,而右边的object对象会尝试调用valueOf方法转为数值,因此true也会被转为1
当你要将dom表单中的一个输入项和一个数值比较时,你可能会这么写
var today = newDate();
if(form.month.value == (today.getMonth() + 1) &&
form.day.value == today.getDate()) {
// happy birthday!
// ...
}
但其实通过Number函数或者一元的+运算符来把某个值转为数值类型
var today = newDate();
if(+form.month.value == (today.getMonth() + 1) &&
+form.day.value == today.getDate()) {
// happy birthday!
// ...
}
这样写还有个好处时,读你代码的人知道你的意图是要比较2个数值类型,而不用去记忆隐式转换规则
更佳的方法是用严格相等运算符来比较,同时也告诉读你代码的人,不需要经过隐式类型转换
var today = newDate();
if(+form.month.value === (today.getMonth() + 1) && // strict
+form.day.value === today.getDate()) { // strict
// happy birthday!
// ...
}
操作数1 | 操作数2 | 强制规则 |
---|---|---|
null | undefined | 始终为true |
null 或者 undefined | 除null和undefined外的任意值 | 始终为false |
基础类型string,number或者bool | date类型 | 基础类型 => number,Date对象 => 基础类型 (先使用toString方法如果没有,然后valueOf) |
基础类型string,number或者bool | 非date类型 | 基础类型 => number,非Date对象 => 基础类型(先使用valueOf如果没有使用toString) |
基础类型string,number或者bool | 基础类型string,number或者bool | 基础类型 => number |
举例说明,当你要比较一个表示时间的字符串和一个Date对象
var date = newDate("1999/12/31");
date == "1999/12/31";// false
date.toString();// "Fri Dec 31 1999 00:00:00 GMT-0800 (PST)"
按照规则Date会先执行toString方法,因此要比较一个Date对象和一个非Date对象时,你需要去实现将对象格式化后再来比较
function toYMD(date) {
vary = date.getYear() + 1900,// year is 1900-indexed
m = date.getMonth() + 1, // month is 0-indexed
d = date.getDate();
return y
+ "/"+ (m < 10? "0"+ m : m)
+ "/"+ (d < 10? "0"+ d : d);
}
toYMD(date) === "1999/12/31";// true