初涉JavaScript模式 (2) : 基本技巧

来源:互联网 时间:1970-01-01

尽量少用全局变量

大量使用全局变量会导致的后果

全局变量创建以后会在整个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伤不起啊)

后记:

第一次写这么长的博客,文中难免有错误之处,如果能指出感激不进

这篇读书笔记,部分加入了自己工作中遇到的需求加以理解,如有不对之处,请联系我,共同进步


相关阅读:
Top