1.全局变量污染与变量提升
2.数据类型
3.特殊值(NaN、undefined、null)
4. === 与 ==
5.没有真正的数组
6.避免使用with与eval
7.消除switch歧义
8.不要省略块标志 { }
Javascript的弱类型以及函数作用域等规则使用编写Javascript代码极为容易,但是编写可维护、高质量的代码却变得十分困难,这个系列的文章将总结在项目开发过程中,能够改善代码可读性、可维护性及优化运行性能的一系列技巧。
如有问题,请不吝指出,非常感谢;如果喜欢,右下角点个推荐吧~
1.全局变量污染与变量提升
- 定义全局变量的3种方式
1 2 3 |
|
- 全局对象是所有域中都可见的变量,如果程序比较小,那么定义全局变量可以避免函数调用时参数的传递,但是对于一个大的项目,全局变量定义不善,极为容易造成全局变量被某个程序改掉,而没有被发现,这也使调试时难以发现问题所在。
- 在项目较大时,建议尽可能地避免使用全局变量,那么兼顾灵活性与可读性的解决方法就是在程序中只创建一个全局变量。
1 2 3 4 5 6 7 8 9 10 |
|
- 与C、Python等语言拥有块级作用域不同,Javascript作用域是以函数为单位的,定义在函数中的变量函数外部不可见,但是在函数内部(包括其子函数)处处可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
2.数据类型
- 浮点数是不精确的,在项目中,特别是在线支付环节,如果希望计算的结果是准确的,一般先乘100转换为整数后,计算完后再除以100,这样能够得到预想中的结果。
1 |
|
- 检测数据类型,我们先看面试中常出现的判断题
1 2 3 4 5 6 7 |
|
- typeof null 返回的是‘object‘,项目开发中,我们并不希望null被判断为‘object‘,自定义一个函数,可以满足需求。
1 2 3 |
|
- 上面的方法不能识别数组、日期等对象,如果需要判断日期、数组、正则、错误等类型,那么可以考虑使用
toString()
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
- 自定义类型,可结合typeof、constructor属性和instanceof实现
3.特殊值(NaN、undefined、null)
1 2 3 4 5 |
|
- Javascript提供了isNaN函数检测NaN值
1 2 3 4 5 6 7 8 9 10 11 |
|
- 如何检查一个值是不是真的NaN
1 2 3 4 5 6 7 8 |
|
- 使用isFinite检查是否可以是个数或可被转换为一个数
1 2 3 4 |
|
- 判断参数是否真的是一个数字,而不是可转换为数字的字符串等
1 2 3 4 |
|
- undefined有如下几种情况
(1) 定义了变量但没有赋值 var cc; cc === undefined // => true
(2) 获取一个对象不存在的属性
(3) 没有返回值的函数,new + 构造函数除外(4) 定义函数时声明多个形参,调用时传入参数个数少于声明,多余的参数为undefined
- null 只能显示指定,一般用来清空对象
- 0、NaN、‘‘(空字符串)、false、null、undefined的布尔值为false(注意,[](空数组),{}(空对象)布尔值为 true )
1 2 3 |
|
4. === 与 ==
- 使用 == 比较前如果不是相同类型的值,将进行类型转换,转换规则请看下面的例子。
1 2 3 4 5 6 7 8 |
|
- === ,如果类型不同,则直接返回false,类型相同再进行比较。
- == 与 != 将进行隐式的类型转换,转换规则复杂,因此在实际的项目开发中,建议避免使用 == 与 !=,尽量使用 === 与 !==。
5.没有真正的数组
- 在Javascript中,数组也是对象,因此typeof的结果是object,如果需要判断一个对象是不是数组,那么可以使用第2部分的toString()方法,当然还可以利用构造函数判断。
1 |
|
- 需要注意的是,函数参数arguments并不是数组,只是一个具有length属性,以数字作为键的对象,因此arguments不能使用数组的方法,但是利用上述方法仍将arguments判断为数组。
- 有时我们需要对arguments对象执行数组的某些方法,这个时候可以利用下面的方法将arguments转换为真数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
6.避免使用with与eval
1 2 3 4 5 6 7 |
|
- 不推荐使用with的3个理由
(1) 使用with时,先检查对象是否有该属性,存在则使用,不存在则继续查找作用域,会降低执行性能
(2) with语句中的变量语意不明确,可读性差(3) 例如上例中的c不小心隐式声明为全局变量,本意是给ob.c赋值,易出现问题且难以调试
- 对于eval(),可以直接执行一段js代码,不推荐理由如下
(1) eval需要将字符串解释为代码后执行,降低执行性能
(2) eval降低程序的可读性,例如 eval("var a = ob." + key)等价于var a=ob[key],后者明显更优(3) eval存在安全问题,任意传入字符串都可能被解析执行,恶意代码也不例外
7.消除switch歧义
- switch中的case语句,定义了函数的起点,却没有定义函数的终点,不要忘记case语句后使用break
- 有时候,我们确实需要贯穿case语句,比如多种case情况,执行同一个方法,这时候,建议显式声明,消除歧义
1 2 3 4 5 6 7 8 9 10 11 12 |
|
8.不要省略块标志 { }
- if、while、do、for等语句接受{ ... } 代码块,但是有时我们在代码块中只有一行代码,为了使代码看起来简洁,很可能省略 { },但是这可能产生问题。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
- 加上括号,也增强了代码的可读性,下面这种写法是不是一目了然了呢
1 2 3 4 5 6 7 8 9 |
|