改善JavaScript程序的188个建议34.docx
- 文档编号:7154064
- 上传时间:2023-01-21
- 格式:DOCX
- 页数:9
- 大小:101.54KB
改善JavaScript程序的188个建议34.docx
《改善JavaScript程序的188个建议34.docx》由会员分享,可在线阅读,更多相关《改善JavaScript程序的188个建议34.docx(9页珍藏版)》请在冰豆网上搜索。
改善JavaScript程序的188个建议34
建议3:
减少全局变量污染
定义全局变量有3种方式:
在任何函数外面直接执行var语句。
1.var f = 'value';
直接添加一个属性到全局对象上。
全局对象是所有全局变量的容器。
在Web浏览器中,全局对象名为window。
1.window.f = 'value';
直接使用未经声明的变量,以这种方式定义的全局变量被称为隐式的全局变量。
1.f = 'value';
为方便初学者在使用前无须声明变量而有意设计了隐式的全局变量,然而不幸的是忘记声明变量成了一个非常普遍的现象。
JavaScript的策略是让那些被忘记预先声明的变量成为全局变量,这导致在程序中查找Bug变得非常困难。
JavaScript语言最为糟糕的就是它对全局变量的依赖性。
全局变量就是在所有作用域中都可见的变量。
全局变量在很小的程序中可能会带来方便,但随着程序越来越大,它很快变得难以处理。
因为一个全局变量可以被程序的任何部分在任意时间改变,使得程序的行为被极大地复杂化。
在程序中使用全局变量降低了程序的可靠性。
全局变量使在同一个程序中运行独立的子程序变得更难。
如果某些全局变量的名称与子程序中的变量名称相同,那么它们将会相互冲突并可能导致程序无法运行,而且通常还使程序难以调试。
实际上,这些全局变量削弱了程序的灵活性,应该避免使用全局变量。
努力减少使用全局变量的方法:
在应用程序中创建唯一一个全局变量,并定义该变量为当前应用的容器。
1.var My = {};
2.My.name = {
3. "first-name" :
" first ",
4. "last-name" :
" last "
5.};
6.My.work = {
7. number :
123,
8. one :
{
9. name :
" one ",
10. time :
"2012-9-14 12:
55",
11. city :
"beijing"
12. },
13. two :
{
14. name :
"two",
15. time :
"2012-9-12 12:
42",
16. city :
"shanghai"
17. }
18.};
只要把多个全局变量都追加在一个名称空间下,将显著降低与其他应用程序产生冲突的概率,应用程序也会变得更容易阅读,因为My.work指向的是顶层结构。
当然也可以使用闭包体将信息隐藏,它是另一种有效减少“全局污染”的方法。
在编程语言中,作用域控制着变量与参数的可见性及生命周期。
这为程序开发提供了一个重要的帮助,因为它减少了名称冲突,并且提供了自动内存管理。
1.var foo = function() {
2. var a = 1, b = 2;
3. var bar = function() {
4. var b = 3, c = 4; // a=1, b =3, c=4
5. a += b + c; // a=8, b =3, c=4
6. }; // a=1, b =2, c=undefined
7. bar(); // a=21, b =2, c=undefined
8.};
大多数采用C语言语法的语言都拥有块级作用域。
对于一个代码块,即包括在一对大括号中的语句,其中定义的所有变量在代码块的外部是不可见的。
定义在代码块中的变量在代码块执行结束后会被释放掉。
但是,对于JavaScript语言来说,虽然该语言支持代码块的语法形式,但是它并不支持块级作用域。
JavaScript支持函数作用域,定义在函数中的参数和变量在函数外部是不可见的,并且在一个函数中的任何位置定义的变量在该函数中的任何地方都可见。
其他主流编程语言都推荐尽可能迟地声明变量,但是在JavaScript中就不能够这样,因为它缺少块级作用域,最好的做法是在函数体的顶部声明函数中可能用到的所有变量。
建议4:
注意JavaScript数据类型的特殊性
(1)
1.防止浮点数溢出
二进制的浮点数不能正确地处理十进制的小数,因此0.1+0.2不等于0.3。
1.num = 0.1+0.2; //0.30000000000000004
这是JavaScript中最经常报告的Bug,并且这是遵循二进制浮点数算术标准(IEEE754)而导致的结果。
这个标准适合很多应用,但它违背了数字基本常识。
幸运的是,浮点数中的整数运算是精确的,所以小数表现出来的问题可以通过指定精度来避免。
例如,针对上面的相加可以这样进行处理:
1.a = (1+2)/10; //0.3
这种处理经常在货币计算中用到,在计算货币时当然期望得到精确的结果。
例如,元可以通过乘以100而全部转成分,然后就可以准确地将每项相加,求和后的结果可以除以100转换回元。
2.慎用JavaScript类型自动转换
在JavaScript中能够自动转换变量的数据类型,这种转换是一种隐性行为。
在自动转换数据类型时,JavaScript一般遵循:
如果某个类型的值被用于需要其他类型的值的环境中,JavaScript就自动将这个值转换成所需要的类型,具体说明见表1.1。
表1.1 数据类型自动转换
(续)
如果把非空对象用在逻辑运算环境中,则对象被转换为true。
此时的对象包括所有类型的对象,即使是值为false的包装对象也被转换为true。
如果把对象用在数值运算环境中,则对象会被自动转换为数字,如果转换失败,则返回值为NaN。
当数组被用在数值运算环境中时,数组将根据包含的元素来决定转换的值。
如果数组为空数组,则被转换为数值0。
如果数组仅包含一个数字元素,则被转换为该数字的数值。
如果数组包含多个元素,或者仅包含一个非数字元素,则返回NaN。
当对象用于字符串环境中时,JavaScript能够调用toString()方法把对象转换为字符串再进行相关计算。
当对象与数值进行加号运算时,则会尝试将对象转换为数值,然后参与求和运算。
如果不能够将对象转换为有效数值,则执行字符串连接操作。
建议4:
注意JavaScript数据类型的特殊性
(2)
3.正确检测数据类型
使用typeof运算符返回一个用于识别其运算数类型的字符串。
对于任何变量来说,使用typeof运算符总是以字符串的形式返回以下6种类型之一:
"number"
"string"
"boolean"
"object"
"function"
"undefined"
不幸的是,在使用typeof检测null值时,返回的是“object”,而不是“null”。
更好的检测null的方式其实很简单。
下面定义一个检测值类型的一般方法:
1.function type(o){
2. return (o === null) ?
"null" :
(typeof o);
3.}
这样就可以避开因为null值影响基本数据的类型检测。
注意:
typeof不能够检测复杂的数据类型,以及各种特殊用途的对象,如正则表达式对象、日期对象、数学对象等。
对于对象或数组,可以使用constructor属性,该属性值引用的是原来构造该对象的函数。
如果结合typeof运算符和constructor属性,基本能够完成数据类型的检测。
表1.2所示列举了不同类型数据的检测结果。
表1.2 数据类型检测
使用constructor属性可以判断绝大部分数据的类型。
但是,对于undefined和null特殊值,就不能使用constructor属性,因为使用JavaScript解释器会抛出异常。
此时可以先把值转换为布尔值,如果为true,则说明不是undefined和null值,然后再调用constructor属性,例如:
1.var value = undefined;
2.alert(typeof value); //"undefined"
3.alert(value && value.constructor); //undefined
4.var value = null;
5.alert(typeof value); //"object"
6.alert(value && value.constructor); //null
对于数值直接量,也不能使用constructor属性,需要加上一个小括号,这是因为小括号运算符能够把数值转换为对象,例如:
1.alert((10).constructor);
使用toString()方法检测对象类型是最安全、最准确的。
调用toString()方法把对象转换为字符串,然后通过检测字符串中是否包含数组所特有的标志字符可以确定对象的类型。
toString()方法返回的字符串形式如下:
1.[object class]
建议4:
注意JavaScript数据类型的特殊性(3)
其中,object表示对象的通用类型,class表示对象的内部类型,内部类型的名称与该对象的构造函数名对应。
例如,Array对象的class为“Array”,Function对象的class为“Function”,Date对象的class为“Date”,内部Math对象的class为“Math”,所有Error对象(包括各种Error子类的实例)的class为“Error”。
客户端JavaScript的对象和由JavaScript实现定义的其他所有对象都具有预定义的特定class值,如“Window”、“Document”和“Form”等。
用户自定义对象的class值为“Object”。
class值提供的信息与对象的constructor属性值相似,但是class值是以字符串的形式提供这些信息的,而不是以构造函数的形式提供这些信息的,所以在特定的环境中是非常有用的。
如果使用typeof运算符来检测,则所有对象的class值都为“Object”或“Function”,所以此时的class值不能够提供有效信息。
但是,要获取对象的class值的唯一方法是必须调用Object对象定义的默认toString()方法,因为不同对象都会预定义自己的toString()方法,所以不能直接调用对象的toString()方法。
例如,下面对象的toString()方法返回的就是当前UTC时间字符串,而不是字符串“[objectDate]”。
1.var d = new Date();
2.alert(d.toString()); //当前UTC时间字符串
要调用Object对象定义的默认toString()方法,可以先调用Object.prototype.toString对象的默认toString()函数,再调用该函数的apply()方法在想要检测的对象上执行。
结合上面的对象d,具体实现代码如下:
1.var d = new Date();
2.var m = Object.prototype.toString;
3.alert(m.apply(d)); //" [object Date] "
下面是一个比较完整的数据类型安全检测方法。
1.// 安全检测JavaScript基本数据类型和内置对象
2.// 参数:
o表示检测的值
3./* 返回值:
返回字符串"undefined"、"number"、"boolean"、
"string"、"function"、"regexp"、"array"、"date"、"error"、"object"或"null" */
4.function typeOf(o){
5. var _toString = Object.prototype.toString;
6. // 获取对象的toString()方法引用
7. // 列举基本数据类型和内置对象类型,可以进一步补充该数组的检测数据类型范围
8. var _type ={
9. "undefined" :
"undefined",
10. "number" :
"number",
11. "boolean" :
"boolean",
12. "string" :
"string",
13. "[object Function]" :
"function",
14. "[object RegExp]" :
"regexp",
15. "[object Array]" :
"array",
16. "[object Date]" :
"date",
17. "[object Error]" :
"error"
18. }
19. return _type[typeof o] || _type[_toString.call(o)] || (o ?
"object" :
"null");
20.}
应用示例:
1.var a = Math.abs;
2.alert(typeOf(a)); //"function"
上述方法适用于JavaScript基本数据类型和内置对象,而对于自定义对象是无效的。
这是因为自定义对象被转换为字符串后,返回的值是没有规律的,并且不同浏览器的返回值也是不同的。
因此,要检测非内置对象,只能够使用constructor属性和instaceof运算符来实现。
建议4:
注意JavaScript数据类型的特殊性(4)
4.避免误用parseInt
parseInt是一个将字符串转换为整数的函数,与parseFloat(将字符串转换为浮点数)对应,这两种函数是JavaScript提供的两种静态函数,用于把非数字的原始值转换为数字。
在开始转换时,parseInt会先查看位置0处的字符,如果该位置不是有效数字,则将返回NaN,不再深入分析。
如果位置0处的字符是数字,则将查看位置1处的字符,并重复前面的测试,依此类推,直到发现非数字字符为止,此时parseInt()函数将把前面分析合法的数字字符转换为数值并返回。
1.parseInt("123abc"); // 123
2.parseInt("1.73"); // 1
3.parseInt(".123"); // NaN
浮点数中的点号对于parseInt来说属于非法字符,因此它不会被转换并返回,这样,在使用parseInt时,就存在潜在的误用风险。
例如,我们并不希望parseInt("16")与parseInt("16tons")产生相同的结果。
如果该函数能够提醒我们出现额外文本就好了,但它不会那么做。
对于以0为开头的数字字符串,parseInt()函数会把它作为八进制数字处理,先把它转换为数值,然后再转换为十进制的数字返回。
对于以0x开头的数字字符串,parseInt()函数则会把它作为十六进制数字处理,先把它转换为数值,然后再转换为十进制的数字返回。
例如:
1.var d = "010"; //八进制
2.var e = "0x10"; //十六进制
3.parseInt(d); //8
4.parseInt(e); //16
如果字符串的第一个字符是0,那么该字符串将基于八进制而不是十进制来求值。
在八进制中,8和9不是数字,所以parseInt("08")和parseInt("09")的结果为0,这个错误导致了在程序解析日期和时间时经常会出现问题。
幸运的是,parseInt可以接受一个基数作为参数,这样parseInt("08",10)结果为8,parseInt("09",10)结果为9。
因此,建议读者在使用parseInt时,一定要提供这个基数参数。
通过在parseInt中提供基数参数,可以把二进制、八进制、十六进制等不同进制的数字字符串转换为整数。
例如,下面把十六进制数字字符串"123abc"转换为十进制整数。
1.parseInt("123abc",16); // 1194684
再如,把二进制、八进制和十进制数字字符串转换为整数:
1.parseInt("10",2); //把二进制数字10转换为十进制整数为2
2.parseInt("10",8); //把八进制数字10转换为十进制整数为8
3.parseInt("10" ,10); //把十进制数字10转换为十进制整数为10
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 改善 JavaScript 程序 188 建议 34