[译]JavaScript中的变量声明:你可以打破的三条规则

原文:http://www.2ality.com/2012/11/var-statement-rules.html


本文提到了在使用var语句时经常被提到的三条规则,然后告诉你什么时候我们可以打破这些规则.在阅读本文之前,你必须已经了解了JavaScript中函数作用域内的var声明是如何工作的 [1] .

1.你可以打破的三条规则

1.1. 要打破的规则:不要把var语句放在代码块中

以常规看法来说,下面的代码是不好的:

// 非常规写法
function foo(x, y) {
    if (x > y) {
        var tmp = x;
        x = y;
        y = tmp;
    }
    ...
}

为什么说是不好的: 看到这样的代码,也许有人会认为变量tmp只存在于if语句块中.而实际情况是,变量tmp的声明会被提升,也就是说变量tmp的声明操作实际上是在函数的最开始处,而赋值操作还留在原来语句存在的地方.因此,下面的写法更能反应出JavaScript引擎内部实际上看到的代码(预解析之后):

// 常规写法
function foo(x, y) {
    var tmp;
    if (x > y) {
        tmp = x;
        x = y;
        y = tmp;
    }
    ...
}

反对观点: 从JavaScript语义的角度看,变量tmp不仅仅存在于if语句块中.可是,从概念上讲,tmp被限制在了那个语句块中:别的地方没有用到它,如果有人删除了这个if语句块,tmp的声明操作也应该被删除.这样的话,非常规写法能更好的表现出作者的意图.

对于函数体很长的函数来说,常规写法是对的.可是,一个函数的行数不应该长于5-10行,这种长度下的话,增加概念上的清晰度更值得考虑.

1.2.要打破的规则: 不要把var语句放在循环体中

常规看法告诉我们: 不要像下面这样写.

// 非常规写法
for (var i = 0; i < 10000; i++) {
    var foo = 1;
}

说是下面这样的写法会更快点:

// 常规写法
var foo; for (var i = 0; i < 10000; i++) { foo = 1; }

可是,常规看法是不对的,在一些引擎中,常规写法甚至更慢,这个jsPerf测试就能证明:这两种写法并没有明显的性能差异.

1.3.要打破的规则: 每个函数都使用单一的var语句

常规看法告诉我们:你应该把所有的变量声明都放在函数体开始的地方,而且仅能使用一条var语句.例如:

// 常规写法
var foo = 1,
    bar = 2,
    baz = 3;

这么规定有两个原因.首先,这样写可以防止变量提升(hoisting)带来的诡异问题.其次,还可以避免重复的var关键字.

我们前面已经提到一个比防止变量提升更值得考虑的事情.

避免重复的var会产生一个负面效应:就是如果你漏掉一个逗号,你会意外的创建一些全局变量.例如:

var foo = 1  // 没有逗号
    bar = 2,
    baz = 3;

因为在1和bar之间是由换行符分割的,JavaScript引擎会自动在换行符前面插入一个分号 [2]. 因此,后面的两行就成为了一个只包含了bar和baz赋值操作的逗号表达式语句,在非严格模式中,这意味着会创建两个全局变量.如果同样的代码运行在严格模式中 [3]:

(function () {
    "use strict";
    var foo = 1
        bar = 2,
        baz = 3;
}());

会抛出异常:我们为一个不存在的变量执行了赋值操作:

ReferenceError: bar is not defined

与其使用单独的var语句声明多个变量,我更推荐下面的方式:

var foo = 1;
var bar = 2;
var baz = 3;

这种方式有几个优点:

  • 忘记标点符号不会引发问题. 如果忘记写分号,自动分号插入(ASI)会帮助你,而不是为难你.
  • 更方便修改代码和删除部分变量的赋值操作.
  • 不需要缩进.

2. ECMAScript 6

ECMAScript 6中将会引入块级作用域下的变量声明(通过let语句).另外,还会增加解构赋值(destructuring assignment)语法:在赋值运算符左侧可以访问到右侧数据的内部结构.使用解构赋值可以省去在1.1小节中的函数foo中的临时变量的使用,会让代码更简洁:

function foo(x, y) {
    if (x > y) {
        [y, x] = [x, y];
    }
    ...
}

3. 结论

本文我们讨论了三个关于var语句的,可以打破而且也经常需要打破的规则.和往常一样,不要盲目的听从任何人的建议(包括那些常规的看法以及本文给出的看法),要确保自己真的知道自己在干什么.

4. 参考

  1. JavaScript variable scoping and its pitfalls
  2. Automatic semicolon insertion in JavaScript
  3. JavaScript’s strict mode: a summary
posted @ 2012-11-15 14:54  紫云飞  阅读(2714)  评论(2编辑  收藏  举报