初涉JavaScript模式 (2) : 基本技巧
尽量少用全局变量
大量使用全局变量会导致的后果
全局变量创建以后会在整个JavaScript应用和Web页面中共享。所有的全局变量都存在于一个全局命名空间内,很容易发生冲突
不知不觉创建了全局变量
其原因在于JavaScript的两个特性,第一个是JavaScript可直接使用变量而无需声明,第二个是JavaScript的暗示全局变量的概念,即任何变量如果未经声明,就为全局对象所有
为了避免我们无意间创建了全局变量 我们可以使用var声明变量
另一种创建暗示全局变量的反模式是带有var声明的链式赋值。
产生上面结果的原因是 JavaScript的从右到左的操作符优先级 它的执行顺序是这样的
```javascript var a = (b = 0); ```
解决方案也很简单 就是对链式赋值的所有变量都进行赋值,如下代码所示:
```javascript function demo(){ var a,b; a = b = 0; } ```
另一个避免全局变量大量使用的原因在于代码移植,你的代码在当前环境(主机)运行是没有问题的,但换个环境,全局变量可能会和存在于其他环境的变量发生冲突。
全局变量的释放
暗示全局变量和明确定义的全局变量有细微的不同,不同之处在于是否能用delete销毁,代码如下:
以上代码说明暗示全局变量可以通过delete删除,而var定义则不能
访问全局变量
在浏览器下,可通过window属性在代码的任意位置访问到全局变量,但是也可能发生意外(声明了一个名为window的局部变量)。
在其他环境下(如NodeJS),window就不叫window了,NodeJS下整个Runtime时期的全局变量是global,顾用window还不够严谨。
解决方案代码如下:
```javascript var global = (function(){ return this; }()); ```
按这种方式通常都能获取全局对象,因为this在函数内部作为一个函数调用(不是通过构造器创建也不是通过call,apply指定),往往指向全局对象
但是如果你的代码运行在ES5的严格模式下,就不能这样用了,解决代码如下:
变量及函数声明的提升
JavaScript允许在函数的任意地方声明多个变量或函数,无论在哪里声明都等同于在函数顶部声明,这就是所谓的提升
以上代码说明 var v1 被看做是局部变量的声明,所以打印出来的是undefined 这其实还涉及到一个变量对象(Variable Object)的问题,打印的时候他会先在Variable Object中找,如果找不到才会到window中去找,如果发现有了,则直接打印出来
for循环的优化
看到这里,你可能会觉得奇怪,for还有可以优化的地方吗?谈不上优化,就是一些注意点
闲话不多说直接上代码:
```javascript //经典的for循环 for(var i = 0; i < array.length; i++){ //对array[i]进行操作 } ```
这种模式的问题在于每次循环都要访问数组的length,如果是数组还好,影响不是很大(速度变慢)。但是如果遍历的是DOM集合,我们知道在document下操作DOM是很耗性能的,而如果我们将length用变量保存起来,从而避免了每次循环对DOM的查询,在所有的浏览器中都会大大降低性能的损耗,其中在safari中速度会提高3倍,IE7中会提高170倍之多
还有一个可以改进的是可以通过:
```javascript var array = [],len = array.length ; for(len,len--){ //处理array[len] } ```
这种模式 有2个好处,使用了最少的变量,逐步减至0,因为同0比较 比 同数组的长度比较速度更快
还有一种while的模式,和这个类似,我就不贴代码了(嘿嘿)
不要增加内置对象的原型
以前喜欢写这种代码,被我英明的 “会哥” 提醒了一下,专门去看了下,反模式的典型啊。。。自我检讨下
增加内置构造函数(Object,Array,Function)的原型是很爽的(埋下的坑迟早要填。。),但是这可能会严重影响可维护性,因为这将使你的代码变得更加不可预测,比如你重写了Array的slice方法,而别的工程师不知道,他得不到预期的结果(比全局对象污染更严重),如果一定要写也必须先准确的记录下来,并和团队交流清楚
应该避免的一些小细节
避免使用隐式类型转换
JavaScript 在执行比较语句的时候会进行隐式类型转换,这也是为什么执行 false==0 或 "" == 0 这类比较语句会返回true的原因
为了避免隐式转换造成的混淆不清,最好在使用比较语句的时候使用 === 和!===
避免使用eval()
eval 会将任意字符串当作JavaScript代码来执行(ES5 严格模式下使用eval会报错),eval会执行被修改过的代码(可能被客户端修改),会产生一些安全隐患
eval的替代方案 使用浏览器自带的方法来解析JSON请求 JSON.parse() ,对于不支持parse的可以使用来自JSON.org的类库,还有就是通过setTimeout,setInterval,和function的构造函数来传递参数,在大部分情况下也会导致类似eval的隐患,如下代码所示:
```javascript //反模式 setTimeout("func()" , 1000); setTimeout("func(1,2,3)" , 1000); //推荐的模式 setTimeout(func , 1000); setTimeout(function(){ func(1,2,3); } , 1000); 如果传给setTimeout是字符串,JavaScript仍然会去以程序代码的方式去执行传递过来的字符串,如果一定要用eval,可以考虑用new Function()的方式来替代eval,代码如下: var code = "console.log(5)" new Function(code)(); //5 ```
使用parseInt的第二个参数
通常第二个参数我们都是忽略,但是有时候会发生一些bug,比如如果我传的字符串是以0开头的就会出现错误,他会不确定是8进制还是10进制,所以最好还是加上进制。另外将一个字符串转换成数值有更快的方法,代码如下:
```javascript + “08” //8 Number(“08”) //8 ```
这些方法比parseInt快很多,parseInt是解析而不是简单的转换,例如输入“08 soso” 除了parseInt 其他的都会返回NaN
运行JSLint
JSLint 可以检查你的代码,提前发现你的代码有哪些不足,写完代码后在JSLint上跑一遍,这是很好的编程模式(BUG伤不起啊)
后记:
第一次写这么长的博客,文中难免有错误之处,如果能指出感激不进
这篇读书笔记,部分加入了自己工作中遇到的需求加以理解,如有不对之处,请联系我,共同进步