title: Hello World
编写高质量JavaScript代码的基本要点
软件bug的修复是昂贵的,并且随着时间的推移,这些bug的成本也会增加,尤其当这些bug潜伏并慢慢出现在已经发布的软件中时。当你发现bug 的时候就立即修复它是最好的,此时你代码要解决的问题在你脑中还是很清晰的。否则,你转移到其他任务,忘了那个特定的代码,一段时间后再去查看这些代码就 需要:
花时间学习和理解这个问题
化时间是了解应该解决的问题代码
还有问题,特别对于大的项目或是公司,修复bug的这位伙计不是写代码的那个人(且发现bug和修复bug的不是同一个人)。因此,必须降低理解代 码花费的时间,无论是一段时间前你自己写的代码还是团队中的其他成员写的代码。这关系到底线(营业收入)和开发人员的幸福,因为我们更应该去开发新的激动 人心的事物而不是花几小时几天的时间去维护遗留代码。
另一个相关软件开发生命的事实是,读代码花费的时间要比写来得多。有时候,当你专注并深入思考某个问题的时候,你可以坐下来,一个下午写大量的代码。
你的代码很能很快就工作了,但是,随着应用的成熟,还会有很多其他的事情发生,这就要求你的进行进行审查,修改,和调整。例如:
bug是暴露的
新功能被添加到应用程序
程序在新的环境下工作(例如,市场上出现新想浏览器)
代码改变用途
代码得完全从头重新,或移植到另一个架构上或者甚至使用另一种语言
由于这些变化,很少人力数小时写的代码最终演变成花数周来阅读这些代码。这就是为什么创建可维护的代码对应用程序的成功至关重要。
可维护的代码意味着:
可读的
一致的
可预测的
看上去就像是同一个人写的
已记录
最小全局变量(Minimizing Globals)
JavaScript通过函数管理作用域。在函数内部声明的变量只在这个函数内部,函数外面不可用。另一方面,全局变量就是在任何函数外面声明的或是未声明直接简单使用的。
每个JavaScript环境有一个全局对象,当你在任意的函数外面使用this的时候可以访问到。你创建的每一个全部变量都成了这个全局对象的属 性。在浏览器中,方便起见,该全局对象有个附加属性叫做window,此window(通常)指向该全局对象本身。下面的代码片段显示了如何在浏览器环境 中创建和访问的全局变量:
myglobal = “hello”; // 不推荐写法
console.log(myglobal); // “hello”
console.log(window.myglobal); // “hello”
console.log(window[“myglobal”]); // “hello”
console.log(this.myglobal); // “hello”
全局变量的问题
全局变量的问题在于,你的JavaScript应用程序和web页面上的所有代码都共享了这些全局变量,他们住在同一个全局命名空间,所以当程序的两个不同部分定义同名但不同作用的全局变量的时候,命名冲突在所难免。
web页面包含不是该页面开发者所写的代码也是比较常见的,例如:
第三方的JavaScript库
广告方的脚本代码
第三方用户跟踪和分析脚本代码
不同类型的小组件,标志和按钮
比方说,该第三方脚本定义了一个全局变量,叫做result;接着,在你的函数中也定义一个名为result的全局变量。其结果就是后面的变量覆盖前面的,第三方脚本就一下子嗝屁啦!
因此,要想和其他脚本成为好邻居的话,尽可能少的使用全局变量是很重要的。在书中后面提到的一些减少全局变量的策略,例如命名空间模式或是函数立即自动执行,但是要想让全局变量少最重要的还是始终使用var来声明变量。
由于JavaScript的两个特征,不自觉地创建出全局变量是出乎意料的容易。首先,你可以甚至不需要声明就可以使用变量;第二,JavaScript有隐含的全局概念,意味着你不声明的任何变量都会成为一个全局对象属性。参考下面的代码:
function sum(x, y) {
// 不推荐写法: 隐式全局变量
result = x + y;
return result;
}
此段代码中的result没有声明。代码照样运作正常,但在调用函数后你最后的结果就多一个全局命名空间,这可以是一个问题的根源。
经验法则是始终使用var声明变量,正如改进版的sum()函数所演示的:
function sum(x, y) {
var result = x + y;
return result;
}
另一个创建隐式全局变量的反例就是使用任务链进行部分var声明。下面的片段中,a是本地变量但是b确实全局变量,这可能不是你希望发生的:
// 反例,勿使用
function foo() {
var a = b = 0;
// …
}
此现象发生的原因在于这个从右到左的赋值,首先,是赋值表达式b = 0,此情况下b是未声明的。这个表达式的返回值是0,然后这个0就分配给了通过var定义的这个局部变量a。换句话说,就好比你输入了:
var a = (b = 0);
如果你已经准备好声明变量,使用链分配是比较好的做法,不会产生任何意料之外的全局变量,如:
function foo() {
var a, b;
// … a = b = 0; // 两个均局部变量
}
忘记var的副作用(Side Effects When Forgetting var)
隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力。
通过var创建的全局变量(任何函数之外的程序中创建)是不能被删除的。
无var创建的隐式全局变量(无视是否在函数中创建)是能被删除的。
这表明,在技术上,隐式全局变量并不是真正的全局变量,但它们是全局对象的属性。属性是可以通过delete操作符删除的,而变量是不能的:
// 定义三个全局变量
var global_var = 1;
global_novar = 2; // 反面教材
(function () {
global_fromfunc = 3; // 反面教材
}());
// 试图删除
delete global_var; // false
delete global_novar; // true
delete global_fromfunc; // true
// 测试该删除
typeof global_var; // “number”
typeof global_novar; // “undefined”
typeof global_fromfunc; // “undefined”
在ES5严格模式下,未声明的变量(如在前面的代码片段中的两个反面教材)工作时会抛出一个错误。
单var形式(Single var Pattern)
在函数顶部使用单var语句是比较有用的一种形式,其好处在于:
提供了一个单一的地方去寻找功能所需要的所有局部变量
防止变量在定义之前使用的逻辑错误
帮助你记住声明的全局变量,因此较少了全局变量//zxx:此处我自己是有点晕乎的…
少代码(类型啊传值啊单线完成)
单var形式长得就像下面这个样子:
function func() {
var a = 1,
b = 2,
sum = a + b,
myobject = {},
i,
j;
// function body…
}
您可以使用一个var语句声明多个变量,并以逗号分隔。像这种初始化变量同时初始化值的做法是很好的。这样子可以防止逻辑错误(所有未初始化但声明的变量的初始值是undefined)和增加代码的可读性。在你看到代码后,你可以根据初始化的值知道这些变量大致的用途,例如是要当作对象呢还是当作整数来使。
你也可以在声明的时候做一些实际的工作,例如前面代码中的sum = a + b这个情况,另外一个例子就是当你使用DOM(文档对象模型)引用时,你可以使用单一的var把DOM引用一起指定为局部变量,就如下面代码所示的:
function updateElement() {
var el = document.getElementById(“result”),
style = el.style;
// 使用el和style干点其他什么事…
}
预解析:var散布的问题(Hoisting: A Problem with Scattered vars)
JavaScript中,你可以在函数的任何位置声明多个var语句,并且它们就好像是在函数顶部声明一样发挥作用,这种行为称为 hoisting(悬置/置顶解析/预解析)。当你使用了一个变量,然后不久在函数中又重新声明的话,就可能产生逻辑错误。对于JavaScript,只 要你的变量是在同一个作用域中(同一函数),它都被当做是声明的,即使是它在var声明前使用的时候。看下面这个例子:
// 反例
myname = “global”; // 全局变量
function func() {
alert(myname); // “undefined”
var myname = “local”;
alert(myname); // “local”
}
func();
在这个例子中,你可能会以为第一个alert弹出的是”global”,第二个弹出”loacl”。这种期许是可以理解的,因为在第一个alert 的时候,myname未声明,此时函数肯定很自然而然地看全局变量myname,但是,实际上并不是这么工作的。第一个alert会弹 出”undefined”是因为myname被当做了函数的局部变量(尽管是之后声明的),所有的变量声明当被悬置到函数的顶部了。因此,为了避免这种混 乱,最好是预先声明你想使用的全部变量。
上面的代码片段执行的行为可能就像下面这样:
myname = “global”; // global variable
function func() {
var myname; // 等同于 -> var myname = undefined;
alert(myname); // “undefined”
myname = “local”;
alert(myname); // “local”}
func();